From b1017315df14f5345f2c743aee0e41dc066234ab Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Thu, 4 Jan 2024 11:24:30 +0800 Subject: [PATCH 01/90] ADM-675: [backend]feat:config cache for getJiraCards method in JiraFeignClient --- backend/src/main/java/heartbeat/client/JiraFeignClient.java | 1 + backend/src/main/java/heartbeat/config/CacheConfig.java | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/main/java/heartbeat/client/JiraFeignClient.java b/backend/src/main/java/heartbeat/client/JiraFeignClient.java index ef560a41c3..7699432059 100644 --- a/backend/src/main/java/heartbeat/client/JiraFeignClient.java +++ b/backend/src/main/java/heartbeat/client/JiraFeignClient.java @@ -28,6 +28,7 @@ JiraBoardConfigDTO getJiraBoardConfiguration(URI baseUrl, @PathVariable String b StatusSelfDTO getColumnStatusCategory(URI baseUrl, @PathVariable String statusNum, @RequestHeader String authorization); + @Cacheable(cacheNames = "jiraCards", key = "#boardId+'-'+#queryCount+'-'+#startAt+'-'+#jql") @GetMapping(path = "/rest/agile/1.0/board/{boardId}/issue?maxResults={queryCount}&startAt={startAt}&jql={jql}") String getJiraCards(URI baseUrl, @PathVariable String boardId, @PathVariable int queryCount, @PathVariable int startAt, @PathVariable String jql, @RequestHeader String authorization); diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 783c9e46a2..61d45ab0fb 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -35,6 +35,7 @@ public CacheManager ehCacheManager() { cacheManager.createCache("targetField", getCacheConfiguration(FieldResponseDTO.class)); cacheManager.createCache("boardVerification", getCacheConfiguration(JiraBoardVerifyDTO.class)); cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); + cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); return cacheManager; } From 97cec4fe2ade4ea6756c63595590ef16dd90819e Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Thu, 4 Jan 2024 11:33:53 +0800 Subject: [PATCH 02/90] ADM-675: [backend]feat:config cache for getHolidays method in HolidayFeignClient --- backend/src/main/java/heartbeat/client/HolidayFeignClient.java | 2 ++ backend/src/main/java/heartbeat/config/CacheConfig.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/backend/src/main/java/heartbeat/client/HolidayFeignClient.java b/backend/src/main/java/heartbeat/client/HolidayFeignClient.java index 51ba6c75a9..e8a73b3db1 100644 --- a/backend/src/main/java/heartbeat/client/HolidayFeignClient.java +++ b/backend/src/main/java/heartbeat/client/HolidayFeignClient.java @@ -2,6 +2,7 @@ import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import heartbeat.config.HolidayFeignClientConfiguration; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -10,6 +11,7 @@ configuration = HolidayFeignClientConfiguration.class) public interface HolidayFeignClient { + @Cacheable(cacheNames = "holidayResult", key = "#year") @GetMapping(path = "/{year}.json") HolidaysResponseDTO getHolidays(@PathVariable String year); diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 61d45ab0fb..295eaa5807 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -10,6 +10,8 @@ import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; + +import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; @@ -36,6 +38,7 @@ public CacheManager ehCacheManager() { cacheManager.createCache("boardVerification", getCacheConfiguration(JiraBoardVerifyDTO.class)); cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); + cacheManager.createCache("holidayResult", getCacheConfiguration(HolidaysResponseDTO.class)); return cacheManager; } From 241a6c3948a128a2c9cb138949e10004f57c5ed0 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 11:43:07 +0800 Subject: [PATCH 03/90] ADM-675: [backend]fix:fix the error that holidayDTO can not be serialized --- .../main/java/heartbeat/client/dto/board/jira/HolidayDTO.java | 4 +++- .../heartbeat/client/dto/board/jira/HolidaysResponseDTO.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java index 345fec0391..7610d6e01e 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class HolidayDTO { +public class HolidayDTO implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java index 07e5e11e0e..2a0896791f 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @@ -13,7 +14,7 @@ @NoArgsConstructor @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class HolidaysResponseDTO { +public class HolidaysResponseDTO implements Serializable { private List days; From 9f95124a3ad4284d96495e2123d3ebb8fffd3a7c Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 16:30:11 +0800 Subject: [PATCH 04/90] ADM-675: [backend]feat:config cache for all method in BuildKiteFeignClient --- .../main/java/heartbeat/client/BuildKiteFeignClient.java | 7 +++++++ .../client/dto/pipeline/buildkite/BuildKiteBuildInfo.java | 3 ++- .../client/dto/pipeline/buildkite/BuildKiteJob.java | 4 +++- .../dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java | 4 +++- .../dto/pipeline/buildkite/BuildKitePipelineDTO.java | 3 ++- .../client/dto/pipeline/buildkite/BuildKiteTokenInfo.java | 3 ++- .../client/dto/pipeline/buildkite/CreatedByDTO.java | 3 ++- .../heartbeat/client/dto/pipeline/buildkite/EnvDTO.java | 4 +++- .../client/dto/pipeline/buildkite/ProviderDTO.java | 4 +++- .../client/dto/pipeline/buildkite/ProviderSettingsDTO.java | 4 +++- .../heartbeat/client/dto/pipeline/buildkite/StepsDTO.java | 4 +++- backend/src/main/java/heartbeat/config/CacheConfig.java | 6 ++++++ 12 files changed, 39 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java index f2b26be7b9..74e30c2b61 100644 --- a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java +++ b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java @@ -8,6 +8,7 @@ import java.util.List; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -21,14 +22,17 @@ @FeignClient(name = "buildKiteFeignClient", url = "${buildKite.url}", configuration = BuildKiteFeignClientDecoder.class) public interface BuildKiteFeignClient { + @Cacheable(cacheNames = "tokenInfo", key = "#token") @GetMapping(path = "v2/access-token") @ResponseStatus(HttpStatus.OK) BuildKiteTokenInfo getTokenInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "buildKiteOrganizationInfo", key = "#token") @GetMapping(path = "v2/organizations") @ResponseStatus(HttpStatus.OK) List getBuildKiteOrganizationsInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pipelineInfo", key = "#organizationId+'-'+#page+'-'+#perPage+'-'+#startTime+'-'+#endTime") @GetMapping(path = "v2/organizations/{organizationId}/pipelines?page={page}&per_page={perPage}") @ResponseStatus(HttpStatus.OK) List getPipelineInfo(@RequestHeader("Authorization") String token, @@ -43,6 +47,9 @@ ResponseEntity> getPipelineSteps(@RequestHeader("Author @RequestParam("per_page") String perPage, @RequestParam("created_from") String createdFrom, @RequestParam("created_to") String createdTo, @RequestParam("branch[]") List branch); + @Cacheable(cacheNames = "pipelineStepsInfo", + key = "#organizationId+'-'+#pipelineId+'-'+#page+'-'+#perPage+'-'" + + "+#createdFrom+'-'+#createdTo+'-'+(#branch!=null ? branch.toString() : '')") @GetMapping(path = "v2/organizations/{organizationId}/pipelines/{pipelineId}/builds", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseStatus(HttpStatus.OK) diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java index 078fa80926..4afadd19f6 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java @@ -8,6 +8,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.time.Instant; import java.util.Comparator; import java.util.List; @@ -18,7 +19,7 @@ @AllArgsConstructor @NoArgsConstructor @Builder -public class BuildKiteBuildInfo { +public class BuildKiteBuildInfo implements Serializable { private List jobs; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java index d9fb5b84ce..204c60d905 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @JsonIgnoreProperties(ignoreUnknown = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class BuildKiteJob { +public class BuildKiteJob implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java index 80e5a66a9b..c643b710f7 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java @@ -6,12 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKiteOrganizationsInfo { +public class BuildKiteOrganizationsInfo implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java index 9acdab32f6..4c6e0ac6bf 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java @@ -7,6 +7,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.Date; import java.util.List; @@ -15,7 +16,7 @@ @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKitePipelineDTO { +public class BuildKitePipelineDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java index b217c3fa13..ae65912e54 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @AllArgsConstructor @@ -13,7 +14,7 @@ @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKiteTokenInfo { +public class BuildKiteTokenInfo implements Serializable { private List scopes; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java index 39cd559501..5c32e9e343 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.Date; @Data @@ -13,7 +14,7 @@ @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class CreatedByDTO { +public class CreatedByDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java index 489e21e75f..011eaa1112 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java @@ -5,10 +5,12 @@ import lombok.Builder; import lombok.Data; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class EnvDTO { +public class EnvDTO implements Serializable { } diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java index 38522b3cb1..cdaa576862 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class ProviderDTO { +public class ProviderDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java index b6aaa2eaf5..ff8c893866 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class ProviderSettingsDTO { +public class ProviderSettingsDTO implements Serializable { @JsonProperty("trigger_mode") private String triggerMode; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java index 00c9323201..df1c5d9c2a 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class StepsDTO { +public class StepsDTO implements Serializable { private String type; diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 295eaa5807..0b5c196b44 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -7,11 +7,13 @@ import heartbeat.client.dto.board.jira.JiraBoardVerifyDTO; import heartbeat.client.dto.board.jira.StatusSelfDTO; import java.time.Duration; +import java.util.List; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; import heartbeat.client.dto.board.jira.HolidaysResponseDTO; +import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; @@ -39,6 +41,10 @@ public CacheManager ehCacheManager() { cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); cacheManager.createCache("holidayResult", getCacheConfiguration(HolidaysResponseDTO.class)); + cacheManager.createCache("tokenInfo", getCacheConfiguration(BuildKiteTokenInfo.class)); + cacheManager.createCache("buildKiteOrganizationInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pipelineInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pipelineStepsInfo", getCacheConfiguration(List.class)); return cacheManager; } From f439587648e65057cdfc844d8b0a83ecc86ef212 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 16:33:50 +0800 Subject: [PATCH 05/90] ADM-675: [backend]feat:config cache for all method in GitHubFeignClient --- .../src/main/java/heartbeat/client/GitHubFeignClient.java | 7 +++++++ .../java/heartbeat/client/dto/codebase/github/Author.java | 4 +++- .../heartbeat/client/dto/codebase/github/AuthorOuter.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Base.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Comment.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Commit.java | 4 +++- .../heartbeat/client/dto/codebase/github/CommitInfo.java | 3 ++- .../java/heartbeat/client/dto/codebase/github/Commits.java | 4 +++- .../heartbeat/client/dto/codebase/github/Committer.java | 4 +++- .../client/dto/codebase/github/CommitterOuter.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/File.java | 4 +++- .../dto/codebase/github/GitHubOrganizationsInfo.java | 4 +++- .../heartbeat/client/dto/codebase/github/GitHubRepo.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Head.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Html.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Issue.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/License.java | 4 +++- .../client/dto/codebase/github/LinkCollection.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Owner.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Parent.java | 4 +++- .../client/dto/codebase/github/PullRequestInfo.java | 3 ++- .../java/heartbeat/client/dto/codebase/github/Repo.java | 5 ++--- .../client/dto/codebase/github/ReviewComment.java | 4 +++- .../client/dto/codebase/github/ReviewComments.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Self.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Stats.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Status.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Tree.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/User.java | 4 +++- .../heartbeat/client/dto/codebase/github/Verification.java | 4 +++- backend/src/main/java/heartbeat/config/CacheConfig.java | 7 +++++++ 31 files changed, 98 insertions(+), 31 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/GitHubFeignClient.java b/backend/src/main/java/heartbeat/client/GitHubFeignClient.java index c07d2163d6..b6f0b880bc 100644 --- a/backend/src/main/java/heartbeat/client/GitHubFeignClient.java +++ b/backend/src/main/java/heartbeat/client/GitHubFeignClient.java @@ -5,6 +5,7 @@ import heartbeat.client.dto.codebase.github.GitHubRepo; import heartbeat.client.dto.codebase.github.PullRequestInfo; import heartbeat.decoder.GitHubFeignClientDecoder; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; @@ -17,29 +18,35 @@ @FeignClient(name = "githubFeignClient", url = "${github.url}", configuration = GitHubFeignClientDecoder.class) public interface GitHubFeignClient { + @Cacheable(cacheNames = "githubOrganizationInfo", key = "#token") @GetMapping(path = "/user/orgs") @ResponseStatus(HttpStatus.OK) List getGithubOrganizationsInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "githubAllRepos", key = "#token") @GetMapping(path = "/user/repos") @ResponseStatus(HttpStatus.OK) List getAllRepos(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "githubRepos", key = "#organizationName") @GetMapping(path = "/orgs/{organizationName}/repos") @ResponseStatus(HttpStatus.OK) List getReposByOrganizationName(@PathVariable String organizationName, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "commitInfo", key = "#repository+'-'+#commitId") @GetMapping(path = "/repos/{repository}/commits/{commitId}") @ResponseStatus(HttpStatus.OK) CommitInfo getCommitInfo(@PathVariable String repository, @PathVariable String commitId, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pullRequestCommitInfo", key = "#repository+'-'+#mergedPullNumber") @GetMapping(path = "/repos/{repository}/pulls/{mergedPullNumber}/commits") @ResponseStatus(HttpStatus.OK) List getPullRequestCommitInfo(@PathVariable String repository, @PathVariable String mergedPullNumber, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pullRequestListInfo", key = "#repository+'-'+#deployId") @GetMapping(path = "/repos/{repository}/commits/{deployId}/pulls") @ResponseStatus(HttpStatus.OK) List getPullRequestListInfo(@PathVariable String repository, @PathVariable String deployId, diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java index 415198e2ac..da481a3f19 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Author { +public class Author implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java index 8be0cc0528..566a81519c 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class AuthorOuter { +public class AuthorOuter implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java index ac96c750e0..e2bc62608b 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Base { +public class Base implements Serializable { private String label; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java index ff97ba624b..84d4dfbfcb 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Comment { +public class Comment implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java index 6309b1b558..2af13c7134 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Commit { +public class Commit implements Serializable { private Author author; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java index 1f6a55e9a1..6fdf70289a 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java @@ -6,13 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class CommitInfo { +public class CommitInfo implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java index 1db391606a..6a6087d8fc 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Commits { +public class Commits implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java index 50fd43c57c..136c19dad6 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Committer { +public class Committer implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java index becaf04070..23eb7d5cef 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class CommitterOuter { +public class CommitterOuter implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java index 8f7b7c3330..5279069781 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class File { +public class File implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java index d7833728ea..a16ecdf8ce 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java @@ -6,12 +6,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; + @AllArgsConstructor @NoArgsConstructor @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class GitHubOrganizationsInfo { +public class GitHubOrganizationsInfo implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java index 97d4f42186..e806cf8b88 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java @@ -7,12 +7,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; + @AllArgsConstructor @NoArgsConstructor @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class GitHubRepo { +public class GitHubRepo implements Serializable { @JsonProperty("html_url") private String htmlUrl; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java index c8666a1b87..dab44a8da7 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Head { +public class Head implements Serializable { private String label; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java index 4323338dbd..c9f7b0d42c 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Html { +public class Html implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java index fe24e2e905..b935061bb3 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Issue { +public class Issue implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java index 74350cf99d..46ce69b932 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class License { +public class License implements Serializable { private String key; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java index e6d588c70c..5caf031822 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class LinkCollection { +public class LinkCollection implements Serializable { private Self self; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java index a33378eec5..24fd8a1a92 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Owner { +public class Owner implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java index 67523769c8..291a39b40f 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Parent { +public class Parent implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java index b5bf8e20de..f0672a2b36 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @@ -14,7 +15,7 @@ @NoArgsConstructor @AllArgsConstructor @Getter -public class PullRequestInfo { +public class PullRequestInfo implements Serializable { private String url; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java index 94991be6a9..ee55d42092 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java @@ -1,20 +1,19 @@ package heartbeat.client.dto.codebase.github; import com.fasterxml.jackson.annotation.JsonProperty; -import heartbeat.client.dto.codebase.github.License; -import heartbeat.client.dto.codebase.github.Owner; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Repo { +public class Repo implements Serializable { private Integer id; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java index 6828b6b3db..52d50eff3e 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class ReviewComment { +public class ReviewComment implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java index f71d3eef76..b040cf3f29 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class ReviewComments { +public class ReviewComments implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java index b6615ae9ad..20aa3b6afb 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Self { +public class Self implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java index 8a35608185..ce1dd29918 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Stats { +public class Stats implements Serializable { private Integer total; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java index 4f8e827c97..59d115d7f9 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Status { +public class Status implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java index db4d8b9936..2bb563eed6 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Tree { +public class Tree implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java index 181c9e4c3e..4d82094d20 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class User { +public class User implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java index abfcb34f66..5991419b87 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Verification { +public class Verification implements Serializable { private Boolean verified; diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 0b5c196b44..7c777f87eb 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -13,6 +13,7 @@ import javax.cache.spi.CachingProvider; import heartbeat.client.dto.board.jira.HolidaysResponseDTO; +import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; @@ -45,6 +46,12 @@ public CacheManager ehCacheManager() { cacheManager.createCache("buildKiteOrganizationInfo", getCacheConfiguration(List.class)); cacheManager.createCache("pipelineInfo", getCacheConfiguration(List.class)); cacheManager.createCache("pipelineStepsInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("githubOrganizationInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("githubAllRepos", getCacheConfiguration(List.class)); + cacheManager.createCache("githubRepos", getCacheConfiguration(List.class)); + cacheManager.createCache("commitInfo", getCacheConfiguration(CommitInfo.class)); + cacheManager.createCache("pullRequestCommitInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pullRequestListInfo", getCacheConfiguration(List.class)); return cacheManager; } From 5f832731d81ddd03dcdae80880164bdfeb09300a Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Tue, 9 Jan 2024 11:13:26 +0800 Subject: [PATCH 06/90] ADM-708:[backend]refactor: add board type transfer method --- .../heartbeat/controller/board/dto/request/BoardType.java | 8 ++++++++ .../java/heartbeat/service/board/jira/JiraService.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/heartbeat/controller/board/dto/request/BoardType.java b/backend/src/main/java/heartbeat/controller/board/dto/request/BoardType.java index 7ede536fba..e6672e69c9 100644 --- a/backend/src/main/java/heartbeat/controller/board/dto/request/BoardType.java +++ b/backend/src/main/java/heartbeat/controller/board/dto/request/BoardType.java @@ -18,4 +18,12 @@ public static BoardType fromValue(String type) { }; } + public static BoardType fromStyle(String style) { + return switch (style) { + case "next-gen" -> JIRA; + case "classic" -> CLASSIC_JIRA; + default -> throw new IllegalArgumentException("Board type does not find!"); + }; + } + } 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 0130b5776a..035e1fdb67 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -137,7 +137,7 @@ public BoardConfigDTO getInfo(BoardType boardType, BoardRequestParam boardReques String jiraBoardStyle = jiraFeignClient .getProject(baseUrl, boardRequestParam.getProjectKey(), boardRequestParam.getToken()) .getStyle(); - BoardType jiraBoardType = "classic".equals(jiraBoardStyle) ? BoardType.CLASSIC_JIRA : BoardType.JIRA; + BoardType jiraBoardType = BoardType.fromStyle(jiraBoardStyle); Map> partitions = getTargetFieldAsync(baseUrl, boardRequestParam).join() .stream() From 5177418bb98eca55aa688b5e64103e8ac15833d5 Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Tue, 9 Jan 2024 11:13:48 +0800 Subject: [PATCH 07/90] ADM-708:[backend]refactor: update log and exception content --- .../heartbeat/service/board/jira/JiraService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 035e1fdb67..565315d074 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -118,13 +118,13 @@ public String verify(BoardType boardType, BoardVerifyRequestParam boardVerifyReq } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); - log.error("Failed when call Jira to verify board, board id: {}, e: {}", - boardVerifyRequestParam.getBoardId(), cause.getMessage()); + log.error("Failed to call Jira to verify board, board id: {}, e: {}", boardVerifyRequestParam.getBoardId(), + cause.getMessage()); if (cause instanceof BaseException baseException) { throw baseException; } throw new InternalServerErrorException( - String.format("Failed when call Jira to verify board, cause is %s", cause.getMessage())); + String.format("Failed to call Jira to verify board, cause is %s", cause.getMessage())); } } @@ -158,13 +158,13 @@ public BoardConfigDTO getInfo(BoardType boardType, BoardRequestParam boardReques } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); - log.error("Failed when call Jira to get board config, project key: {}, board id: {}, e: {}", + log.error("Failed to call Jira to get board config, project key: {}, board id: {}, e: {}", boardRequestParam.getBoardId(), boardRequestParam.getProjectKey(), cause.getMessage()); if (cause instanceof BaseException baseException) { throw baseException; } throw new InternalServerErrorException( - String.format("Failed when call Jira to get board config, cause is %s", cause.getMessage())); + String.format("Failed to call Jira to get board config, cause is %s", cause.getMessage())); } } @@ -191,13 +191,13 @@ public BoardConfigDTO getJiraConfiguration(BoardType boardType, BoardRequestPara } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); - log.error("Failed when call Jira to get board config, project key: {}, board id: {}, e: {}", + log.error("Failed to call Jira to get board config, project key: {}, board id: {}, e: {}", boardRequestParam.getBoardId(), boardRequestParam.getProjectKey(), cause.getMessage()); if (cause instanceof BaseException baseException) { throw baseException; } throw new InternalServerErrorException( - String.format("Failed when call Jira to get board config, cause is %s", cause.getMessage())); + String.format("Failed to call Jira to get board config, cause is %s", cause.getMessage())); } } From 5f080cfb91d99fb19fdebb825b3b37ccb199e6e6 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 11:04:15 +0800 Subject: [PATCH 08/90] ADM-652 [frontend] feat: add show more button --- .../Common/ReportGrid/ReportTitle/style.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 11 +++++++++-- .../Metrics/ReportStep/BoradMetrics/style.tsx | 13 +++++++++++++ frontend/src/theme.ts | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx index 987d6ab8eb..8d397b2e7a 100644 --- a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx @@ -4,6 +4,7 @@ import { Typography } from '@mui/material' export const StyledMetricsTitleSection = styled('div')({ display: 'flex', + alignItems: 'center', }) export const StyledMetricsSign = styled('canvas')({ @@ -18,7 +19,6 @@ export const StyledMetricsTitle = styled(Typography)({ fontSize: '1rem', fontFamily: theme.main.font.secondary, textAlign: 'start', - marginBottom: '1rem', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index d6bd0bd4c4..d2a0386206 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -12,7 +12,11 @@ import { import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' import { selectMetricsContent } from '@src/context/Metrics/metricsSlice' import dayjs from 'dayjs' -import { StyledMetricsSection } from '@src/components/Metrics/ReportStep/BoradMetrics/style' +import { + StyledMetricsSection, + StyledShowMore, + StyledTitleWrapper, +} from '@src/components/Metrics/ReportStep/BoradMetrics/style' import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' import { ReportGrid } from '@src/components/Common/ReportGrid' @@ -120,7 +124,10 @@ const BoardMetrics = ({ return ( <> - + + + {boardReport && {'show more >'}} + diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index 84eb460497..f9a67947bc 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,5 +1,18 @@ import { styled } from '@mui/material/styles' +import { theme } from '@src/theme' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', }) + +export const StyledTitleWrapper = styled('div')({ + display: 'flex', + alignItems: 'center', + marginBottom: '1rem', +}) + +export const StyledShowMore = styled('div')({ + marginLeft: '0.5rem', + fontSize: '0.8rem', + color: theme.palette.link, +}) diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index dc18bbbd9c..25c60945b0 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -77,6 +77,7 @@ export const theme = createTheme({ main: '#3498db', light: '#b9c4cc', }, + link: '#4350AF', }, main: { backgroundColor: indigo[FIVE_HUNDRED], From ec0506308c6d306662a59dabbb29e425a7016acf Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 14:38:13 +0800 Subject: [PATCH 09/90] ADM-652 [frontend] feat: add detail router --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 12 ++++++++++-- frontend/src/config/routes.ts | 5 +++++ frontend/src/constants/router.ts | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index d2a0386206..13428326ec 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -21,6 +21,8 @@ import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/uti import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { useNavigate } from 'react-router-dom' +import { ROUTE } from '@src/constants/router' interface BoardMetricsProps { startToRequestBoardData: (request: ReportRequestDTO) => void @@ -38,13 +40,15 @@ const BoardMetrics = ({ endDate, }: BoardMetricsProps) => { const configData = useAppSelector(selectConfig) + const navigate = useNavigate() const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = useAppSelector(selectMetricsContent) + const jiraColumns = useAppSelector(selectJiraColumns) + const { metrics, calendarType } = configData.basic const { board } = configData const { token, type, site, projectKey, boardId, email } = board.config const jiraToken = getJiraBoardToken(token, email) - const jiraColumns = useAppSelector(selectJiraColumns) const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value ) @@ -121,12 +125,16 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) + const handleShowMore = () => { + navigate(ROUTE.METRICS_DETAIL_PAGE) + } + return ( <> - {boardReport && {'show more >'}} + {boardReport && {'show more >'}} diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index 87cb5760d5..c6c283160a 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -12,6 +12,11 @@ export const routes = [ component: lazy(() => import('../pages/Metrics')), name: 'Metrics', }, + { + path: 'detail', + component: lazy(() => import('../components/Metrics/ReportStep/ReportDetail')), + name: 'Detail', + }, { path: '/error-page', component: lazy(() => import('../pages/ErrorPage')), diff --git a/frontend/src/constants/router.ts b/frontend/src/constants/router.ts index f421479251..fc715eaecb 100644 --- a/frontend/src/constants/router.ts +++ b/frontend/src/constants/router.ts @@ -2,4 +2,5 @@ export enum ROUTE { BASE_PAGE = '/', ERROR_PAGE = '/error-page', METRICS_PAGE = '/metrics', + METRICS_DETAIL_PAGE = '/detail', } From 50b9f6e032755ebf7520c3ccb531c8ae2a6bc6f6 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 15:51:46 +0800 Subject: [PATCH 10/90] ADM-652 [frontend] feat: add detail page --- .../Metrics/ReportButtonGroup/index.tsx | 32 +- .../Metrics/ReportStep/ReportDetail/index.tsx | 449 ++++++------------ .../components/Metrics/ReportStep/index.tsx | 10 +- frontend/src/context/config/configSlice.ts | 6 +- frontend/src/context/report/reportSlice.ts | 30 ++ frontend/src/hooks/useGenerateReportEffect.ts | 7 +- frontend/src/store.ts | 2 + 7 files changed, 210 insertions(+), 326 deletions(-) create mode 100644 frontend/src/context/report/reportSlice.ts diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 4ba10cd339..d20887ecaf 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -1,5 +1,5 @@ import { Tooltip } from '@mui/material' -import { REQUIRED_DATA, TIPS } from '@src/constants/resources' +import { TIPS } from '@src/constants/resources' import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style' import SaveAltIcon from '@mui/icons-material/SaveAlt' import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons' @@ -9,7 +9,7 @@ import { backStep } from '@src/context/stepper/StepperSlice' import { useAppDispatch } from '@src/hooks/useAppDispatch' import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' import { useAppSelector } from '@src/hooks' -import { selectMetrics } from '@src/context/config/configSlice' +import { isSelectBoardMetrics, isSelectDoraMetrics } from '@src/context/config/configSlice' import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' import { StyledButtonGroup, @@ -19,13 +19,13 @@ import { import { ReportResponseDTO } from '@src/clients/report/dto/response' interface ReportButtonGroupProps { - handleSave: () => void + handleSave?: () => void csvTimeStamp: number startDate: string endDate: string setErrorMessage: (message: string) => void - shouldShowBoardExportButton: boolean reportData: ReportResponseDTO | undefined + isShowSaveButton?: boolean } export const ReportButtonGroup = ({ @@ -35,16 +35,12 @@ export const ReportButtonGroup = ({ endDate, setErrorMessage, reportData, - shouldShowBoardExportButton, + isShowSaveButton = true, }: ReportButtonGroupProps) => { const dispatch = useAppDispatch() const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() - const requiredData = useAppSelector(selectMetrics) - const isShowExportPipelineButton = - requiredData.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) || - requiredData.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) || - requiredData.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES) || - requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) + const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) + const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) useEffect(() => { setErrorMessage(errorMessage) @@ -68,11 +64,13 @@ export const ReportButtonGroup = ({ return ( <> - - }> - {COMMON_BUTTONS.SAVE} - - + {isShowSaveButton && ( + + }> + {COMMON_BUTTONS.SAVE} + + + )} {COMMON_BUTTONS.BACK} @@ -83,7 +81,7 @@ export const ReportButtonGroup = ({ > {COMMON_BUTTONS.EXPORT_METRIC_DATA} - {shouldShowBoardExportButton && ( + {isShowExportBoardButton && ( handleDownload(DOWNLOAD_TYPES.BOARD, startDate, endDate)} diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 3f60fa0dab..b04bb55f3a 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,301 +1,148 @@ -// import { useEffect, useLayoutEffect, useState } from 'react' -// import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' -// import { Loading } from '@src/components/Loading' -// import { useAppSelector } from '@src/hooks' -// import { selectConfig, selectJiraColumns, selectMetrics } from '@src/context/config/configSlice' -// import { CALENDAR, PIPELINE_STEP, NAME, REQUIRED_DATA, MESSAGE, TIPS } from '@src/constants/resources' -// import { -// COMMON_BUTTONS, -// DOWNLOAD_TYPES, -// INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// INIT_REPORT_DATA_WITH_TWO_COLUMNS, -// } from '@src/constants/commons' -// import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -// import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -// import { CSVReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' -// import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice' -// import dayjs from 'dayjs' -// import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style' -// import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -// import { backStep, selectTimeStamp } from '@src/context/stepper/StepperSlice' -// import { useAppDispatch } from '@src/hooks/useAppDispatch' -// import { ErrorNotification } from '@src/components/ErrorNotification' -// import { useNavigate } from 'react-router-dom' -// import CollectionDuration from '@src/components/Common/CollectionDuration' -// import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' -// import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' -// import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -// import { ReportResponse } from '@src/clients/report/dto/response' -// import { ROUTE } from '@src/constants/router' -// import { Tooltip } from '@mui/material' -// import SaveAltIcon from '@mui/icons-material/SaveAlt' -// import { -// ButtonGroupStyle, -// ErrorNotificationContainer, -// ExportButton, -// } from '@src/components/Metrics/ReportStep/ReportDetail/style' -// -// interface ReportDetailInterface extends useNotificationLayoutEffectInterface { -// handleSave: () => void -// } -// -// const ReportDetail = ({ updateProps, handleSave }: ReportDetailInterface) => { -// const dispatch = useAppDispatch() -// const navigate = useNavigate() -// const { -// startPollingReports, -// stopPollingReports, -// reports, -// isLoading, -// isServerError, -// errorMessage: reportErrorMsg, -// } = useGenerateReportEffect() -// const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() -// const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) -// const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) -// const [classificationState, setClassificationState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [changeFailureRateState, setChangeFailureRateState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) -// const csvTimeStamp = useAppSelector(selectTimeStamp) -// const configData = useAppSelector(selectConfig) -// const { -// cycleTimeSettings, -// treatFlagCardAsBlock, -// users, -// pipelineCrews, -// targetFields, -// doneColumn, -// deploymentFrequencySettings, -// leadTimeForChanges, -// assigneeFilter, -// } = useAppSelector(selectMetricsContent) -// const { metrics, calendarType, dateRange } = configData.basic -// const { board, pipelineTool, sourceControl } = configData -// const { token, type, site, projectKey, boardId, email } = board.config -// const { startDate, endDate } = dateRange -// const requiredData = useAppSelector(selectMetrics) -// const isShowExportBoardButton = -// requiredData.includes(REQUIRED_DATA.VELOCITY) || -// requiredData.includes(REQUIRED_DATA.CYCLE_TIME) || -// requiredData.includes(REQUIRED_DATA.CLASSIFICATION) -// const isShowExportPipelineButton = -// requiredData.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) || -// requiredData.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) || -// requiredData.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES) || -// requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) -// -// const getPipelineConfig = (pipelineConfigs: IPipelineConfig[]) => { -// if (!pipelineConfigs[0].organization && pipelineConfigs.length === 1) { -// return [] -// } -// return pipelineConfigs.map(({ organization, pipelineName, step, branches }) => { -// const pipelineConfigFromPipelineList = configData.pipelineTool.verifiedResponse.pipelineList.find( -// (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organization -// ) -// if (pipelineConfigFromPipelineList != undefined) { -// const { orgName, orgId, name, id, repository } = pipelineConfigFromPipelineList -// return { -// orgId, -// orgName, -// id, -// name, -// step, -// repository, -// branches, -// } -// } -// }) as { -// id: string -// name: string -// orgId: string -// orgName: string -// repository: string -// step: string -// branches: string[] -// }[] -// } -// -// const jiraColumns = useAppSelector(selectJiraColumns) -// const jiraColumnsWithValue = jiraColumns?.map( -// (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value -// ) -// -// const jiraToken = getJiraBoardToken(token, email) -// const getReportRequestBody = (): ReportRequestDTO => ({ -// metrics: metrics, -// startTime: dayjs(startDate).valueOf().toString(), -// endTime: dayjs(endDate).valueOf().toString(), -// considerHoliday: calendarType === CALENDAR.CHINA, -// buildKiteSetting: { -// pipelineCrews, -// ...pipelineTool.config, -// deploymentEnvList: getPipelineConfig(deploymentFrequencySettings), -// }, -// codebaseSetting: { -// type: sourceControl.config.type, -// token: sourceControl.config.token, -// leadTime: getPipelineConfig(leadTimeForChanges), -// }, -// jiraBoardSetting: { -// token: jiraToken, -// type: type.toLowerCase().replace(' ', '-'), -// site, -// projectKey, -// boardId, -// boardColumns: filterAndMapCycleTimeSettings(cycleTimeSettings, jiraColumnsWithValue), -// treatFlagCardAsBlock, -// users, -// assigneeFilter, -// targetFields, -// doneColumn, -// }, -// csvTimeStamp: csvTimeStamp, -// }) -// -// const getExportCSV = ( -// dataType: DOWNLOAD_TYPES, -// startDate: string | null, -// endDate: string | null -// ): CSVReportRequestDTO => ({ -// dataType: dataType, -// csvTimeStamp: csvTimeStamp, -// startDate: startDate ?? '', -// endDate: endDate ?? '', -// }) -// -// useEffect(() => { -// startPollingReports(getReportRequestBody()) -// }, []) -// -// useEffect(() => { -// updateReportData(reports) -// return () => { -// stopPollingReports() -// } -// }, [reports]) -// -// const updateReportData = (res: ReportResponse | undefined) => { -// res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) -// res?.cycleTimeList && setCycleTimeState({ ...cycleTimeState, value: res.cycleTimeList, isShow: true }) -// res?.classification && setClassificationState({ value: res.classification, isShow: true }) -// res?.deploymentFrequencyList && -// setDeploymentFrequencyState({ -// ...deploymentFrequencyState, -// value: res.deploymentFrequencyList, -// isShow: true, -// }) -// res?.meanTimeToRecoveryList && -// setMeanTimeToRecoveryState({ -// ...meanTimeToRecoveryState, -// value: res.meanTimeToRecoveryList, -// isShow: true, -// }) -// res?.changeFailureRateList && -// setChangeFailureRateState({ -// ...changeFailureRateState, -// value: res.changeFailureRateList, -// isShow: true, -// }) -// res?.leadTimeForChangesList && -// setLeadTimeForChangesState({ -// ...leadTimeForChangesState, -// value: res.leadTimeForChangesList, -// isShow: true, -// }) -// res?.exportValidityTimeMin && setExportValidityTimeMin(res.exportValidityTimeMin) -// } -// -// const handleDownload = (dataType: DOWNLOAD_TYPES, startDate: string | null, endDate: string | null) => { -// fetchExportData(getExportCSV(dataType, startDate, endDate)) -// } -// -// const handleBack = () => { -// dispatch(backStep()) -// } -// -// return ( -// <> -// {isLoading ? ( -// -// ) : isServerError ? ( -// navigate(ROUTE.ERROR_PAGE) -// ) : ( -// <> -// {startDate && endDate && } -// {reportErrorMsg && ( -// -// -// -// )} -// {errorMessage && ( -// -// -// -// )} -// {velocityState.isShow && } -// {cycleTimeState.isShow && } -// {classificationState.isShow && ( -// -// )} -// {deploymentFrequencyState.isShow && ( -// -// )} -// {leadTimeForChangesState.isShow && ( -// -// )} -// {changeFailureRateState.isShow && ( -// -// )} -// {meanTimeToRecoveryState.isShow && ( -// -// )} -// -// )} -// -// ) -// } -// -// export default ReportDetail +import React, { useEffect, useState } from 'react' +import { useAppSelector } from '@src/hooks' +import { selectConfig } from '@src/context/config/configSlice' +import { PIPELINE_STEP, NAME } from '@src/constants/resources' +import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import { selectTimeStamp } from '@src/context/stepper/StepperSlice' +import { ErrorNotification } from '@src/components/ErrorNotification' +import CollectionDuration from '@src/components/Common/CollectionDuration' +import { ReportResponse } from '@src/clients/report/dto/response' +import { ErrorNotificationContainer } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { selectReportData } from '@src/context/report/reportSlice' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' +import Header from '@src/layouts/Header' + +const ReportDetail = () => { + const [errorMessage, setErrorMessage] = useState() + const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) + const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) + const [classificationState, setClassificationState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [changeFailureRateState, setChangeFailureRateState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const configData = useAppSelector(selectConfig) + const { dateRange } = configData.basic + const { startDate, endDate } = dateRange + const csvTimeStamp = useAppSelector(selectTimeStamp) + + const reportData = useAppSelector(selectReportData) + + useEffect(() => { + updateReportData(reportMapper(reportData)) + }) + + const updateReportData = (res: ReportResponse | undefined) => { + res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) + res?.cycleTimeList && setCycleTimeState({ ...cycleTimeState, value: res.cycleTimeList, isShow: true }) + res?.classification && setClassificationState({ value: res.classification, isShow: true }) + res?.deploymentFrequencyList && + setDeploymentFrequencyState({ + ...deploymentFrequencyState, + value: res.deploymentFrequencyList, + isShow: true, + }) + res?.meanTimeToRecoveryList && + setMeanTimeToRecoveryState({ + ...meanTimeToRecoveryState, + value: res.meanTimeToRecoveryList, + isShow: true, + }) + res?.changeFailureRateList && + setChangeFailureRateState({ + ...changeFailureRateState, + value: res.changeFailureRateList, + isShow: true, + }) + res?.leadTimeForChangesList && + setLeadTimeForChangesState({ + ...leadTimeForChangesState, + value: res.leadTimeForChangesList, + isShow: true, + }) + } + + return ( + <> +
+ {startDate && endDate && } + {errorMessage && ( + + + + )} + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} + /> + + ) +} + +export default ReportDetail diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 04d82f1e8a..0ae8faa0a8 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useLayoutEffect, useState } from 'react' import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' import { useAppSelector } from '@src/hooks' -import { selectConfig } from '@src/context/config/configSlice' -import { BOARD_METRICS, DORA_METRICS, MESSAGE } from '@src/constants/resources' +import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' +import { MESSAGE } from '@src/constants/resources' import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' import { ErrorNotification } from '@src/components/ErrorNotification' import { useNavigate } from 'react-router-dom' @@ -39,9 +39,8 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const { updateProps } = notification const [errorMessage, setErrorMessage] = useState() - const { metrics } = configData.basic - const shouldShowBoardMetrics = metrics.some((metric) => BOARD_METRICS.includes(metric)) - const shouldShowDoraMetrics = metrics.some((metric) => DORA_METRICS.includes(metric)) + const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics) + const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics) useLayoutEffect(() => { exportValidityTimeMin && @@ -125,7 +124,6 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { handleSave()} reportData={reportData} - shouldShowBoardExportButton={shouldShowBoardMetrics} startDate={startDate} endDate={endDate} csvTimeStamp={csvTimeStamp} diff --git a/frontend/src/context/config/configSlice.ts b/frontend/src/context/config/configSlice.ts index 256f747c47..eac7b16ce8 100644 --- a/frontend/src/context/config/configSlice.ts +++ b/frontend/src/context/config/configSlice.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit' import type { RootState } from '@src/store' -import { CALENDAR, MESSAGE } from '@src/constants/resources' +import { BOARD_METRICS, CALENDAR, DORA_METRICS, MESSAGE } from '@src/constants/resources' import { REQUIRED_DATA } from '@src/constants/resources' import { IBoardState, initialBoardState } from '@src/context/config/board/boardSlice' import { initialPipelineToolState, IPipelineToolState } from '@src/context/config/pipelineTool/pipelineToolSlice' @@ -183,6 +183,10 @@ export const selectProjectName = (state: RootState) => state.config.basic.projec export const selectCalendarType = (state: RootState) => state.config.basic.calendarType export const selectDateRange = (state: RootState) => state.config.basic.dateRange export const selectMetrics = (state: RootState) => state.config.basic.metrics +export const isSelectBoardMetrics = (state: RootState) => + state.config.basic.metrics.some((metric) => BOARD_METRICS.includes(metric)) +export const isSelectDoraMetrics = (state: RootState) => + state.config.basic.metrics.some((metric) => DORA_METRICS.includes(metric)) export const selectBoard = (state: RootState) => state.config.board.config export const isPipelineToolVerified = (state: RootState) => state.config.pipelineTool.isVerified export const selectPipelineTool = (state: RootState) => state.config.pipelineTool.config diff --git a/frontend/src/context/report/reportSlice.ts b/frontend/src/context/report/reportSlice.ts new file mode 100644 index 0000000000..5152163c24 --- /dev/null +++ b/frontend/src/context/report/reportSlice.ts @@ -0,0 +1,30 @@ +import { createSlice } from '@reduxjs/toolkit' +import type { RootState } from '@src/store' +import { ReportResponseDTO } from '@src/clients/report/dto/response' + +export interface ReportState { + reportData: ReportResponseDTO | null +} + +export const initialBoardState: ReportState = { + reportData: null, +} + +export const reportSlice = createSlice({ + name: 'report', + initialState: { + ...initialBoardState, + }, + reducers: { + updateReportData: (state, action) => { + state.reportData = action.payload + }, + }, +}) +export const { updateReportData } = reportSlice.actions + +export const selectReportData = (state: RootState) => { + return state.report.reportData +} + +export default reportSlice.reducer diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 5d0210b91b..21d2ca1ab7 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -6,6 +6,8 @@ import { InternalServerException } from '@src/exceptions/InternalServerException import { ReportResponseDTO } from '@src/clients/report/dto/response' import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons' import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' +import { useAppDispatch } from '@src/hooks/useAppDispatch' +import { updateReportData } from '@src/context/report/reportSlice' export interface useGenerateReportEffectInterface { startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void @@ -18,6 +20,7 @@ export interface useGenerateReportEffectInterface { export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const reportPath = '/reports' + const dispatch = useAppDispatch() const [isServerError, setIsServerError] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [reportData, setReportData] = useState() @@ -88,7 +91,9 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const handleAndUpdateData = (response: ReportResponseDTO) => { const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime) - setReportData({ ...response, exportValidityTime: exportValidityTime }) + const report = { ...response, exportValidityTime: exportValidityTime } + setReportData(report) + dispatch(updateReportData(report)) } return { diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 385fba18a7..6296065eba 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -3,6 +3,7 @@ import stepperReducer from './context/stepper/StepperSlice' import configReducer from './context/config/configSlice' import metricsSlice from './context/Metrics/metricsSlice' import headerSlice from '@src/context/header/headerSlice' +import reportSlice from '@src/context/report/reportSlice' export const store = configureStore({ reducer: { @@ -10,6 +11,7 @@ export const store = configureStore({ config: configReducer, metrics: metricsSlice, header: headerSlice, + report: reportSlice, }, }) From 3ae9fb6b6d8e908fddffeb1c94bd468ad68228e7 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 16:05:38 +0800 Subject: [PATCH 11/90] ADM-652 [frontend] feat: add deep for useEffect --- .../src/components/Metrics/ReportStep/ReportDetail/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index b04bb55f3a..0be08bca67 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -16,7 +16,7 @@ import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' const ReportDetail = () => { - const [errorMessage, setErrorMessage] = useState() + const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [classificationState, setClassificationState] = useState({ @@ -48,7 +48,7 @@ const ReportDetail = () => { useEffect(() => { updateReportData(reportMapper(reportData)) - }) + }, [reportData]) const updateReportData = (res: ReportResponse | undefined) => { res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) From 7a07035e98e36bcb225c609386bf3dfd79842a6a Mon Sep 17 00:00:00 2001 From: xuebing Date: Thu, 4 Jan 2024 11:09:55 +0800 Subject: [PATCH 12/90] ADM-652 [frontend] feat: enhance style for detail page --- .../Metrics/ReportStep/ReportDetail/index.tsx | 108 +++++++++--------- .../Metrics/ReportStep/ReportDetail/style.tsx | 20 +--- 2 files changed, 59 insertions(+), 69 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 0be08bca67..a2cc94cf52 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -7,9 +7,8 @@ import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import { ErrorNotification } from '@src/components/ErrorNotification' -import CollectionDuration from '@src/components/Common/CollectionDuration' import { ReportResponse } from '@src/clients/report/dto/response' -import { ErrorNotificationContainer } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { ErrorNotificationContainer, StyledTableWrapper } from '@src/components/Metrics/ReportStep/ReportDetail/style' import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' @@ -83,64 +82,65 @@ const ReportDetail = () => { return ( <>
- {startDate && endDate && } {errorMessage && ( )} - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} /> - )} - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - { - setErrorMessage(message) - }} - csvTimeStamp={csvTimeStamp} - /> + ) } diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx index 709f0d816c..472aa7c78c 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx @@ -1,20 +1,5 @@ import { styled } from '@mui/material/styles' -import { Button } from '@mui/material' -import { theme } from '@src/theme' import { Z_INDEX } from '@src/constants/commons' -import { basicButtonStyle } from '@src/components/Metrics/ReportStep/style' - -export const ExportButton = styled(Button)({ - ...basicButtonStyle, - width: '12rem', - backgroundColor: theme.main.backgroundColor, - color: theme.main.color, - '&:hover': { - ...basicButtonStyle, - backgroundColor: theme.main.backgroundColor, - color: theme.main.color, - }, -}) export const ErrorNotificationContainer = styled('div')({ position: 'fixed', @@ -24,3 +9,8 @@ export const ErrorNotificationContainer = styled('div')({ zIndex: Z_INDEX.MODAL_BACKDROP, width: '80%', }) + +export const StyledTableWrapper = styled('div')({ + width: '80%', + margin: '2rem auto', +}) From 79e8cb1e5158cba773891c5ff88bc553989328d6 Mon Sep 17 00:00:00 2001 From: xuebing Date: Thu, 4 Jan 2024 14:31:57 +0800 Subject: [PATCH 13/90] ADM-652 [frontend] feat: enhance button style --- .../Metrics/ReportButtonGroup/index.tsx | 2 +- .../Metrics/ReportButtonGroup/style.tsx | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index d20887ecaf..9694c05e5c 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -63,7 +63,7 @@ export const ReportButtonGroup = ({ return ( <> - + {isShowSaveButton && ( }> diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index 6a1745465f..c0e764bed9 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -12,16 +12,16 @@ export const StyledRightButtonGroup = styled('div')({ }, }) -export const StyledButtonGroup = styled('div')({ - boxSizing: 'border-box', - display: 'flex', - alignItems: 'center', - textAlign: 'center', - margin: '0 auto', - justifyContent: 'space-between', - width: '100%', - paddingTop: '2rem', -}) +export const StyledButtonGroup = styled('div')` + box-sizing: border-box; + display: flex; + align-items: center; + text-align: center; + margin: 0 auto; + justify-content: ${(props) => (props.isShowSaveButton ? 'space-between' : 'flex-end')}; + width: 100%; + padding-top: 2rem; +` export const StyledExportButton = styled(Button)({ ...basicButtonStyle, From 1fd94b9abbab6c836edb4a9a60942e84798402c1 Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 09:36:18 +0800 Subject: [PATCH 14/90] ADM-652 [frontend] feat: enhance router logic --- .../Metrics/ReportButtonGroup/index.tsx | 12 ++- .../Metrics/ReportButtonGroup/style.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 9 +- .../Metrics/ReportStep/BoradMetrics/style.tsx | 4 +- .../Metrics/ReportStep/ReportDetail/index.tsx | 100 ++++++++++-------- 5 files changed, 72 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 9694c05e5c..1be0f8c666 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -17,6 +17,7 @@ import { StyledRightButtonGroup, } from '@src/components/Metrics/ReportButtonGroup/style' import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { useNavigate } from 'react-router-dom' interface ReportButtonGroupProps { handleSave?: () => void @@ -25,7 +26,7 @@ interface ReportButtonGroupProps { endDate: string setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined - isShowSaveButton?: boolean + isFromDetailPage?: boolean } export const ReportButtonGroup = ({ @@ -35,9 +36,10 @@ export const ReportButtonGroup = ({ endDate, setErrorMessage, reportData, - isShowSaveButton = true, + isFromDetailPage = false, }: ReportButtonGroupProps) => { const dispatch = useAppDispatch() + const navigate = useNavigate() const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) @@ -58,13 +60,13 @@ export const ReportButtonGroup = ({ } const handleBack = () => { - dispatch(backStep()) + isFromDetailPage ? navigate(-1) : dispatch(backStep()) } return ( <> - - {isShowSaveButton && ( + + {!isFromDetailPage && ( }> {COMMON_BUTTONS.SAVE} diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index c0e764bed9..d33b638e68 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -18,7 +18,7 @@ export const StyledButtonGroup = styled('div')` align-items: center; text-align: center; margin: 0 auto; - justify-content: ${(props) => (props.isShowSaveButton ? 'space-between' : 'flex-end')}; + justify-content: ${(props) => (props.isFromDetailPage ? 'flex-end' : 'space-between')}; width: 100%; padding-top: 2rem; ` diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 13428326ec..364b8223c5 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -23,6 +23,7 @@ import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' import { useNavigate } from 'react-router-dom' import { ROUTE } from '@src/constants/router' +import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' interface BoardMetricsProps { startToRequestBoardData: (request: ReportRequestDTO) => void @@ -125,16 +126,14 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) - const handleShowMore = () => { - navigate(ROUTE.METRICS_DETAIL_PAGE) - } - return ( <> - {boardReport && {'show more >'}} + + {'show more >'} + diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index f9a67947bc..015987f1c7 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,5 +1,6 @@ import { styled } from '@mui/material/styles' import { theme } from '@src/theme' +import { Link } from 'react-router-dom' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', @@ -11,8 +12,9 @@ export const StyledTitleWrapper = styled('div')({ marginBottom: '1rem', }) -export const StyledShowMore = styled('div')({ +export const StyledShowMore = styled(Link)({ marginLeft: '0.5rem', fontSize: '0.8rem', + textDecoration: 'none', color: theme.palette.link, }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index a2cc94cf52..ea58bf481f 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,8 +1,12 @@ import React, { useEffect, useState } from 'react' import { useAppSelector } from '@src/hooks' import { selectConfig } from '@src/context/config/configSlice' -import { PIPELINE_STEP, NAME } from '@src/constants/resources' -import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { + INIT_REPORT_DATA_WITH_THREE_COLUMNS, + INIT_REPORT_DATA_WITH_TWO_COLUMNS, + RETRIEVE_REPORT_TYPES, +} from '@src/constants/commons' import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' @@ -13,8 +17,10 @@ import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' +import { useLocation } from 'react-router-dom' const ReportDetail = () => { + const { state } = useLocation() const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) @@ -88,50 +94,58 @@ const ReportDetail = () => { )} - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - + {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + <> + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + )} - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - + {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + <> + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + )} Date: Fri, 5 Jan 2024 10:15:35 +0800 Subject: [PATCH 15/90] ADM-652 [frontend] feat: add Breadcrumbs --- .../Metrics/ReportStep/ReportDetail/index.tsx | 158 ++++++++++-------- .../Metrics/ReportStep/ReportDetail/style.tsx | 11 +- 2 files changed, 100 insertions(+), 69 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index ea58bf481f..03e0a7a0af 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -12,15 +12,22 @@ import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import { ErrorNotification } from '@src/components/ErrorNotification' import { ReportResponse } from '@src/clients/report/dto/response' -import { ErrorNotificationContainer, StyledTableWrapper } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { + ErrorNotificationContainer, + StyledContainer, + StyledNavigator, + StyledTableWrapper, +} from '@src/components/Metrics/ReportStep/ReportDetail/style' import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' -import { useLocation } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' +import { Breadcrumbs, Link, Typography } from '@mui/material' const ReportDetail = () => { const { state } = useLocation() + const navigate = useNavigate() const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) @@ -85,76 +92,91 @@ const ReportDetail = () => { }) } + const handleBack = (event) => { + event.preventDefault() + navigate(-1) + } + return ( <>
- {errorMessage && ( - - - - )} - - {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( - <> - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - - )} - - )} - {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( - <> - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - + + {errorMessage && ( + + + )} - { - setErrorMessage(message) - }} - csvTimeStamp={csvTimeStamp} - /> - + + + + Report + + board + + + + {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + <> + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + + )} + {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + <> + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} + /> + + ) } diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx index 472aa7c78c..320cb82993 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx @@ -11,6 +11,15 @@ export const ErrorNotificationContainer = styled('div')({ }) export const StyledTableWrapper = styled('div')({ + width: '100%', + margin: '2rem 0', +}) + +export const StyledNavigator = styled('div')({ + margin: '1rem 0', +}) + +export const StyledContainer = styled('div')({ width: '80%', - margin: '2rem auto', + margin: '0 auto', }) From cb4c34060e31e6e766a79a7e64f5b56f9edb1413 Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 10:44:40 +0800 Subject: [PATCH 16/90] ADM-652 [frontend] feat: extra REPORT_METRICS const --- .../Metrics/ReportStep/ReportDetail/index.tsx | 24 +++++++++++-------- frontend/src/constants/resources.ts | 6 +++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 03e0a7a0af..1a52505811 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react' import { useAppSelector } from '@src/hooks' import { selectConfig } from '@src/context/config/configSlice' -import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { NAME, PIPELINE_STEP, REPORT_METRICS, REQUIRED_DATA } from '@src/constants/resources' import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS, @@ -109,19 +109,23 @@ const ReportDetail = () => { - Report + {REPORT_METRICS.REPORT} - board + {REPORT_METRICS.BOARD} {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( <> - {velocityState.isShow && } - {cycleTimeState.isShow && } + {velocityState.isShow && ( + + )} + {cycleTimeState.isShow && ( + + )} {classificationState.isShow && ( { <> {deploymentFrequencyState.isShow && ( { )} {leadTimeForChangesState.isShow && ( { )} {changeFailureRateState.isShow && ( { )} {meanTimeToRecoveryState.isShow && ( Date: Fri, 5 Jan 2024 11:00:05 +0800 Subject: [PATCH 17/90] ADM-652 [frontend] feat: add show more for Dora metrics --- .../Metrics/ReportStep/DoraMetrics/index.tsx | 10 +++++++++- .../Metrics/ReportStep/DoraMetrics/style.tsx | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index c8ab222eec..0c8984ec1a 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -18,6 +18,9 @@ import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' import { StyledSpacing } from '@src/components/Metrics/ReportStep/style' import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util' +import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style' +import { ROUTE } from '@src/constants/router' +import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' interface DoraMetricsProps { startToRequestDoraData: (request: ReportRequestDTO) => void @@ -168,7 +171,12 @@ const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDa return ( <> - + + + + {'show more >'} + + {shouldShowSourceControl && } diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index 84eb460497..d400534e26 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -1,5 +1,20 @@ import { styled } from '@mui/material/styles' +import { Link } from 'react-router-dom' +import { theme } from '@src/theme' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', }) + +export const StyledTitleWrapper = styled('div')({ + display: 'flex', + alignItems: 'center', + marginBottom: '1rem', +}) + +export const StyledShowMore = styled(Link)({ + marginLeft: '0.5rem', + fontSize: '0.8rem', + textDecoration: 'none', + color: theme.palette.link, +}) From 622435adbce2f2abc26f9a23d3de10b558268ace Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 11:02:42 +0800 Subject: [PATCH 18/90] ADM-652 [frontend] feat: add SHOW_MORE constant --- .../src/components/Metrics/ReportStep/BoradMetrics/index.tsx | 3 ++- .../src/components/Metrics/ReportStep/DoraMetrics/index.tsx | 3 ++- frontend/src/constants/resources.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 364b8223c5..d14e334f3e 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -8,6 +8,7 @@ import { REPORT_PAGE, METRICS_TITLE, REQUIRED_DATA, + SHOW_MORE, } from '@src/constants/resources' import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' import { selectMetricsContent } from '@src/context/Metrics/metricsSlice' @@ -132,7 +133,7 @@ const BoardMetrics = ({ - {'show more >'} + {SHOW_MORE} diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index 0c8984ec1a..0152a6b045 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -8,6 +8,7 @@ import { REPORT_PAGE, METRICS_TITLE, REQUIRED_DATA, + SHOW_MORE, } from '@src/constants/resources' import { ReportRequestDTO } from '@src/clients/report/dto/request' import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice' @@ -174,7 +175,7 @@ const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDa - {'show more >'} + {SHOW_MORE} {shouldShowSourceControl && } diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index de095a7f17..0a4bbd7a55 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -9,6 +9,8 @@ export const REPORT_METRICS = { DORA: 'Dora Metrics', } +export const SHOW_MORE = 'show more >' + export enum REQUIRED_DATA { All = 'All', VELOCITY = 'Velocity', From 47fcf3787c746f1fad8e6406ee4302b817b4f4c8 Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 11:16:09 +0800 Subject: [PATCH 19/90] ADM-652 [frontend] feat: enhance show export button logic --- .../Metrics/ReportButtonGroup/index.tsx | 22 ++++++++++++------- .../Metrics/ReportStep/ReportDetail/index.tsx | 10 +++++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 1be0f8c666..15c558c368 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -27,10 +27,14 @@ interface ReportButtonGroupProps { setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined isFromDetailPage?: boolean + isShowDoraMetrics?: boolean + isShowBoardMetrics?: boolean } export const ReportButtonGroup = ({ handleSave, + isShowDoraMetrics, + isShowBoardMetrics, csvTimeStamp, startDate, endDate, @@ -77,13 +81,15 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.BACK} - handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} - > - {COMMON_BUTTONS.EXPORT_METRIC_DATA} - - {isShowExportBoardButton && ( + {!isFromDetailPage && ( + handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} + > + {COMMON_BUTTONS.EXPORT_METRIC_DATA} + + )} + {(isFromDetailPage ? isShowBoardMetrics : isShowExportBoardButton) && ( handleDownload(DOWNLOAD_TYPES.BOARD, startDate, endDate)} @@ -91,7 +97,7 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.EXPORT_BOARD_DATA} )} - {isShowExportPipelineButton && ( + {(isFromDetailPage ? isShowDoraMetrics : isShowExportPipelineButton) && ( { navigate(-1) } + const isShowBoardMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.BOARD + + const isShowDoraMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.DORA + return ( <>
@@ -115,7 +119,7 @@ const ReportDetail = () => { - {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + {isShowBoardMetrics() && ( <> {velocityState.isShow && ( @@ -133,7 +137,7 @@ const ReportDetail = () => { )} )} - {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + {isShowDoraMetrics() && ( <> {deploymentFrequencyState.isShow && ( { )} Date: Fri, 5 Jan 2024 16:01:07 +0800 Subject: [PATCH 20/90] [ADM-652] Update detail page show. --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 154 ++++++++------- .../Metrics/ReportStep/ReportDetail/index.tsx | 176 +++++------------- .../components/Metrics/ReportStep/index.tsx | 124 ++++++------ frontend/src/config/routes.ts | 5 - frontend/src/utils/types.ts | 2 + 5 files changed, 201 insertions(+), 260 deletions(-) create mode 100644 frontend/src/utils/types.ts diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index d14e334f3e..412208ed46 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react' -import { useAppSelector } from '@src/hooks' -import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice' +import React, { useEffect } from 'react'; +import { useAppSelector } from '@src/hooks'; +import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice'; import { BOARD_METRICS, CALENDAR, @@ -17,107 +17,104 @@ import { StyledMetricsSection, StyledShowMore, StyledTitleWrapper, -} from '@src/components/Metrics/ReportStep/BoradMetrics/style' -import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' -import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' -import { ReportGrid } from '@src/components/Common/ReportGrid' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { useNavigate } from 'react-router-dom' -import { ROUTE } from '@src/constants/router' -import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' +} from '@src/components/Metrics/ReportStep/BoradMetrics/style'; +import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util'; +import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle'; +import { ReportGrid } from '@src/components/Common/ReportGrid'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { NullAble as Nullable } from '@src/utils/types'; interface BoardMetricsProps { - startToRequestBoardData: (request: ReportRequestDTO) => void - boardReport?: ReportResponseDTO - csvTimeStamp: number - startDate: string | null - endDate: string | null + startToRequestBoardData: (request: ReportRequestDTO) => void; + onShowDetail: () => void; + boardReport?: ReportResponseDTO; + csvTimeStamp: number; + startDate: Nullable; + endDate: Nullable; } const BoardMetrics = ({ startToRequestBoardData, + onShowDetail, boardReport, csvTimeStamp, startDate, endDate, }: BoardMetricsProps) => { - const configData = useAppSelector(selectConfig) - const navigate = useNavigate() + const configData = useAppSelector(selectConfig); const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = - useAppSelector(selectMetricsContent) - const jiraColumns = useAppSelector(selectJiraColumns) + useAppSelector(selectMetricsContent); + const jiraColumns = useAppSelector(selectJiraColumns); - const { metrics, calendarType } = configData.basic - const { board } = configData - const { token, type, site, projectKey, boardId, email } = board.config - const jiraToken = getJiraBoardToken(token, email) + const { metrics, calendarType } = configData.basic; + const { board } = configData; + const { token, type, site, projectKey, boardId, email } = board.config; + const jiraToken = getJiraBoardToken(token, email); const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value - ) - const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)) + ); + const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)); - const getBoardReportRequestBody = (): BoardReportRequestDTO => { - return { - metrics: boardMetrics, - startTime: dayjs(startDate).valueOf().toString(), - endTime: dayjs(endDate).valueOf().toString(), - considerHoliday: calendarType === CALENDAR.CHINA, - jiraBoardSetting: { - token: jiraToken, - type: type.toLowerCase().replace(' ', '-'), - site, - projectKey, - boardId, - boardColumns: filterAndMapCycleTimeSettings(cycleTimeSettings, jiraColumnsWithValue), - treatFlagCardAsBlock, - users, - assigneeFilter, - targetFields, - doneColumn, - }, - csvTimeStamp: csvTimeStamp, - } - } + const getBoardReportRequestBody = (): BoardReportRequestDTO => ({ + metrics: boardMetrics, + startTime: dayjs(startDate).valueOf().toString(), + endTime: dayjs(endDate).valueOf().toString(), + considerHoliday: calendarType === CALENDAR.CHINA, + jiraBoardSetting: { + token: jiraToken, + type: type.toLowerCase().replace(' ', '-'), + site, + projectKey, + boardId, + boardColumns: filterAndMapCycleTimeSettings(cycleTimeSettings, jiraColumnsWithValue), + treatFlagCardAsBlock, + users, + assigneeFilter, + targetFields, + doneColumn, + }, + csvTimeStamp: csvTimeStamp, + }); const getBoardItems = () => { const velocity = boardReport?.velocity const cycleTime = boardReport?.cycleTime const velocityItems = boardMetrics.includes(REQUIRED_DATA.VELOCITY) ? [ - { - title: METRICS_TITLE.VELOCITY, - items: velocity && [ - { - value: velocity.velocityForSP, - subtitle: METRICS_SUBTITLE.VELOCITY, - isToFixed: false, - }, - { - value: velocity.velocityForCards, - subtitle: METRICS_SUBTITLE.THROUGHPUT, - isToFixed: false, - }, - ], - }, - ] + { + title: METRICS_TITLE.VELOCITY, + items: velocity && [ + { + value: velocity.velocityForSP, + subtitle: METRICS_SUBTITLE.VELOCITY, + isToFixed: false, + }, + { + value: velocity.velocityForCards, + subtitle: METRICS_SUBTITLE.THROUGHPUT, + isToFixed: false, + }, + ], + }, + ] : [] const cycleTimeItems = boardMetrics.includes(REQUIRED_DATA.CYCLE_TIME) ? [ - { - title: METRICS_TITLE.CYCLE_TIME, - items: cycleTime && [ - { - value: cycleTime.averageCycleTimePerSP, - subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_SP, - }, - { - value: cycleTime.averageCycleTimePerCard, - subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_CARD, - }, - ], - }, - ] + { + title: METRICS_TITLE.CYCLE_TIME, + items: cycleTime && [ + { + value: cycleTime.averageCycleTimePerSP, + subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_SP, + }, + { + value: cycleTime.averageCycleTimePerCard, + subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_CARD, + }, + ], + }, + ] : [] return [...velocityItems, ...cycleTimeItems] @@ -127,6 +124,7 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) + return ( <> diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index b89433cb8b..f30d4f1c3c 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,34 +1,14 @@ -import React, { useEffect, useState } from 'react' -import { useAppSelector } from '@src/hooks' -import { selectConfig } from '@src/context/config/configSlice' -import { NAME, PIPELINE_STEP, REPORT_METRICS, REQUIRED_DATA } from '@src/constants/resources' -import { - INIT_REPORT_DATA_WITH_THREE_COLUMNS, - INIT_REPORT_DATA_WITH_TWO_COLUMNS, - RETRIEVE_REPORT_TYPES, -} from '@src/constants/commons' -import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -import { selectTimeStamp } from '@src/context/stepper/StepperSlice' -import { ErrorNotification } from '@src/components/ErrorNotification' import { ReportResponse } from '@src/clients/report/dto/response' -import { - ErrorNotificationContainer, - StyledContainer, - StyledNavigator, - StyledTableWrapper, -} from '@src/components/Metrics/ReportStep/ReportDetail/style' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' +import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' import { selectReportData } from '@src/context/report/reportSlice' +import { useAppSelector } from '@src/hooks' import { reportMapper } from '@src/hooks/reportMapper/report' -import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' -import Header from '@src/layouts/Header' -import { useLocation, useNavigate } from 'react-router-dom' -import { Breadcrumbs, Link, Typography } from '@mui/material' +import { useEffect, useState } from 'react' -const ReportDetail = () => { - const { state } = useLocation() - const navigate = useNavigate() - const [errorMessage, setErrorMessage] = useState('') +const ReportDetail = ({ onBack }: { onBack: () => void }) => { const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [classificationState, setClassificationState] = useState({ @@ -51,15 +31,11 @@ const ReportDetail = () => { value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, isShow: false, }) - const configData = useAppSelector(selectConfig) - const { dateRange } = configData.basic - const { startDate, endDate } = dateRange - const csvTimeStamp = useAppSelector(selectTimeStamp) const reportData = useAppSelector(selectReportData) useEffect(() => { - updateReportData(reportMapper(reportData)) + updateReportData(reportMapper(reportData!)) }, [reportData]) const updateReportData = (res: ReportResponse | undefined) => { @@ -92,101 +68,51 @@ const ReportDetail = () => { }) } - const handleBack = (event) => { - event.preventDefault() - navigate(-1) - } - - const isShowBoardMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.BOARD - - const isShowDoraMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.DORA - return ( <> -
- - {errorMessage && ( - - - - )} - - - - {REPORT_METRICS.REPORT} - - {REPORT_METRICS.BOARD} - - - - {isShowBoardMetrics() && ( - <> - {velocityState.isShow && ( - - )} - {cycleTimeState.isShow && ( - - )} - {classificationState.isShow && ( - - )} - - )} - {isShowDoraMetrics() && ( - <> - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - - )} - { - setErrorMessage(message) - }} - csvTimeStamp={csvTimeStamp} - /> - - +
{'< back'}
+ {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} ) } diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 0ae8faa0a8..13291975fb 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -1,23 +1,28 @@ -import React, { useEffect, useLayoutEffect, useState } from 'react' -import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' -import { useAppSelector } from '@src/hooks' -import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' -import { MESSAGE } from '@src/constants/resources' -import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { useNavigate } from 'react-router-dom' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -import { ROUTE } from '@src/constants/router' -import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' -import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics' -import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' -import { selectTimeStamp } from '@src/context/stepper/StepperSlice' +import React, { useEffect, useLayoutEffect, useState } from 'react'; +import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect'; +import { useAppSelector } from '@src/hooks'; +import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice'; +import { MESSAGE } from '@src/constants/resources'; +import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { useNavigate } from 'react-router-dom'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; +import { ROUTE } from '@src/constants/router'; +import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup'; +import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics'; +import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics'; +import { selectTimeStamp } from '@src/context/stepper/StepperSlice'; +import DateRangeViewer from '@src/components/Common/DateRangeViewer'; +import { MetricSelectionHeader } from '../MetricsStep/style'; +import ReportDetail from './ReportDetail'; export interface ReportStepProps { - notification: useNotificationLayoutEffectInterface - handleSave: () => void + notification: useNotificationLayoutEffectInterface; + handleSave: () => void; } +type PageType = 'Summary' | 'BoardReport' | 'DoraReport'; + const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const navigate = useNavigate() const { @@ -27,20 +32,21 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startToRequestDoraData, reportData, stopPollingReports, - } = useGenerateReportEffect() + } = useGenerateReportEffect(); - const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) - const configData = useAppSelector(selectConfig) - const csvTimeStamp = useAppSelector(selectTimeStamp) + const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined); + const [pageType, setPageType] = useState('Summary'); + const configData = useAppSelector(selectConfig); + const csvTimeStamp = useAppSelector(selectTimeStamp); - const startDate = configData.basic.dateRange.startDate ?? '' - const endDate = configData.basic.dateRange.endDate ?? '' + const startDate = configData.basic.dateRange.startDate ?? ''; + const endDate = configData.basic.dateRange.endDate ?? ''; - const { updateProps } = notification - const [errorMessage, setErrorMessage] = useState() + const { updateProps } = notification; + const [errorMessage, setErrorMessage] = useState(); - const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics) - const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics) + const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics); + const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics); useLayoutEffect(() => { exportValidityTimeMin && @@ -49,7 +55,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { title: MESSAGE.NOTIFICATION_FIRST_REPORT.replace('%s', exportValidityTimeMin.toString()), closeAutomatically: true, }) - }, [exportValidityTimeMin]) + }, [exportValidityTimeMin]); useLayoutEffect(() => { if (exportValidityTimeMin) { @@ -74,21 +80,45 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { clearInterval(timer) } } - }, [exportValidityTimeMin]) + }, [exportValidityTimeMin]); useEffect(() => { setErrorMessage(reportErrorMsg) - }, [reportErrorMsg]) + }, [reportErrorMsg]); useEffect(() => { setExportValidityTimeMin(reportData?.exportValidityTime) - }, [reportData]) + }, [reportData]); useLayoutEffect(() => { return () => { stopPollingReports() } - }, []) + }, []); + + const showSummary = () => <> + {shouldShowBoardMetrics && ( + setPageType('BoardReport')} + boardReport={reportData} + csvTimeStamp={csvTimeStamp} + /> + )} + {shouldShowDoraMetrics && ( + + )} + ; + const showBoardDetail = () => setPageType('Summary')} /> + return ( <> @@ -96,31 +126,21 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { navigate(ROUTE.ERROR_PAGE) ) : ( <> + {startDate && endDate && ( + + + + )} {errorMessage && ( )} - <> - {shouldShowBoardMetrics && ( - - )} - {shouldShowDoraMetrics && ( - - )} - + { + pageType === 'Summary' ? showSummary() : + pageType === "BoardReport" ? showBoardDetail() : + showBoardDetail() + } handleSave()} reportData={reportData} @@ -137,4 +157,4 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { ) } -export default ReportStep +export default ReportStep; diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index c6c283160a..87cb5760d5 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -12,11 +12,6 @@ export const routes = [ component: lazy(() => import('../pages/Metrics')), name: 'Metrics', }, - { - path: 'detail', - component: lazy(() => import('../components/Metrics/ReportStep/ReportDetail')), - name: 'Detail', - }, { path: '/error-page', component: lazy(() => import('../pages/ErrorPage')), diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts new file mode 100644 index 0000000000..cd98507017 --- /dev/null +++ b/frontend/src/utils/types.ts @@ -0,0 +1,2 @@ +export type NullAble = T | null; +export type Optional = T | null | undefined; \ No newline at end of file From fb1bc714558a87c25cff685a366b86c94a5bf27a Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Fri, 5 Jan 2024 17:54:40 +0800 Subject: [PATCH 21/90] fix format --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 115 ++++++++-------- .../components/Metrics/ReportStep/index.tsx | 128 +++++++++--------- frontend/src/utils/types.ts | 4 +- 3 files changed, 121 insertions(+), 126 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 412208ed46..a6a247bfc2 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react'; -import { useAppSelector } from '@src/hooks'; -import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice'; +import React, { useEffect } from 'react' +import { useAppSelector } from '@src/hooks' +import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice' import { BOARD_METRICS, CALENDAR, @@ -17,20 +17,20 @@ import { StyledMetricsSection, StyledShowMore, StyledTitleWrapper, -} from '@src/components/Metrics/ReportStep/BoradMetrics/style'; -import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util'; -import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle'; -import { ReportGrid } from '@src/components/Common/ReportGrid'; -import { ReportResponseDTO } from '@src/clients/report/dto/response'; -import { NullAble as Nullable } from '@src/utils/types'; +} from '@src/components/Metrics/ReportStep/BoradMetrics/style' +import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' +import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' +import { ReportGrid } from '@src/components/Common/ReportGrid' +import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { NullAble as Nullable } from '@src/utils/types' interface BoardMetricsProps { - startToRequestBoardData: (request: ReportRequestDTO) => void; - onShowDetail: () => void; - boardReport?: ReportResponseDTO; - csvTimeStamp: number; - startDate: Nullable; - endDate: Nullable; + startToRequestBoardData: (request: ReportRequestDTO) => void + onShowDetail: () => void + boardReport?: ReportResponseDTO + csvTimeStamp: number + startDate: Nullable + endDate: Nullable } const BoardMetrics = ({ @@ -41,19 +41,19 @@ const BoardMetrics = ({ startDate, endDate, }: BoardMetricsProps) => { - const configData = useAppSelector(selectConfig); + const configData = useAppSelector(selectConfig) const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = - useAppSelector(selectMetricsContent); - const jiraColumns = useAppSelector(selectJiraColumns); + useAppSelector(selectMetricsContent) + const jiraColumns = useAppSelector(selectJiraColumns) - const { metrics, calendarType } = configData.basic; - const { board } = configData; - const { token, type, site, projectKey, boardId, email } = board.config; - const jiraToken = getJiraBoardToken(token, email); + const { metrics, calendarType } = configData.basic + const { board } = configData + const { token, type, site, projectKey, boardId, email } = board.config + const jiraToken = getJiraBoardToken(token, email) const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value - ); - const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)); + ) + const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)) const getBoardReportRequestBody = (): BoardReportRequestDTO => ({ metrics: boardMetrics, @@ -74,47 +74,47 @@ const BoardMetrics = ({ doneColumn, }, csvTimeStamp: csvTimeStamp, - }); + }) const getBoardItems = () => { const velocity = boardReport?.velocity const cycleTime = boardReport?.cycleTime const velocityItems = boardMetrics.includes(REQUIRED_DATA.VELOCITY) ? [ - { - title: METRICS_TITLE.VELOCITY, - items: velocity && [ - { - value: velocity.velocityForSP, - subtitle: METRICS_SUBTITLE.VELOCITY, - isToFixed: false, - }, - { - value: velocity.velocityForCards, - subtitle: METRICS_SUBTITLE.THROUGHPUT, - isToFixed: false, - }, - ], - }, - ] + { + title: METRICS_TITLE.VELOCITY, + items: velocity && [ + { + value: velocity.velocityForSP, + subtitle: METRICS_SUBTITLE.VELOCITY, + isToFixed: false, + }, + { + value: velocity.velocityForCards, + subtitle: METRICS_SUBTITLE.THROUGHPUT, + isToFixed: false, + }, + ], + }, + ] : [] const cycleTimeItems = boardMetrics.includes(REQUIRED_DATA.CYCLE_TIME) ? [ - { - title: METRICS_TITLE.CYCLE_TIME, - items: cycleTime && [ - { - value: cycleTime.averageCycleTimePerSP, - subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_SP, - }, - { - value: cycleTime.averageCycleTimePerCard, - subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_CARD, - }, - ], - }, - ] + { + title: METRICS_TITLE.CYCLE_TIME, + items: cycleTime && [ + { + value: cycleTime.averageCycleTimePerSP, + subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_SP, + }, + { + value: cycleTime.averageCycleTimePerCard, + subtitle: METRICS_SUBTITLE.AVERAGE_CYCLE_TIME_PRE_CARD, + }, + ], + }, + ] : [] return [...velocityItems, ...cycleTimeItems] @@ -124,15 +124,12 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) - return ( <> - - {SHOW_MORE} - + {boardReport && {'show more >'}} diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 13291975fb..cd067f8f6e 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -1,27 +1,27 @@ -import React, { useEffect, useLayoutEffect, useState } from 'react'; -import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect'; -import { useAppSelector } from '@src/hooks'; -import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice'; -import { MESSAGE } from '@src/constants/resources'; -import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style'; -import { ErrorNotification } from '@src/components/ErrorNotification'; -import { useNavigate } from 'react-router-dom'; -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; -import { ROUTE } from '@src/constants/router'; -import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup'; -import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics'; -import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics'; -import { selectTimeStamp } from '@src/context/stepper/StepperSlice'; -import DateRangeViewer from '@src/components/Common/DateRangeViewer'; -import { MetricSelectionHeader } from '../MetricsStep/style'; -import ReportDetail from './ReportDetail'; +import React, { useEffect, useLayoutEffect, useState } from 'react' +import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' +import { useAppSelector } from '@src/hooks' +import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' +import { MESSAGE } from '@src/constants/resources' +import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' +import { ErrorNotification } from '@src/components/ErrorNotification' +import { useNavigate } from 'react-router-dom' +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' +import { ROUTE } from '@src/constants/router' +import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' +import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics' +import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' +import { selectTimeStamp } from '@src/context/stepper/StepperSlice' +import DateRangeViewer from '@src/components/Common/DateRangeViewer' +import { MetricSelectionHeader } from '../MetricsStep/style' +import ReportDetail from './ReportDetail' export interface ReportStepProps { - notification: useNotificationLayoutEffectInterface; - handleSave: () => void; + notification: useNotificationLayoutEffectInterface + handleSave: () => void } -type PageType = 'Summary' | 'BoardReport' | 'DoraReport'; +type PageType = 'Summary' | 'BoardReport' | 'DoraReport' const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const navigate = useNavigate() @@ -32,21 +32,21 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startToRequestDoraData, reportData, stopPollingReports, - } = useGenerateReportEffect(); + } = useGenerateReportEffect() - const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined); - const [pageType, setPageType] = useState('Summary'); - const configData = useAppSelector(selectConfig); - const csvTimeStamp = useAppSelector(selectTimeStamp); + const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) + const [pageType, setPageType] = useState('Summary') + const configData = useAppSelector(selectConfig) + const csvTimeStamp = useAppSelector(selectTimeStamp) - const startDate = configData.basic.dateRange.startDate ?? ''; - const endDate = configData.basic.dateRange.endDate ?? ''; + const startDate = configData.basic.dateRange.startDate ?? '' + const endDate = configData.basic.dateRange.endDate ?? '' - const { updateProps } = notification; - const [errorMessage, setErrorMessage] = useState(); + const { updateProps } = notification + const [errorMessage, setErrorMessage] = useState() - const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics); - const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics); + const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics) + const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics) useLayoutEffect(() => { exportValidityTimeMin && @@ -55,7 +55,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { title: MESSAGE.NOTIFICATION_FIRST_REPORT.replace('%s', exportValidityTimeMin.toString()), closeAutomatically: true, }) - }, [exportValidityTimeMin]); + }, [exportValidityTimeMin]) useLayoutEffect(() => { if (exportValidityTimeMin) { @@ -80,45 +80,47 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { clearInterval(timer) } } - }, [exportValidityTimeMin]); + }, [exportValidityTimeMin]) useEffect(() => { setErrorMessage(reportErrorMsg) - }, [reportErrorMsg]); + }, [reportErrorMsg]) useEffect(() => { setExportValidityTimeMin(reportData?.exportValidityTime) - }, [reportData]); + }, [reportData]) useLayoutEffect(() => { return () => { stopPollingReports() } - }, []); - - const showSummary = () => <> - {shouldShowBoardMetrics && ( - setPageType('BoardReport')} - boardReport={reportData} - csvTimeStamp={csvTimeStamp} - /> - )} - {shouldShowDoraMetrics && ( - - )} - ; - const showBoardDetail = () => setPageType('Summary')} /> + }, []) + const showSummary = () => ( + <> + {shouldShowBoardMetrics && ( + setPageType('BoardReport')} + boardReport={reportData} + csvTimeStamp={csvTimeStamp} + /> + )} + {shouldShowDoraMetrics && ( + + )} + + ) + const showBoardDetail = () => setPageType('Summary')} /> + const showDoraDetail = () => setPageType('Summary')} /> return ( <> @@ -136,11 +138,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { )} - { - pageType === 'Summary' ? showSummary() : - pageType === "BoardReport" ? showBoardDetail() : - showBoardDetail() - } + {pageType === 'Summary' ? showSummary() : pageType === 'BoardReport' ? showBoardDetail() : showDoraDetail()} handleSave()} reportData={reportData} @@ -157,4 +155,4 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { ) } -export default ReportStep; +export default ReportStep diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts index cd98507017..7ce993c448 100644 --- a/frontend/src/utils/types.ts +++ b/frontend/src/utils/types.ts @@ -1,2 +1,2 @@ -export type NullAble = T | null; -export type Optional = T | null | undefined; \ No newline at end of file +export type NullAble = T | null +export type Optional = T | null | undefined From fe32242ac471aeb0d5e2e0074fb824f905743ca6 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Fri, 5 Jan 2024 18:04:24 +0800 Subject: [PATCH 22/90] update the showmore --- .../src/components/Metrics/ReportStep/BoradMetrics/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index a6a247bfc2..8de925c1db 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -129,7 +129,7 @@ const BoardMetrics = ({ - {boardReport && {'show more >'}} + {boardReport &&
{'show more >'}
}
From 6f783ecfb73295c2237b85754f10f1721237b8f5 Mon Sep 17 00:00:00 2001 From: xuebing Date: Mon, 8 Jan 2024 10:16:42 +0800 Subject: [PATCH 23/90] ADM-652 [frontend] feat: split detail component --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 2 +- .../Metrics/ReportStep/DoraMetrics/index.tsx | 21 +-- .../Metrics/ReportStep/ReportDetail/board.tsx | 99 ++++++++++++++ .../ReportStep/ReportDetail/doraDetail.tsx | 99 ++++++++++++++ .../Metrics/ReportStep/ReportDetail/index.tsx | 122 +----------------- .../components/Metrics/ReportStep/index.tsx | 5 +- 6 files changed, 217 insertions(+), 131 deletions(-) create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 8de925c1db..a6a247bfc2 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -129,7 +129,7 @@ const BoardMetrics = ({ - {boardReport &&
{'show more >'}
} + {boardReport && {'show more >'}}
diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index 0152a6b045..0de4f37841 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -20,18 +20,25 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' import { StyledSpacing } from '@src/components/Metrics/ReportStep/style' import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util' import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style' -import { ROUTE } from '@src/constants/router' -import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' +import { NullAble as Nullable } from '@src/utils/types' interface DoraMetricsProps { startToRequestDoraData: (request: ReportRequestDTO) => void + onShowDetail: () => void doraReport?: ReportResponseDTO csvTimeStamp: number - startDate: string | null - endDate: string | null + startDate: Nullable + endDate: Nullable } -const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDate, endDate }: DoraMetricsProps) => { +const DoraMetrics = ({ + startToRequestDoraData, + onShowDetail, + doraReport, + csvTimeStamp, + startDate, + endDate, +}: DoraMetricsProps) => { const configData = useAppSelector(selectConfig) const { pipelineTool, sourceControl } = configData const { metrics, calendarType } = configData.basic @@ -174,9 +181,7 @@ const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDa - - {SHOW_MORE} - + {SHOW_MORE} {shouldShowSourceControl && } diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx new file mode 100644 index 0000000000..d5aa83731d --- /dev/null +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -0,0 +1,99 @@ +import { ReportResponse } from '@src/clients/report/dto/response' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' +import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { selectReportData } from '@src/context/report/reportSlice' +import { useAppSelector } from '@src/hooks' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { useEffect, useState } from 'react' + +export const BoardDetail = ({ onBack }: { onBack: () => void }) => { + const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [changeFailureRateState, setChangeFailureRateState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + + const reportData = useAppSelector(selectReportData) + + useEffect(() => { + updateReportData(reportMapper(reportData!)) + }, [reportData]) + + const updateReportData = (res: ReportResponse | undefined) => { + res?.deploymentFrequencyList && + setDeploymentFrequencyState({ + ...deploymentFrequencyState, + value: res.deploymentFrequencyList, + isShow: true, + }) + res?.meanTimeToRecoveryList && + setMeanTimeToRecoveryState({ + ...meanTimeToRecoveryState, + value: res.meanTimeToRecoveryList, + isShow: true, + }) + res?.changeFailureRateList && + setChangeFailureRateState({ + ...changeFailureRateState, + value: res.changeFailureRateList, + isShow: true, + }) + res?.leadTimeForChangesList && + setLeadTimeForChangesState({ + ...leadTimeForChangesState, + value: res.leadTimeForChangesList, + isShow: true, + }) + } + + return ( + <> +
{'< back'}
+ {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + + ) +} diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx new file mode 100644 index 0000000000..988fe6881c --- /dev/null +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx @@ -0,0 +1,99 @@ +import { ReportResponse } from '@src/clients/report/dto/response' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' +import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { selectReportData } from '@src/context/report/reportSlice' +import { useAppSelector } from '@src/hooks' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { useEffect, useState } from 'react' + +export const DoraDetail = ({ onBack }: { onBack: () => void }) => { + const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [changeFailureRateState, setChangeFailureRateState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + + const reportData = useAppSelector(selectReportData) + + useEffect(() => { + updateReportData(reportMapper(reportData!)) + }, [reportData]) + + const updateReportData = (res: ReportResponse | undefined) => { + res?.deploymentFrequencyList && + setDeploymentFrequencyState({ + ...deploymentFrequencyState, + value: res.deploymentFrequencyList, + isShow: true, + }) + res?.meanTimeToRecoveryList && + setMeanTimeToRecoveryState({ + ...meanTimeToRecoveryState, + value: res.meanTimeToRecoveryList, + isShow: true, + }) + res?.changeFailureRateList && + setChangeFailureRateState({ + ...changeFailureRateState, + value: res.changeFailureRateList, + isShow: true, + }) + res?.leadTimeForChangesList && + setLeadTimeForChangesState({ + ...leadTimeForChangesState, + value: res.leadTimeForChangesList, + isShow: true, + }) + } + + return ( + <> +
{'< back'}
+ {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + + ) +} diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index f30d4f1c3c..f62d105453 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,120 +1,2 @@ -import { ReportResponse } from '@src/clients/report/dto/response' -import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' -import { NAME, PIPELINE_STEP } from '@src/constants/resources' -import { selectReportData } from '@src/context/report/reportSlice' -import { useAppSelector } from '@src/hooks' -import { reportMapper } from '@src/hooks/reportMapper/report' -import { useEffect, useState } from 'react' - -const ReportDetail = ({ onBack }: { onBack: () => void }) => { - const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) - const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) - const [classificationState, setClassificationState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [changeFailureRateState, setChangeFailureRateState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - - const reportData = useAppSelector(selectReportData) - - useEffect(() => { - updateReportData(reportMapper(reportData!)) - }, [reportData]) - - const updateReportData = (res: ReportResponse | undefined) => { - res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) - res?.cycleTimeList && setCycleTimeState({ ...cycleTimeState, value: res.cycleTimeList, isShow: true }) - res?.classification && setClassificationState({ value: res.classification, isShow: true }) - res?.deploymentFrequencyList && - setDeploymentFrequencyState({ - ...deploymentFrequencyState, - value: res.deploymentFrequencyList, - isShow: true, - }) - res?.meanTimeToRecoveryList && - setMeanTimeToRecoveryState({ - ...meanTimeToRecoveryState, - value: res.meanTimeToRecoveryList, - isShow: true, - }) - res?.changeFailureRateList && - setChangeFailureRateState({ - ...changeFailureRateState, - value: res.changeFailureRateList, - isShow: true, - }) - res?.leadTimeForChangesList && - setLeadTimeForChangesState({ - ...leadTimeForChangesState, - value: res.leadTimeForChangesList, - isShow: true, - }) - } - - return ( - <> -
{'< back'}
- {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - - )} - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - - ) -} - -export default ReportDetail +export { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail/board' +export { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail/doraDetail' diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index cd067f8f6e..330487c7b9 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -14,7 +14,7 @@ import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import DateRangeViewer from '@src/components/Common/DateRangeViewer' import { MetricSelectionHeader } from '../MetricsStep/style' -import ReportDetail from './ReportDetail' +import { DoraDetail } from './ReportDetail' export interface ReportStepProps { notification: useNotificationLayoutEffectInterface @@ -113,6 +113,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startDate={startDate} endDate={endDate} startToRequestDoraData={startToRequestDoraData} + onShowDetail={() => setPageType('DoraReport')} doraReport={reportData} csvTimeStamp={csvTimeStamp} /> @@ -120,7 +121,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { ) const showBoardDetail = () => setPageType('Summary')} /> - const showDoraDetail = () => setPageType('Summary')} /> + const showDoraDetail = () => setPageType('Summary')} /> return ( <> From 3a089aa944c2266de099125fb4f9c9e022c52d88 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Mon, 8 Jan 2024 12:00:22 +0800 Subject: [PATCH 24/90] [ADM-652][frontend] Separated the detail to two components --- frontend/src/clients/report/dto/response.ts | 23 ++-- .../Metrics/ReportStep/BoradMetrics/index.tsx | 2 +- .../Metrics/ReportStep/DoraMetrics/index.tsx | 2 +- .../Metrics/ReportStep/ReportDetail/board.tsx | 104 +++--------------- .../Metrics/ReportStep/ReportDetail/dora.tsx | 28 +++++ .../ReportStep/ReportDetail/doraDetail.tsx | 99 ----------------- .../Metrics/ReportStep/ReportDetail/index.ts | 3 + .../Metrics/ReportStep/ReportDetail/index.tsx | 2 - .../ReportStep/ReportDetail/withBack.tsx | 13 +++ .../components/Metrics/ReportStep/index.tsx | 9 +- frontend/src/hooks/useGenerateReportEffect.ts | 2 +- frontend/src/utils/types.ts | 2 +- 12 files changed, 81 insertions(+), 208 deletions(-) create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx delete mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts delete mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx diff --git a/frontend/src/clients/report/dto/response.ts b/frontend/src/clients/report/dto/response.ts index c48a930754..85a55496c8 100644 --- a/frontend/src/clients/report/dto/response.ts +++ b/frontend/src/clients/report/dto/response.ts @@ -1,17 +1,18 @@ import { ReportDataWithThreeColumns, ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' +import { Nullable } from '@src/utils/types' export interface ReportResponseDTO { - velocity: VelocityResponse | null - cycleTime: CycleTimeResponse | null - deploymentFrequency: DeploymentFrequencyResponse | null - meanTimeToRecovery: MeanTimeToRecoveryResponse | null - leadTimeForChanges: LeadTimeForChangesResponse | null - changeFailureRate: ChangeFailureRateResponse | null - classificationList: Array | null - exportValidityTime: number | null - isBoardMetricsReady: boolean | null - isPipelineMetricsReady: boolean | null - isSourceControlMetricsReady: boolean | null + velocity: Nullable + cycleTime: Nullable + classificationList: Nullable + deploymentFrequency: Nullable + meanTimeToRecovery: Nullable + leadTimeForChanges: Nullable + changeFailureRate: Nullable + exportValidityTime: Nullable + isBoardMetricsReady: Nullable + isPipelineMetricsReady: Nullable + isSourceControlMetricsReady: Nullable isAllMetricsReady: boolean } diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index a6a247bfc2..182929bb4e 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -22,7 +22,7 @@ import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/uti import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { NullAble as Nullable } from '@src/utils/types' +import { Nullable } from '@src/utils/types' interface BoardMetricsProps { startToRequestBoardData: (request: ReportRequestDTO) => void diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index 0de4f37841..b8dd603ed7 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -20,7 +20,7 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' import { StyledSpacing } from '@src/components/Metrics/ReportStep/style' import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util' import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style' -import { NullAble as Nullable } from '@src/utils/types' +import { Nullable } from '@src/utils/types' interface DoraMetricsProps { startToRequestDoraData: (request: ReportRequestDTO) => void diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index d5aa83731d..f9f50470f5 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -1,99 +1,27 @@ -import { ReportResponse } from '@src/clients/report/dto/response' +import { ReportResponseDTO } from '@src/clients/report/dto/response' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' -import { NAME, PIPELINE_STEP } from '@src/constants/resources' -import { selectReportData } from '@src/context/report/reportSlice' -import { useAppSelector } from '@src/hooks' import { reportMapper } from '@src/hooks/reportMapper/report' -import { useEffect, useState } from 'react' +import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' +import { Optional } from '@src/utils/types' +import { withGoBack } from './withBack' -export const BoardDetail = ({ onBack }: { onBack: () => void }) => { - const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [changeFailureRateState, setChangeFailureRateState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - - const reportData = useAppSelector(selectReportData) +interface Property { + data: ReportResponseDTO + onBack: () => void +} +const showSectionWith2Columns = (title: string, value: Optional) => value && + - useEffect(() => { - updateReportData(reportMapper(reportData!)) - }, [reportData]) +export const BoardDetail = withGoBack(({ data }: Property) => { - const updateReportData = (res: ReportResponse | undefined) => { - res?.deploymentFrequencyList && - setDeploymentFrequencyState({ - ...deploymentFrequencyState, - value: res.deploymentFrequencyList, - isShow: true, - }) - res?.meanTimeToRecoveryList && - setMeanTimeToRecoveryState({ - ...meanTimeToRecoveryState, - value: res.meanTimeToRecoveryList, - isShow: true, - }) - res?.changeFailureRateList && - setChangeFailureRateState({ - ...changeFailureRateState, - value: res.changeFailureRateList, - isShow: true, - }) - res?.leadTimeForChangesList && - setLeadTimeForChangesState({ - ...leadTimeForChangesState, - value: res.leadTimeForChangesList, - isShow: true, - }) - } + const mappedData = reportMapper(data) return ( <> -
{'< back'}
- {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} + {showSectionWith2Columns('Velocity', mappedData.velocityList)} + {showSectionWith2Columns('Cycle time', mappedData.cycleTimeList)} + {mappedData.classification && } ) -} +}) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx new file mode 100644 index 0000000000..d60c411163 --- /dev/null +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx @@ -0,0 +1,28 @@ +import { ReportResponseDTO } from '@src/clients/report/dto/response' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' +import { Optional } from '@src/utils/types' +import { withGoBack } from './withBack' + +interface Property { + data: ReportResponseDTO + onBack: () => void +} +const showSection = (title: string, value: Optional) => value && + + +export const DoraDetail = withGoBack(({ data }: Property) => { + + const mappedData = reportMapper(data) + + return ( + <> + {showSection('Deployment frequency', mappedData.deploymentFrequencyList)} + {showSection('Lead time for changes', mappedData.leadTimeForChangesList)} + {showSection('Change failure rate', mappedData.changeFailureRateList)} + {showSection('Mean Time To Recovery', mappedData.meanTimeToRecoveryList)} + + ) +}) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx deleted file mode 100644 index 988fe6881c..0000000000 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/doraDetail.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { ReportResponse } from '@src/clients/report/dto/response' -import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' -import { NAME, PIPELINE_STEP } from '@src/constants/resources' -import { selectReportData } from '@src/context/report/reportSlice' -import { useAppSelector } from '@src/hooks' -import { reportMapper } from '@src/hooks/reportMapper/report' -import { useEffect, useState } from 'react' - -export const DoraDetail = ({ onBack }: { onBack: () => void }) => { - const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - const [changeFailureRateState, setChangeFailureRateState] = useState({ - value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, - isShow: false, - }) - - const reportData = useAppSelector(selectReportData) - - useEffect(() => { - updateReportData(reportMapper(reportData!)) - }, [reportData]) - - const updateReportData = (res: ReportResponse | undefined) => { - res?.deploymentFrequencyList && - setDeploymentFrequencyState({ - ...deploymentFrequencyState, - value: res.deploymentFrequencyList, - isShow: true, - }) - res?.meanTimeToRecoveryList && - setMeanTimeToRecoveryState({ - ...meanTimeToRecoveryState, - value: res.meanTimeToRecoveryList, - isShow: true, - }) - res?.changeFailureRateList && - setChangeFailureRateState({ - ...changeFailureRateState, - value: res.changeFailureRateList, - isShow: true, - }) - res?.leadTimeForChangesList && - setLeadTimeForChangesState({ - ...leadTimeForChangesState, - value: res.leadTimeForChangesList, - isShow: true, - }) - } - - return ( - <> -
{'< back'}
- {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - - ) -} diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts new file mode 100644 index 0000000000..497f0186bb --- /dev/null +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts @@ -0,0 +1,3 @@ +export * from '@src/components/Metrics/ReportStep/ReportDetail/board' +export * from '@src/components/Metrics/ReportStep/ReportDetail/dora' +export * from '@src/components/Metrics/ReportStep/ReportDetail/style' \ No newline at end of file diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx deleted file mode 100644 index f62d105453..0000000000 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail/board' -export { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail/doraDetail' diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx new file mode 100644 index 0000000000..47c15a7a65 --- /dev/null +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -0,0 +1,13 @@ +import { styled } from '@mui/material/styles' +import { ArrowBack } from '@mui/icons-material' +interface Property { + onBack: () => void +} + +const StyledDiv = styled('div')` +` + +export const withGoBack =

(Child: React.ComponentType

) => (prop: P) => <> + {'Back'} + + \ No newline at end of file diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 330487c7b9..abc32241a0 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -14,7 +14,8 @@ import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import DateRangeViewer from '@src/components/Common/DateRangeViewer' import { MetricSelectionHeader } from '../MetricsStep/style' -import { DoraDetail } from './ReportDetail' +import { BoardDetail, DoraDetail } from './ReportDetail' +import { ReportResponseDTO } from '@src/clients/report/dto/response' export interface ReportStepProps { notification: useNotificationLayoutEffectInterface @@ -120,8 +121,8 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { )} ) - const showBoardDetail = () => setPageType('Summary')} /> - const showDoraDetail = () => setPageType('Summary')} /> + const showBoardDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> + const showDoraDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> return ( <> @@ -139,7 +140,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { )} - {pageType === 'Summary' ? showSummary() : pageType === 'BoardReport' ? showBoardDetail() : showDoraDetail()} + {pageType === 'Summary' ? showSummary() : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} handleSave()} reportData={reportData} diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 21d2ca1ab7..7822697280 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -23,7 +23,7 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const dispatch = useAppDispatch() const [isServerError, setIsServerError] = useState(false) const [errorMessage, setErrorMessage] = useState('') - const [reportData, setReportData] = useState() + const [reportData, setReportData] = useState() const timerIdRef = useRef() let hasPollingStarted = false diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts index 7ce993c448..82d6cfb4b5 100644 --- a/frontend/src/utils/types.ts +++ b/frontend/src/utils/types.ts @@ -1,2 +1,2 @@ -export type NullAble = T | null +export type Nullable = T | null export type Optional = T | null | undefined From 6226f0ac6a067e4a4f196dd0c55924359d389236 Mon Sep 17 00:00:00 2001 From: xuebing Date: Mon, 8 Jan 2024 13:49:14 +0800 Subject: [PATCH 25/90] ADM-652 [frontend] feat: enhance style for show more and back --- .../Common/ReportGrid/ReportTitle/style.tsx | 2 +- .../ReportStep/ReportDetail/withBack.tsx | 26 ++++++++++++++++--- .../components/Metrics/ReportStep/index.tsx | 4 ++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx index 8d397b2e7a..c95dfa8def 100644 --- a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx @@ -8,7 +8,7 @@ export const StyledMetricsTitleSection = styled('div')({ }) export const StyledMetricsSign = styled('canvas')({ - margin: '0.2rem 0.5rem 0 0.5rem', + margin: '0.2rem 0.5rem', height: '1rem', width: '0.3rem', background: theme.main.backgroundColor, diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index 47c15a7a65..ca6f1b40b4 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -5,9 +5,27 @@ interface Property { } const StyledDiv = styled('div')` + display: flex; + align-items: center; + margin-bottom: 1rem; + color: rgba(0, 0, 0, 0.65); + font-size: 1rem; ` -export const withGoBack =

(Child: React.ComponentType

) => (prop: P) => <> - {'Back'} - - \ No newline at end of file +const StyledArrowBack = styled(ArrowBack)` + width: 1.5rem; + margin-right: 0.5rem; +` + +export const withGoBack = +

(Child: React.ComponentType

) => + (prop: P) => + ( + <> + + + {'Back'} + + + + ) diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index abc32241a0..5dc5c90e8d 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -140,7 +140,9 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { )} - {pageType === 'Summary' ? showSummary() : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} + {pageType === 'Summary' + ? showSummary() + : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} handleSave()} reportData={reportData} From 32168ba9c95d2ddce0fecee1f299ad1289d03551 Mon Sep 17 00:00:00 2001 From: xuebing Date: Mon, 8 Jan 2024 13:57:00 +0800 Subject: [PATCH 26/90] ADM-652 [frontend] feat: add handleBack for ReportButtonGroup --- .../components/Metrics/ReportButtonGroup/index.tsx | 11 ++--------- frontend/src/components/Metrics/ReportStep/index.tsx | 9 ++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 15c558c368..6b8b2cb755 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -5,8 +5,6 @@ import SaveAltIcon from '@mui/icons-material/SaveAlt' import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons' import React, { useEffect } from 'react' import { CSVReportRequestDTO } from '@src/clients/report/dto/request' -import { backStep } from '@src/context/stepper/StepperSlice' -import { useAppDispatch } from '@src/hooks/useAppDispatch' import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' import { useAppSelector } from '@src/hooks' import { isSelectBoardMetrics, isSelectDoraMetrics } from '@src/context/config/configSlice' @@ -17,10 +15,10 @@ import { StyledRightButtonGroup, } from '@src/components/Metrics/ReportButtonGroup/style' import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { useNavigate } from 'react-router-dom' interface ReportButtonGroupProps { handleSave?: () => void + handleBack: () => void csvTimeStamp: number startDate: string endDate: string @@ -33,6 +31,7 @@ interface ReportButtonGroupProps { export const ReportButtonGroup = ({ handleSave, + handleBack, isShowDoraMetrics, isShowBoardMetrics, csvTimeStamp, @@ -42,8 +41,6 @@ export const ReportButtonGroup = ({ reportData, isFromDetailPage = false, }: ReportButtonGroupProps) => { - const dispatch = useAppDispatch() - const navigate = useNavigate() const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) @@ -63,10 +60,6 @@ export const ReportButtonGroup = ({ fetchExportData(exportCSV(dataType, startDate, endDate)) } - const handleBack = () => { - isFromDetailPage ? navigate(-1) : dispatch(backStep()) - } - return ( <> diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 5dc5c90e8d..9eb9087da0 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -11,11 +11,12 @@ import { ROUTE } from '@src/constants/router' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics' import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' -import { selectTimeStamp } from '@src/context/stepper/StepperSlice' +import { backStep, selectTimeStamp } from '@src/context/stepper/StepperSlice' import DateRangeViewer from '@src/components/Common/DateRangeViewer' import { MetricSelectionHeader } from '../MetricsStep/style' import { BoardDetail, DoraDetail } from './ReportDetail' import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { useAppDispatch } from '@src/hooks/useAppDispatch' export interface ReportStepProps { notification: useNotificationLayoutEffectInterface @@ -26,6 +27,7 @@ type PageType = 'Summary' | 'BoardReport' | 'DoraReport' const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const navigate = useNavigate() + const dispatch = useAppDispatch() const { isServerError, errorMessage: reportErrorMsg, @@ -124,6 +126,10 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const showBoardDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> const showDoraDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> + const handleBack = () => { + pageType === 'Summary' ? dispatch(backStep()) : setPageType('Summary') + } + return ( <> {isServerError ? ( @@ -144,6 +150,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { ? showSummary() : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} handleBack()} handleSave={() => handleSave()} reportData={reportData} startDate={startDate} From c3727bf16c7e21573e1e8c769e9aed6224220f1d Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Mon, 8 Jan 2024 14:15:38 +0800 Subject: [PATCH 27/90] [ADM-652][frontend] Refine the go back link style --- .../Metrics/ReportStep/ReportDetail/board.tsx | 3 ++- .../Metrics/ReportStep/ReportDetail/style.tsx | 25 ------------------- .../ReportStep/ReportDetail/withBack.tsx | 3 ++- 3 files changed, 4 insertions(+), 27 deletions(-) delete mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index f9f50470f5..f33011e22d 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -21,7 +21,8 @@ export const BoardDetail = withGoBack(({ data }: Property) => { <> {showSectionWith2Columns('Velocity', mappedData.velocityList)} {showSectionWith2Columns('Cycle time', mappedData.cycleTimeList)} - {mappedData.classification && } + {mappedData.classification && } ) }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx deleted file mode 100644 index 320cb82993..0000000000 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { styled } from '@mui/material/styles' -import { Z_INDEX } from '@src/constants/commons' - -export const ErrorNotificationContainer = styled('div')({ - position: 'fixed', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - zIndex: Z_INDEX.MODAL_BACKDROP, - width: '80%', -}) - -export const StyledTableWrapper = styled('div')({ - width: '100%', - margin: '2rem 0', -}) - -export const StyledNavigator = styled('div')({ - margin: '1rem 0', -}) - -export const StyledContainer = styled('div')({ - width: '80%', - margin: '0 auto', -}) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index ca6f1b40b4..f496025259 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -8,7 +8,8 @@ const StyledDiv = styled('div')` display: flex; align-items: center; margin-bottom: 1rem; - color: rgba(0, 0, 0, 0.65); + color: #595959; + cursor: pointer; font-size: 1rem; ` From 613cba99bf3ff5e7633c6ed5ba048d44d832fc72 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Mon, 8 Jan 2024 16:33:23 +0800 Subject: [PATCH 28/90] [ADM-652][frontend] Add unit testing for report detail --- frontend/.eslintrc.json | 1 + .../ReportStep/ReportDetail/board.test.tsx | 117 ++++++++++++++++++ .../ReportStep/ReportDetail/dora.test.tsx | 98 +++++++++++++++ .../ReportStep/ReportDetail/withBack.test.tsx | 27 ++++ .../Common/ReportForThreeColumns/index.tsx | 2 +- .../Common/ReportForTwoColumns/index.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/style.tsx | 2 +- .../Metrics/ReportStep/DoraMetrics/style.tsx | 2 +- .../Metrics/ReportStep/ReportDetail/board.tsx | 17 ++- .../Metrics/ReportStep/ReportDetail/dora.tsx | 6 +- .../Metrics/ReportStep/ReportDetail/index.ts | 1 - .../ReportStep/ReportDetail/withBack.tsx | 1 + frontend/src/theme.ts | 1 - 13 files changed, 263 insertions(+), 14 deletions(-) create mode 100644 frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx create mode 100644 frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx create mode 100644 frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index 8193acc2ee..c5b7d3c592 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -25,6 +25,7 @@ "semi": 0, "react/react-in-jsx-scope": "off", "import/prefer-default-export": "off", + "react/display-name": "off", "react/jsx-filename-extension": [1, { "extensions": [".jsx", ".tsx"] }] }, "settings": { diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx new file mode 100644 index 0000000000..24558a7531 --- /dev/null +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -0,0 +1,117 @@ +import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { render } from '@testing-library/react' +import { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail' +import { reportMapper } from '@src/hooks/reportMapper/report' + +jest.mock('@src/hooks/reportMapper/report') + +describe('board', () => { + const data: ReportResponseDTO = {} as ReportResponseDTO + + afterEach(jest.clearAllMocks) + + it('should render a back link', () => { + ;(reportMapper as jest.Mock).mockReturnValue({}) + const { getByText, getByTestId } = render() + expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() + expect(getByText('Back')).toBeInTheDocument() + }) + + describe('Velocity', () => { + it('should show velocity when velocity data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + velocityList: [ + { id: 0, name: 'name1', valueList: [{ value: 1 }] }, + { id: 1, name: 'name2', valueList: [{ value: 2 }] }, + ], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Velocity')).toBeInTheDocument() + expect(getByTestId('Velocity')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(2) + }) + + it('should not show velocity when velocity data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + velocityList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Velocity').length).toEqual(0) + }) + }) + + describe('Cycle time', () => { + it('should show cycle time when cycle time data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + cycleTimeList: [ + { id: 0, name: 'name1', valueList: [{ value: 1 }] }, + { id: 1, name: 'name2', valueList: [{ value: 2 }] }, + { id: 2, name: 'name3', valueList: [{ value: 3 }] }, + ], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Cycle time')).toBeInTheDocument() + expect(getByTestId('Cycle time')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(3) + }) + + it('should not show cycle time when cycle time data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + cycleTimeList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Cycle time').length).toEqual(0) + }) + }) + + describe('Classification', () => { + it('should show classifications when classifications data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + classification: [ + { id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }, + { id: 1, name: 'name2', valuesList: [{ name: 'test2', value: 2 }] }, + { id: 2, name: 'name3', valuesList: [{ name: 'test3', value: 3 }] }, + { id: 3, name: 'name4', valuesList: [{ name: 'test4', value: 4 }] }, + ], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Classifications')).toBeInTheDocument() + expect(getByTestId('Classifications')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(8) + }) + + it('should not show classifications when classifications data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + velocityList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Classifications').length).toEqual(0) + }) + }) + + it('should show all data when all data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + velocityList: [{ id: 0, name: 'name1', valueList: [{ value: 1 }] }], + cycleTimeList: [ + { id: 0, name: 'name1', valueList: [{ value: 1 }] }, + { id: 1, name: 'name2', valueList: [{ value: 2 }] }, + ], + classification: [ + { id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }, + { id: 1, name: 'name2', valuesList: [{ name: 'test2', value: 2 }] }, + { id: 2, name: 'name3', valuesList: [{ name: 'test3', value: 3 }] }, + ], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Velocity')).toBeInTheDocument() + expect(getByTestId('Velocity')).toBeInTheDocument() + expect(getByText('Cycle time')).toBeInTheDocument() + expect(getByTestId('Cycle time')).toBeInTheDocument() + expect(getByText('Classifications')).toBeInTheDocument() + expect(getByTestId('Classifications')).toBeInTheDocument() + + expect(container.querySelectorAll('table[data-test-id="Velocity"] > tbody > tr').length).toBe(1) + expect(container.querySelectorAll('table[data-test-id="Cycle time"] > tbody > tr').length).toBe(2) + expect(container.querySelectorAll('table[data-test-id="Classifications"] > tbody > tr').length).toBe(6) + }) +}) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx new file mode 100644 index 0000000000..725cdd0edb --- /dev/null +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx @@ -0,0 +1,98 @@ +import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { render } from '@testing-library/react' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail' + +jest.mock('@src/hooks/reportMapper/report') +describe('DoraDetail', () => { + const data: ReportResponseDTO = {} as ReportResponseDTO + + afterEach(jest.clearAllMocks) + + it('should render a back link', () => { + ;(reportMapper as jest.Mock).mockReturnValue({}) + const { getByText, getByTestId } = render() + expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() + expect(getByText('Back')).toBeInTheDocument() + }) + + describe('Deployment frequency', () => { + it('should show deploymentFrequencyList when deploymentFrequencyList data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + deploymentFrequencyList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Deployment frequency')).toBeInTheDocument() + expect(getByTestId('Deployment frequency')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(2) + }) + + it('should not show deploymentFrequencyList when deploymentFrequencyList data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + deploymentFrequencyList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Deployment frequency').length).toEqual(0) + }) + }) + + describe('Lead time for changes', () => { + it('should show leadTimeForChangesList when leadTimeForChangesList data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + leadTimeForChangesList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Lead time for changes')).toBeInTheDocument() + expect(getByTestId('Lead time for changes')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(2) + }) + + it('should not show leadTimeForChangesList when leadTimeForChangesList data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + leadTimeForChangesList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Lead time for changes').length).toEqual(0) + }) + }) + + describe('Change failure rate', () => { + it('should show changeFailureRateList when changeFailureRateList data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + changeFailureRateList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Change failure rate')).toBeInTheDocument() + expect(getByTestId('Change failure rate')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(2) + }) + + it('should not show changeFailureRateList when changeFailureRateList data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + changeFailureRateList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Change failure rate').length).toEqual(0) + }) + }) + + describe('Mean Time To Recovery', () => { + it('should show meanTimeToRecoveryList when meanTimeToRecoveryList data is existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + meanTimeToRecoveryList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], + }) + const { getByText, getByTestId, container } = render() + expect(getByText('Mean Time To Recovery')).toBeInTheDocument() + expect(getByTestId('Mean Time To Recovery')).toBeInTheDocument() + expect(container.querySelectorAll('tbody > tr').length).toBe(2) + }) + + it('should not show meanTimeToRecoveryList when meanTimeToRecoveryList data is not existing', () => { + ;(reportMapper as jest.Mock).mockReturnValue({ + meanTimeToRecoveryList: null, + }) + const { queryAllByText } = render() + expect(queryAllByText('Mean Time To Recovery').length).toEqual(0) + }) + }) +}) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx new file mode 100644 index 0000000000..eaa8af291f --- /dev/null +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx @@ -0,0 +1,27 @@ +import { withGoBack } from '@src/components/Metrics/ReportStep/ReportDetail/withBack' +import { render, fireEvent } from '@testing-library/react' + +describe('withGoBack', () => { + const onBack = jest.fn() + + afterEach(jest.clearAllMocks) + + it('should render a link with back', () => { + const Component = withGoBack(() =>

test1
) + const { getByText } = render() + expect(getByText('Back')).toBeInTheDocument() + }) + + it('should render the icon', () => { + const Component = withGoBack(() =>
test2
) + const { getByTestId } = render() + expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() + }) + + it('should call onBack when the back is clicked', () => { + const Component = withGoBack(() =>
test3
) + const { getByText } = render() + fireEvent.click(getByText('Back')) + expect(onBack).toBeCalled() + }) +}) diff --git a/frontend/src/components/Common/ReportForThreeColumns/index.tsx b/frontend/src/components/Common/ReportForThreeColumns/index.tsx index 17c02c924d..b42c88a9db 100644 --- a/frontend/src/components/Common/ReportForThreeColumns/index.tsx +++ b/frontend/src/components/Common/ReportForThreeColumns/index.tsx @@ -58,7 +58,7 @@ export const ReportForThreeColumns = ({ title, fieldName, listName, data }: Repo <> {title} - +
{fieldName} diff --git a/frontend/src/components/Common/ReportForTwoColumns/index.tsx b/frontend/src/components/Common/ReportForTwoColumns/index.tsx index 9bbc207f4d..1bd4893750 100644 --- a/frontend/src/components/Common/ReportForTwoColumns/index.tsx +++ b/frontend/src/components/Common/ReportForTwoColumns/index.tsx @@ -38,7 +38,7 @@ export const ReportForTwoColumns = ({ title, data }: ReportForTwoColumnsProps) = <> {title} -
+
Name diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index 015987f1c7..2484ef4c76 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -16,5 +16,5 @@ export const StyledShowMore = styled(Link)({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', - color: theme.palette.link, + color: '#4350AF', }) diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index d400534e26..5aa0f54cd1 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -16,5 +16,5 @@ export const StyledShowMore = styled(Link)({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', - color: theme.palette.link, + color: '#4350AF', }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index f33011e22d..2393403074 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -1,3 +1,4 @@ +import React from 'react' import { ReportResponseDTO } from '@src/clients/report/dto/response' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' @@ -10,19 +11,25 @@ interface Property { data: ReportResponseDTO onBack: () => void } -const showSectionWith2Columns = (title: string, value: Optional) => value && - -export const BoardDetail = withGoBack(({ data }: Property) => { +const showSectionWith2Columns = (title: string, value: Optional) => + value && +export const BoardDetail = withGoBack(({ data }: Property) => { const mappedData = reportMapper(data) return ( <> {showSectionWith2Columns('Velocity', mappedData.velocityList)} {showSectionWith2Columns('Cycle time', mappedData.cycleTimeList)} - {mappedData.classification && } + {mappedData.classification && ( + + )} ) }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx index d60c411163..0d274630ec 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx @@ -1,3 +1,4 @@ +import React from 'react' import { ReportResponseDTO } from '@src/clients/report/dto/response' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { NAME, PIPELINE_STEP } from '@src/constants/resources' @@ -10,11 +11,10 @@ interface Property { data: ReportResponseDTO onBack: () => void } -const showSection = (title: string, value: Optional) => value && - +const showSection = (title: string, value: Optional) => + value && export const DoraDetail = withGoBack(({ data }: Property) => { - const mappedData = reportMapper(data) return ( diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts index 497f0186bb..c89a903db8 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts @@ -1,3 +1,2 @@ export * from '@src/components/Metrics/ReportStep/ReportDetail/board' export * from '@src/components/Metrics/ReportStep/ReportDetail/dora' -export * from '@src/components/Metrics/ReportStep/ReportDetail/style' \ No newline at end of file diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index f496025259..fe4c98a8e2 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -1,3 +1,4 @@ +import React from 'react' import { styled } from '@mui/material/styles' import { ArrowBack } from '@mui/icons-material' interface Property { diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index 25c60945b0..dc18bbbd9c 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -77,7 +77,6 @@ export const theme = createTheme({ main: '#3498db', light: '#b9c4cc', }, - link: '#4350AF', }, main: { backgroundColor: indigo[FIVE_HUNDRED], From a64609624f7d1394af30f5eb5f08b2e52f0d8ab8 Mon Sep 17 00:00:00 2001 From: xuebing Date: Mon, 8 Jan 2024 18:16:55 +0800 Subject: [PATCH 29/90] ADM-652 [frontend] feat: delete unused code --- .../Metrics/ReportButtonGroup/index.tsx | 20 +++++-------- .../Metrics/ReportButtonGroup/style.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/style.tsx | 4 +-- .../Metrics/ReportStep/DoraMetrics/style.tsx | 4 +-- frontend/src/context/report/reportSlice.ts | 30 ------------------- frontend/src/hooks/useGenerateReportEffect.ts | 7 +---- frontend/src/store.ts | 2 -- 7 files changed, 11 insertions(+), 58 deletions(-) delete mode 100644 frontend/src/context/report/reportSlice.ts diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 6b8b2cb755..7130ded126 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -24,22 +24,16 @@ interface ReportButtonGroupProps { endDate: string setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined - isFromDetailPage?: boolean - isShowDoraMetrics?: boolean - isShowBoardMetrics?: boolean } export const ReportButtonGroup = ({ handleSave, handleBack, - isShowDoraMetrics, - isShowBoardMetrics, csvTimeStamp, startDate, endDate, setErrorMessage, reportData, - isFromDetailPage = false, }: ReportButtonGroupProps) => { const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) @@ -62,27 +56,27 @@ export const ReportButtonGroup = ({ return ( <> - - {!isFromDetailPage && ( + + { }> {COMMON_BUTTONS.SAVE} - )} + } {COMMON_BUTTONS.BACK} - {!isFromDetailPage && ( + { handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} > {COMMON_BUTTONS.EXPORT_METRIC_DATA} - )} - {(isFromDetailPage ? isShowBoardMetrics : isShowExportBoardButton) && ( + } + {isShowExportBoardButton && ( handleDownload(DOWNLOAD_TYPES.BOARD, startDate, endDate)} @@ -90,7 +84,7 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.EXPORT_BOARD_DATA} )} - {(isFromDetailPage ? isShowDoraMetrics : isShowExportPipelineButton) && ( + {isShowExportPipelineButton && ( (props.isFromDetailPage ? 'flex-end' : 'space-between')}; + justify-content: space-between; width: 100%; padding-top: 2rem; ` diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index 2484ef4c76..3be35e185d 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,6 +1,4 @@ import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { Link } from 'react-router-dom' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', @@ -12,7 +10,7 @@ export const StyledTitleWrapper = styled('div')({ marginBottom: '1rem', }) -export const StyledShowMore = styled(Link)({ +export const StyledShowMore = styled('div')({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index 5aa0f54cd1..3be35e185d 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -1,6 +1,4 @@ import { styled } from '@mui/material/styles' -import { Link } from 'react-router-dom' -import { theme } from '@src/theme' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', @@ -12,7 +10,7 @@ export const StyledTitleWrapper = styled('div')({ marginBottom: '1rem', }) -export const StyledShowMore = styled(Link)({ +export const StyledShowMore = styled('div')({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', diff --git a/frontend/src/context/report/reportSlice.ts b/frontend/src/context/report/reportSlice.ts deleted file mode 100644 index 5152163c24..0000000000 --- a/frontend/src/context/report/reportSlice.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' -import type { RootState } from '@src/store' -import { ReportResponseDTO } from '@src/clients/report/dto/response' - -export interface ReportState { - reportData: ReportResponseDTO | null -} - -export const initialBoardState: ReportState = { - reportData: null, -} - -export const reportSlice = createSlice({ - name: 'report', - initialState: { - ...initialBoardState, - }, - reducers: { - updateReportData: (state, action) => { - state.reportData = action.payload - }, - }, -}) -export const { updateReportData } = reportSlice.actions - -export const selectReportData = (state: RootState) => { - return state.report.reportData -} - -export default reportSlice.reducer diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 7822697280..f117446e23 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -6,8 +6,6 @@ import { InternalServerException } from '@src/exceptions/InternalServerException import { ReportResponseDTO } from '@src/clients/report/dto/response' import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons' import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { updateReportData } from '@src/context/report/reportSlice' export interface useGenerateReportEffectInterface { startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void @@ -20,7 +18,6 @@ export interface useGenerateReportEffectInterface { export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const reportPath = '/reports' - const dispatch = useAppDispatch() const [isServerError, setIsServerError] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [reportData, setReportData] = useState() @@ -91,9 +88,7 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const handleAndUpdateData = (response: ReportResponseDTO) => { const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime) - const report = { ...response, exportValidityTime: exportValidityTime } - setReportData(report) - dispatch(updateReportData(report)) + setReportData({ ...response, exportValidityTime: exportValidityTime }) } return { diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 6296065eba..385fba18a7 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -3,7 +3,6 @@ import stepperReducer from './context/stepper/StepperSlice' import configReducer from './context/config/configSlice' import metricsSlice from './context/Metrics/metricsSlice' import headerSlice from '@src/context/header/headerSlice' -import reportSlice from '@src/context/report/reportSlice' export const store = configureStore({ reducer: { @@ -11,7 +10,6 @@ export const store = configureStore({ config: configReducer, metrics: metricsSlice, header: headerSlice, - report: reportSlice, }, }) From 6e945d1af554a1173a63410747061181ce2c28dc Mon Sep 17 00:00:00 2001 From: xuebing Date: Mon, 8 Jan 2024 18:35:26 +0800 Subject: [PATCH 30/90] ADM-652 [frontend] feat: add showMore button visible logic --- .../src/components/Metrics/ReportStep/BoradMetrics/index.tsx | 2 +- .../src/components/Metrics/ReportStep/DoraMetrics/index.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 182929bb4e..9a6da1433b 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -129,7 +129,7 @@ const BoardMetrics = ({ - {boardReport && {'show more >'}} + {boardReport?.isBoardMetricsReady && {'show more >'}} diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index b8dd603ed7..a600fbe165 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -181,7 +181,9 @@ const DoraMetrics = ({ - {SHOW_MORE} + {(doraReport?.isPipelineMetricsReady || doraReport?.isSourceControlMetricsReady) && ( + {SHOW_MORE} + )} {shouldShowSourceControl && } From e1d9b14f455a653c8629fd3de3bf6f2daefb3df6 Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 10:17:11 +0800 Subject: [PATCH 31/90] ADM-652 [frontend] feat: enhance style for calender --- frontend/src/components/Metrics/MetricsStep/style.tsx | 3 ++- .../components/Metrics/ReportStep/ReportDetail/withBack.tsx | 2 +- frontend/src/components/Metrics/ReportStep/index.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Metrics/MetricsStep/style.tsx b/frontend/src/components/Metrics/MetricsStep/style.tsx index 4fe4de8441..aa2ed34fde 100644 --- a/frontend/src/components/Metrics/MetricsStep/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/style.tsx @@ -4,7 +4,8 @@ import { Divider } from '@src/components/Common/MetricsSettingTitle/style' export const MetricSelectionHeader = styled('div')({ display: 'flex', justifyContent: 'flex-end', - marginBottom: '1rem', + position: 'relative', + top: '1.6rem', }) export const MetricSelectionWrapper = styled('div')({ diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index fe4c98a8e2..83e28dd63d 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -8,7 +8,7 @@ interface Property { const StyledDiv = styled('div')` display: flex; align-items: center; - margin-bottom: 1rem; + margin-bottom: 2.5rem; color: #595959; cursor: pointer; font-size: 1rem; diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 9eb9087da0..9ce115f440 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -23,7 +23,7 @@ export interface ReportStepProps { handleSave: () => void } -type PageType = 'Summary' | 'BoardReport' | 'DoraReport' +export type PageType = 'Summary' | 'BoardReport' | 'DoraReport' const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const navigate = useNavigate() From f46b295a27d75180a53d43e93db57fd00c2e3ebc Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 10:40:10 +0800 Subject: [PATCH 32/90] ADM-652 [frontend] feat: add props for button group --- .../Metrics/ReportButtonGroup/index.tsx | 16 +++++++++------- .../Metrics/ReportButtonGroup/style.tsx | 2 +- .../Metrics/ReportStep/ReportDetail/withBack.tsx | 2 ++ .../src/components/Metrics/ReportStep/index.tsx | 3 +++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 7130ded126..29c4119484 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -6,8 +6,6 @@ import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons' import React, { useEffect } from 'react' import { CSVReportRequestDTO } from '@src/clients/report/dto/request' import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -import { useAppSelector } from '@src/hooks' -import { isSelectBoardMetrics, isSelectDoraMetrics } from '@src/context/config/configSlice' import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' import { StyledButtonGroup, @@ -24,6 +22,9 @@ interface ReportButtonGroupProps { endDate: string setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined + isShowSave: boolean + isShowExportBoardButton: boolean + isShowExportPipelineButton: boolean } export const ReportButtonGroup = ({ @@ -34,10 +35,11 @@ export const ReportButtonGroup = ({ endDate, setErrorMessage, reportData, + isShowSave, + isShowExportBoardButton, + isShowExportPipelineButton, }: ReportButtonGroupProps) => { const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() - const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) - const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) useEffect(() => { setErrorMessage(errorMessage) @@ -56,14 +58,14 @@ export const ReportButtonGroup = ({ return ( <> - - { + + {isShowSave && ( }> {COMMON_BUTTONS.SAVE} - } + )} {COMMON_BUTTONS.BACK} diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index 5a6ea27248..2e8da87ad8 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -18,7 +18,7 @@ export const StyledButtonGroup = styled('div')` align-items: center; text-align: center; margin: 0 auto; - justify-content: space-between; + justify-content: ${(props) => (props.isShowSave ? 'space-between' : 'flex-end')}; width: 100%; padding-top: 2rem; ` diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index 83e28dd63d..8e703a96df 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -8,6 +8,8 @@ interface Property { const StyledDiv = styled('div')` display: flex; align-items: center; + width: max-content; + z-index: 2; margin-bottom: 2.5rem; color: #595959; cursor: pointer; diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 9ce115f440..4c8d06f86c 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -150,6 +150,9 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { ? showSummary() : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} handleBack()} handleSave={() => handleSave()} reportData={reportData} From 291dbf3665cccb3bea46769e00e95d41a93c7dce Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 11:06:55 +0800 Subject: [PATCH 33/90] ADM-652 [frontend] feat: add isBackFromDetail state --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 4 +++- .../Metrics/ReportStep/DoraMetrics/index.tsx | 4 +++- .../src/components/Metrics/ReportStep/index.tsx | 14 +++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 9a6da1433b..b77de0fc70 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -31,9 +31,11 @@ interface BoardMetricsProps { csvTimeStamp: number startDate: Nullable endDate: Nullable + isBackFromDetail: boolean } const BoardMetrics = ({ + isBackFromDetail, startToRequestBoardData, onShowDetail, boardReport, @@ -121,7 +123,7 @@ const BoardMetrics = ({ } useEffect(() => { - startToRequestBoardData(getBoardReportRequestBody()) + !isBackFromDetail && startToRequestBoardData(getBoardReportRequestBody()) }, []) return ( diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index a600fbe165..2ca6b38673 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -29,9 +29,11 @@ interface DoraMetricsProps { csvTimeStamp: number startDate: Nullable endDate: Nullable + isBackFromDetail: boolean } const DoraMetrics = ({ + isBackFromDetail, startToRequestDoraData, onShowDetail, doraReport, @@ -173,7 +175,7 @@ const DoraMetrics = ({ } useEffect(() => { - startToRequestDoraData(getDoraReportRequestBody()) + !isBackFromDetail && startToRequestDoraData(getDoraReportRequestBody()) }, []) return ( diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 4c8d06f86c..3fb0c81d74 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -39,6 +39,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) const [pageType, setPageType] = useState('Summary') + const [isBackFromDetail, setIsBackFromDetail] = useState(false) const configData = useAppSelector(selectConfig) const csvTimeStamp = useAppSelector(selectTimeStamp) @@ -103,6 +104,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { <> {shouldShowBoardMetrics && ( { )} {shouldShowDoraMetrics && ( { )} ) - const showBoardDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> - const showDoraDetail = (data: ReportResponseDTO) => setPageType('Summary')} data={data} /> + const showBoardDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} /> + const showDoraDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} /> const handleBack = () => { - pageType === 'Summary' ? dispatch(backStep()) : setPageType('Summary') + pageType === 'Summary' ? dispatch(backStep()) : backToSummaryPage() + } + + const backToSummaryPage = () => { + setPageType('Summary') + setIsBackFromDetail(true) } return ( From c71b80661ed9a5bb93153c40900e269c50a725ef Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 14:00:18 +0800 Subject: [PATCH 34/90] ADM-652 [frontend] feat: fix isShowExportPipelineButton logic --- frontend/src/components/Metrics/ReportStep/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 3fb0c81d74..4c7c306e48 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -160,7 +160,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { handleBack()} handleSave={() => handleSave()} reportData={reportData} From 44155da583033646caf8ff11c89a8c7dc11b42b3 Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 14:01:50 +0800 Subject: [PATCH 35/90] ADM-652 [frontend] feat: add props for StyledButtonGroup --- frontend/src/components/Metrics/ReportButtonGroup/style.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index 2e8da87ad8..76e8dfc80d 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -18,7 +18,7 @@ export const StyledButtonGroup = styled('div')` align-items: center; text-align: center; margin: 0 auto; - justify-content: ${(props) => (props.isShowSave ? 'space-between' : 'flex-end')}; + justify-content: ${(props: { isShowSave: boolean }) => (props.isShowSave ? 'space-between' : 'flex-end')}; width: 100%; padding-top: 2rem; ` From 1be73b61398b788794a4c78a21944d037da3481f Mon Sep 17 00:00:00 2001 From: JiangRu1 <3246736839@qq.com> Date: Tue, 9 Jan 2024 14:22:48 +0800 Subject: [PATCH 36/90] ADM-652: [frontend] test: fix tests --- .../components/Metrics/ReportStep/ReportStep.test.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index 2dd1e73ac8..25c9ac66fb 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -28,7 +28,6 @@ import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEff import React from 'react' import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' import { MESSAGE } from '@src/constants/resources' -import { formatMillisecondsToHours } from '@src/utils/util' jest.mock('@src/context/stepper/StepperSlice', () => ({ ...jest.requireActual('@src/context/stepper/StepperSlice'), @@ -82,7 +81,7 @@ describe('Report Step', () => { reportHook.current.stopPollingReports = jest.fn() reportHook.current.isServerError = false reportHook.current.errorMessage = '' - reportHook.current.reportData = MOCK_REPORT_RESPONSE + reportHook.current.reportData = { ...MOCK_REPORT_RESPONSE, exportValidityTime: 30 } } const handleSaveMock = jest.fn() const setup = (params: string[]) => { @@ -228,13 +227,6 @@ describe('Report Step', () => { }) it('should call resetProps and updateProps when remaining time is less than or equal to 5 minutes', () => { - const initExportValidityTimeMin = 30 - React.useState = jest.fn().mockReturnValue([ - initExportValidityTimeMin, - () => { - jest.fn() - }, - ]) const resetProps = jest.fn() const updateProps = jest.fn() notificationHook.current.resetProps = resetProps From 881b5a7a8e5042e17cbf67a7b512ecba07b9880c Mon Sep 17 00:00:00 2001 From: JiangRu1 <3246736839@qq.com> Date: Tue, 9 Jan 2024 15:04:13 +0800 Subject: [PATCH 37/90] ADM-652: [frontend] test: add show more button tests --- .../MetricsStepper/MetricsStepper.test.tsx | 14 ++-- .../Metrics/ReportStep/ReportStep.test.tsx | 69 ++++++++++++++++++- frontend/__tests__/src/fixtures.ts | 6 +- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx index fe97f358da..f218864ce3 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx @@ -3,13 +3,13 @@ import MetricsStepper from '@src/components/Metrics/MetricsStepper' import { Provider } from 'react-redux' import { setupStore } from '../../../utils/setupStoreUtil' import { - BACK, + BASE_PAGE_ROUTE, BOARD_TYPES, CONFIRM_DIALOG_DESCRIPTION, - BASE_PAGE_ROUTE, MOCK_REPORT_URL, NEXT, PIPELINE_TOOL_TYPES, + PREVIOUS, PROJECT_NAME_LABEL, SAVE, SOURCE_CONTROL_TYPES, @@ -169,13 +169,13 @@ describe('MetricsStepper', () => { }) expect(getByText(NEXT)).toBeInTheDocument() - expect(getByText(BACK)).toBeInTheDocument() + expect(getByText(PREVIOUS)).toBeInTheDocument() }) it('should show metrics config step when click back button given config step ', async () => { const { getByText } = setup() - await userEvent.click(getByText(BACK)) + await userEvent.click(getByText(PREVIOUS)) expect(getByText(PROJECT_NAME_LABEL)).toBeInTheDocument() }) @@ -183,7 +183,7 @@ describe('MetricsStepper', () => { it('should show confirm dialog when click back button in config page', async () => { const { getByText } = setup() - await userEvent.click(getByText(BACK)) + await userEvent.click(getByText(PREVIOUS)) expect(getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument() }) @@ -191,7 +191,7 @@ describe('MetricsStepper', () => { it('should close confirm dialog when click cancel button', async () => { const { getByText, queryByText } = setup() - await userEvent.click(getByText(BACK)) + await userEvent.click(getByText(PREVIOUS)) await userEvent.click(getByText(CANCEL)) expect(queryByText(CONFIRM_DIALOG_DESCRIPTION)).not.toBeInTheDocument() @@ -200,7 +200,7 @@ describe('MetricsStepper', () => { it('should go to home page when click Yes button', async () => { const { getByText } = setup() - await userEvent.click(getByText(BACK)) + await userEvent.click(getByText(PREVIOUS)) expect(getByText(YES)).toBeVisible() diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index 25c9ac66fb..ada3c3a163 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -1,4 +1,4 @@ -import { render, renderHook } from '@testing-library/react' +import { act, render, renderHook, waitFor, waitForElementToBeRemoved } from '@testing-library/react' import ReportStep from '@src/components/Metrics/ReportStep' import { BACK, @@ -9,8 +9,10 @@ import { EXPORT_PIPELINE_DATA, MOCK_JIRA_VERIFY_RESPONSE, MOCK_REPORT_RESPONSE, + PREVIOUS, REQUIRED_DATA_LIST, SAVE, + SHOW_MORE, } from '../../../fixtures' import { setupStore } from '../../../utils/setupStoreUtil' import { Provider } from 'react-redux' @@ -202,7 +204,7 @@ describe('Report Step', () => { it('should call handleBack method when clicking back button given back button enabled', async () => { const { getByText } = setup(['']) - const back = getByText(BACK) + const back = getByText(PREVIOUS) await userEvent.click(back) expect(backStep).toHaveBeenCalledTimes(1) @@ -260,6 +262,69 @@ describe('Report Step', () => { jest.useRealTimers() }) + + it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( + 'should render detail page when clicking show more button given metric %s', + async (requiredData) => { + const { getByText } = setup([requiredData]) + + const showMore = getByText(SHOW_MORE) + + act(() => { + userEvent.click(showMore) + }) + + await waitForElementToBeRemoved(showMore) + expect(getByText(BACK)).toBeInTheDocument() + } + ) + + it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( + 'should return report page when clicking back button in Breadcrumb in detail page given metric %s', + async (requiredData) => { + const { getByText } = setup([requiredData]) + + const showMore = getByText(SHOW_MORE) + + act(() => { + userEvent.click(showMore) + }) + + await waitForElementToBeRemoved(showMore) + const back = getByText(BACK) + + act(() => { + userEvent.click(back) + }) + + await waitForElementToBeRemoved(back) + expect(getByText(SHOW_MORE)).toBeInTheDocument() + } + ) + + it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( + 'should return report page when clicking previous button in detail page given metric %s', + async (requiredData) => { + const { getByText } = setup([requiredData]) + + const showMore = getByText(SHOW_MORE) + + act(() => { + userEvent.click(showMore) + }) + + await waitForElementToBeRemoved(showMore) + const previous = getByText(PREVIOUS) + + act(() => { + userEvent.click(previous) + }) + + await waitFor(() => { + expect(getByText(SHOW_MORE)).toBeInTheDocument() + }) + } + ) }) describe('export pipeline data', () => { diff --git a/frontend/__tests__/src/fixtures.ts b/frontend/__tests__/src/fixtures.ts index 11e6e6a3e0..f9c4df1083 100644 --- a/frontend/__tests__/src/fixtures.ts +++ b/frontend/__tests__/src/fixtures.ts @@ -13,10 +13,14 @@ export const CHINA_CALENDAR = 'Calendar with Chinese Holiday' export const NEXT = 'Next' -export const BACK = 'Previous' +export const PREVIOUS = 'Previous' export const SAVE = 'Save' +export const SHOW_MORE = 'show more >' + +export const BACK = 'Back' + export const VERIFY = 'Verify' export const RESET = 'Reset' From 155a23ae920a6341eeaaa35e2a827e66f48f4863 Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 14:40:10 +0800 Subject: [PATCH 38/90] ADM-652 [frontend] test: add e2e test --- .../ReportStep/ReportDetail/board.test.tsx | 2 +- frontend/cypress.config.ts | 2 +- frontend/cypress/e2e/createANewProject.cy.ts | 35 +++++++++++++++---- frontend/cypress/pages/metrics/report.ts | 28 +++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx index 24558a7531..e52a35fa3e 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -82,7 +82,7 @@ describe('board', () => { it('should not show classifications when classifications data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ - velocityList: null, + classification: null, }) const { queryAllByText } = render() expect(queryAllByText('Classifications').length).toEqual(0) diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts index 896ea4948c..0f9234ebe1 100644 --- a/frontend/cypress.config.ts +++ b/frontend/cypress.config.ts @@ -19,5 +19,5 @@ export default defineConfig({ chromeWebSecurity: false, modifyObstructiveCode: false, //Increase timeout - defaultCommandTimeout: 6000, + defaultCommandTimeout: 10000, }) diff --git a/frontend/cypress/e2e/createANewProject.cy.ts b/frontend/cypress/e2e/createANewProject.cy.ts index 9bfc8366b3..0ab20ab240 100644 --- a/frontend/cypress/e2e/createANewProject.cy.ts +++ b/frontend/cypress/e2e/createANewProject.cy.ts @@ -140,13 +140,33 @@ const checkMetricsCalculation = (testId: string, boardData: MetricsDataItem[]) = }) } -// const checkPipelineCalculation = (testId: string) => { -// cy.get(testId).find('tr').contains('Deployment frequency(deployments/day)').should('exist') -// } +const checkBoardShowMore = () => { + reportPage.showMoreBoardButton.should('exist') + reportPage.goToBoardDetailPage() + cy.get('[data-test-id="Velocity"]').find('tbody > tr').should('have.length', 2) + cy.get('[data-test-id="Cycle time"]').find('tbody > tr').should('have.length', 17) + cy.get('[data-test-id="Classifications"]').find('tbody > tr').should('have.length', 103) -// const checkTimeToRecoveryPipelineCalculation = (testId: string) => { -// cy.get(testId).find('tr').contains('Mean Time To Recovery').should('exist') -// } + reportPage.exportBoardData() + checkBoardCSV() + + reportPage.boardGoToReportPage() +} + +const checkDoraShowMore = () => { + reportPage.showMoreDoraButton.should('exist') + reportPage.goToDoraDetailPage() + + cy.get('[data-test-id="Deployment frequency"]').find('tbody > tr').should('have.length', 2) + cy.get('[data-test-id="Lead time for changes"]').find('tbody > tr').should('have.length', 4) + cy.get('[data-test-id="Change failure rate"]').find('tbody > tr').should('have.length', 2) + cy.get('[data-test-id="Mean Time To Recovery"]').find('tbody > tr').should('have.length', 2) + + reportPage.exportPipelineData() + checkPipelineCSV() + + reportPage.doraGoToReportPage() +} const checkCycleTimeTooltip = () => { metricsPage.cycleTimeTitleTooltip.trigger('mouseover') @@ -334,6 +354,9 @@ describe('Create a new project', () => { reportPage.firstNotification.should('not.exist') + checkBoardShowMore() + checkDoraShowMore() + // checkpoint back to metrics step reportPage.backToMetricsStep() diff --git a/frontend/cypress/pages/metrics/report.ts b/frontend/cypress/pages/metrics/report.ts index c0085891af..5a8d8fd57d 100644 --- a/frontend/cypress/pages/metrics/report.ts +++ b/frontend/cypress/pages/metrics/report.ts @@ -31,6 +31,34 @@ class Report { return cy.contains('The file needs to be exported within 30 minutes, otherwise it will expire.') } + get showMoreBoardButton() { + return cy.contains('Board Metrics').parent().siblings().eq(0) + } + + get showMoreDoraButton() { + return cy.contains('DORA Metrics').parent().siblings().eq(0) + } + + get topBackButton() { + return cy.contains('Back') + } + + boardGoToReportPage() { + this.topBackButton.click() + } + + doraGoToReportPage() { + this.backButton.click() + } + + goToBoardDetailPage() { + this.showMoreBoardButton.click() + } + + goToDoraDetailPage() { + this.showMoreDoraButton.click() + } + backToMetricsStep() { this.backButton.click({ force: true }) } From adc673560fd833ef83a95d3dedbddb9175788c4f Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 15:01:06 +0800 Subject: [PATCH 39/90] ADM-652 [frontend] feat extra const: --- .../Metrics/ReportStep/ReportDetail/board.tsx | 7 ++--- .../Metrics/ReportStep/ReportDetail/dora.tsx | 10 +++---- .../ReportStep/ReportDetail/withBack.tsx | 3 ++- .../components/Metrics/ReportStep/index.tsx | 27 +++++++++++-------- frontend/src/constants/resources.ts | 9 ++++--- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index 2393403074..da79d990fd 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -6,6 +6,7 @@ import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' import { Optional } from '@src/utils/types' import { withGoBack } from './withBack' +import { REQUIRED_DATA } from '@src/constants/resources' interface Property { data: ReportResponseDTO @@ -20,11 +21,11 @@ export const BoardDetail = withGoBack(({ data }: Property) => { return ( <> - {showSectionWith2Columns('Velocity', mappedData.velocityList)} - {showSectionWith2Columns('Cycle time', mappedData.cycleTimeList)} + {showSectionWith2Columns(REQUIRED_DATA.VELOCITY, mappedData.velocityList)} + {showSectionWith2Columns(REQUIRED_DATA.CYCLE_TIME, mappedData.cycleTimeList)} {mappedData.classification && ( { return ( <> - {showSection('Deployment frequency', mappedData.deploymentFrequencyList)} - {showSection('Lead time for changes', mappedData.leadTimeForChangesList)} - {showSection('Change failure rate', mappedData.changeFailureRateList)} - {showSection('Mean Time To Recovery', mappedData.meanTimeToRecoveryList)} + {showSection(REQUIRED_DATA.DEPLOYMENT_FREQUENCY, mappedData.deploymentFrequencyList)} + {showSection(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES, mappedData.leadTimeForChangesList)} + {showSection(REQUIRED_DATA.CHANGE_FAILURE_RATE, mappedData.changeFailureRateList)} + {showSection(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY, mappedData.meanTimeToRecoveryList)} ) }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index 8e703a96df..a710e296b4 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -1,6 +1,7 @@ import React from 'react' import { styled } from '@mui/material/styles' import { ArrowBack } from '@mui/icons-material' +import { BACK } from '@src/constants/resources' interface Property { onBack: () => void } @@ -28,7 +29,7 @@ export const withGoBack = <> - {'Back'} + {BACK} diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 4c7c306e48..c805ade033 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useState } from 'react' import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' import { useAppSelector } from '@src/hooks' import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' -import { MESSAGE } from '@src/constants/resources' +import { MESSAGE, REPORT_PAGE_TYPE } from '@src/constants/resources' import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' import { ErrorNotification } from '@src/components/ErrorNotification' import { useNavigate } from 'react-router-dom' @@ -38,7 +38,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { } = useGenerateReportEffect() const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) - const [pageType, setPageType] = useState('Summary') + const [pageType, setPageType] = useState(REPORT_PAGE_TYPE.SUMMARY) const [isBackFromDetail, setIsBackFromDetail] = useState(false) const configData = useAppSelector(selectConfig) const csvTimeStamp = useAppSelector(selectTimeStamp) @@ -108,7 +108,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startDate={startDate} endDate={endDate} startToRequestBoardData={startToRequestBoardData} - onShowDetail={() => setPageType('BoardReport')} + onShowDetail={() => setPageType(REPORT_PAGE_TYPE.BOARD)} boardReport={reportData} csvTimeStamp={csvTimeStamp} /> @@ -119,7 +119,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startDate={startDate} endDate={endDate} startToRequestDoraData={startToRequestDoraData} - onShowDetail={() => setPageType('DoraReport')} + onShowDetail={() => setPageType(REPORT_PAGE_TYPE.DORA)} doraReport={reportData} csvTimeStamp={csvTimeStamp} /> @@ -130,11 +130,11 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const showDoraDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} /> const handleBack = () => { - pageType === 'Summary' ? dispatch(backStep()) : backToSummaryPage() + pageType === REPORT_PAGE_TYPE.SUMMARY ? dispatch(backStep()) : backToSummaryPage() } const backToSummaryPage = () => { - setPageType('Summary') + setPageType(REPORT_PAGE_TYPE.SUMMARY) setIsBackFromDetail(true) } @@ -154,13 +154,18 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { )} - {pageType === 'Summary' + {pageType === REPORT_PAGE_TYPE.SUMMARY ? showSummary() - : !!reportData && (pageType === 'BoardReport' ? showBoardDetail(reportData) : showDoraDetail(reportData))} + : !!reportData && + (pageType === REPORT_PAGE_TYPE.BOARD ? showBoardDetail(reportData) : showDoraDetail(reportData))} handleBack()} handleSave={() => handleSave()} reportData={reportData} diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index 0a4bbd7a55..e700fdce96 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -3,13 +3,14 @@ export const CALENDAR = { CHINA: 'Calendar with Chinese Holiday', } -export const REPORT_METRICS = { - REPORT: 'Report', - BOARD: 'Board Metrics', - DORA: 'Dora Metrics', +export const REPORT_PAGE_TYPE = { + SUMMARY: 'Summary', + BOARD: 'BoardReport', + DORA: 'DoraReport', } export const SHOW_MORE = 'show more >' +export const BACK = 'Back' export enum REQUIRED_DATA { All = 'All', From 7ea77dafbba2b223f43e4ab51ebe1b05a152ff1b Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 15:33:35 +0800 Subject: [PATCH 40/90] ADM-652 [frontend] feat: unified title name --- .../ReportStep/ReportDetail/board.test.tsx | 26 +++++++++---------- .../ReportStep/ReportDetail/dora.test.tsx | 24 ++++++++--------- frontend/cypress/e2e/createANewProject.cy.ts | 14 +++++----- .../Metrics/ReportStep/ReportDetail/board.tsx | 8 +++--- .../Metrics/ReportStep/ReportDetail/dora.tsx | 10 +++---- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx index e52a35fa3e..6c3941a966 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -40,7 +40,7 @@ describe('board', () => { }) }) - describe('Cycle time', () => { + describe('Cycle Time', () => { it('should show cycle time when cycle time data is existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ cycleTimeList: [ @@ -50,8 +50,8 @@ describe('board', () => { ], }) const { getByText, getByTestId, container } = render() - expect(getByText('Cycle time')).toBeInTheDocument() - expect(getByTestId('Cycle time')).toBeInTheDocument() + expect(getByText('Cycle Time')).toBeInTheDocument() + expect(getByTestId('Cycle Time')).toBeInTheDocument() expect(container.querySelectorAll('tbody > tr').length).toBe(3) }) @@ -60,7 +60,7 @@ describe('board', () => { cycleTimeList: null, }) const { queryAllByText } = render() - expect(queryAllByText('Cycle time').length).toEqual(0) + expect(queryAllByText('Cycle Time').length).toEqual(0) }) }) @@ -75,8 +75,8 @@ describe('board', () => { ], }) const { getByText, getByTestId, container } = render() - expect(getByText('Classifications')).toBeInTheDocument() - expect(getByTestId('Classifications')).toBeInTheDocument() + expect(getByText('Classification')).toBeInTheDocument() + expect(getByTestId('Classification')).toBeInTheDocument() expect(container.querySelectorAll('tbody > tr').length).toBe(8) }) @@ -85,7 +85,7 @@ describe('board', () => { classification: null, }) const { queryAllByText } = render() - expect(queryAllByText('Classifications').length).toEqual(0) + expect(queryAllByText('Classification').length).toEqual(0) }) }) @@ -105,13 +105,13 @@ describe('board', () => { const { getByText, getByTestId, container } = render() expect(getByText('Velocity')).toBeInTheDocument() expect(getByTestId('Velocity')).toBeInTheDocument() - expect(getByText('Cycle time')).toBeInTheDocument() - expect(getByTestId('Cycle time')).toBeInTheDocument() - expect(getByText('Classifications')).toBeInTheDocument() - expect(getByTestId('Classifications')).toBeInTheDocument() + expect(getByText('Cycle Time')).toBeInTheDocument() + expect(getByTestId('Cycle Time')).toBeInTheDocument() + expect(getByText('Classification')).toBeInTheDocument() + expect(getByTestId('Classification')).toBeInTheDocument() expect(container.querySelectorAll('table[data-test-id="Velocity"] > tbody > tr').length).toBe(1) - expect(container.querySelectorAll('table[data-test-id="Cycle time"] > tbody > tr').length).toBe(2) - expect(container.querySelectorAll('table[data-test-id="Classifications"] > tbody > tr').length).toBe(6) + expect(container.querySelectorAll('table[data-test-id="Cycle Time"] > tbody > tr').length).toBe(2) + expect(container.querySelectorAll('table[data-test-id="Classification"] > tbody > tr').length).toBe(6) }) }) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx index 725cdd0edb..8b6e04e890 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx @@ -16,14 +16,14 @@ describe('DoraDetail', () => { expect(getByText('Back')).toBeInTheDocument() }) - describe('Deployment frequency', () => { + describe('Deployment Frequency', () => { it('should show deploymentFrequencyList when deploymentFrequencyList data is existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ deploymentFrequencyList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) const { getByText, getByTestId, container } = render() - expect(getByText('Deployment frequency')).toBeInTheDocument() - expect(getByTestId('Deployment frequency')).toBeInTheDocument() + expect(getByText('Deployment Frequency')).toBeInTheDocument() + expect(getByTestId('Deployment Frequency')).toBeInTheDocument() expect(container.querySelectorAll('tbody > tr').length).toBe(2) }) @@ -32,18 +32,18 @@ describe('DoraDetail', () => { deploymentFrequencyList: null, }) const { queryAllByText } = render() - expect(queryAllByText('Deployment frequency').length).toEqual(0) + expect(queryAllByText('Deployment Frequency').length).toEqual(0) }) }) - describe('Lead time for changes', () => { + describe('Lead Time For Changes', () => { it('should show leadTimeForChangesList when leadTimeForChangesList data is existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ leadTimeForChangesList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) const { getByText, getByTestId, container } = render() - expect(getByText('Lead time for changes')).toBeInTheDocument() - expect(getByTestId('Lead time for changes')).toBeInTheDocument() + expect(getByText('Lead Time For Changes')).toBeInTheDocument() + expect(getByTestId('Lead Time For Changes')).toBeInTheDocument() expect(container.querySelectorAll('tbody > tr').length).toBe(2) }) @@ -52,18 +52,18 @@ describe('DoraDetail', () => { leadTimeForChangesList: null, }) const { queryAllByText } = render() - expect(queryAllByText('Lead time for changes').length).toEqual(0) + expect(queryAllByText('Lead Time For Changes').length).toEqual(0) }) }) - describe('Change failure rate', () => { + describe('Change Failure Rate', () => { it('should show changeFailureRateList when changeFailureRateList data is existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ changeFailureRateList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) const { getByText, getByTestId, container } = render() - expect(getByText('Change failure rate')).toBeInTheDocument() - expect(getByTestId('Change failure rate')).toBeInTheDocument() + expect(getByText('Change Failure Rate')).toBeInTheDocument() + expect(getByTestId('Change Failure Rate')).toBeInTheDocument() expect(container.querySelectorAll('tbody > tr').length).toBe(2) }) @@ -72,7 +72,7 @@ describe('DoraDetail', () => { changeFailureRateList: null, }) const { queryAllByText } = render() - expect(queryAllByText('Change failure rate').length).toEqual(0) + expect(queryAllByText('Change Failure Rate').length).toEqual(0) }) }) diff --git a/frontend/cypress/e2e/createANewProject.cy.ts b/frontend/cypress/e2e/createANewProject.cy.ts index 0ab20ab240..8b2cf41346 100644 --- a/frontend/cypress/e2e/createANewProject.cy.ts +++ b/frontend/cypress/e2e/createANewProject.cy.ts @@ -143,9 +143,9 @@ const checkMetricsCalculation = (testId: string, boardData: MetricsDataItem[]) = const checkBoardShowMore = () => { reportPage.showMoreBoardButton.should('exist') reportPage.goToBoardDetailPage() - cy.get('[data-test-id="Velocity"]').find('tbody > tr').should('have.length', 2) - cy.get('[data-test-id="Cycle time"]').find('tbody > tr').should('have.length', 17) - cy.get('[data-test-id="Classifications"]').find('tbody > tr').should('have.length', 103) + cy.get(`[data-test-id="${METRICS_TITLE.VELOCITY}"]`).find('tbody > tr').should('have.length', 2) + cy.get(`[data-test-id="${METRICS_TITLE.CYCLE_TIME}"]`).find('tbody > tr').should('have.length', 17) + cy.get(`[data-test-id="${METRICS_TITLE.CLASSIFICATION}"]`).find('tbody > tr').should('have.length', 103) reportPage.exportBoardData() checkBoardCSV() @@ -157,10 +157,10 @@ const checkDoraShowMore = () => { reportPage.showMoreDoraButton.should('exist') reportPage.goToDoraDetailPage() - cy.get('[data-test-id="Deployment frequency"]').find('tbody > tr').should('have.length', 2) - cy.get('[data-test-id="Lead time for changes"]').find('tbody > tr').should('have.length', 4) - cy.get('[data-test-id="Change failure rate"]').find('tbody > tr').should('have.length', 2) - cy.get('[data-test-id="Mean Time To Recovery"]').find('tbody > tr').should('have.length', 2) + cy.get(`[data-test-id="${METRICS_TITLE.DEPLOYMENT_FREQUENCY}"]`).find('tbody > tr').should('have.length', 2) + cy.get(`[data-test-id="${METRICS_TITLE.LEAD_TIME_FOR_CHANGES}"]`).find('tbody > tr').should('have.length', 4) + cy.get(`[data-test-id="${METRICS_TITLE.CHANGE_FAILURE_RATE}"]`).find('tbody > tr').should('have.length', 2) + cy.get(`[data-test-id="${METRICS_TITLE.MEAN_TIME_TO_RECOVERY}"]`).find('tbody > tr').should('have.length', 2) reportPage.exportPipelineData() checkPipelineCSV() diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index da79d990fd..b8f92dcbb1 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -6,7 +6,7 @@ import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' import { Optional } from '@src/utils/types' import { withGoBack } from './withBack' -import { REQUIRED_DATA } from '@src/constants/resources' +import { METRICS_TITLE } from '@src/constants/resources' interface Property { data: ReportResponseDTO @@ -21,11 +21,11 @@ export const BoardDetail = withGoBack(({ data }: Property) => { return ( <> - {showSectionWith2Columns(REQUIRED_DATA.VELOCITY, mappedData.velocityList)} - {showSectionWith2Columns(REQUIRED_DATA.CYCLE_TIME, mappedData.cycleTimeList)} + {showSectionWith2Columns(METRICS_TITLE.VELOCITY, mappedData.velocityList)} + {showSectionWith2Columns(METRICS_TITLE.CYCLE_TIME, mappedData.cycleTimeList)} {mappedData.classification && ( { return ( <> - {showSection(REQUIRED_DATA.DEPLOYMENT_FREQUENCY, mappedData.deploymentFrequencyList)} - {showSection(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES, mappedData.leadTimeForChangesList)} - {showSection(REQUIRED_DATA.CHANGE_FAILURE_RATE, mappedData.changeFailureRateList)} - {showSection(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY, mappedData.meanTimeToRecoveryList)} + {showSection(METRICS_TITLE.DEPLOYMENT_FREQUENCY, mappedData.deploymentFrequencyList)} + {showSection(METRICS_TITLE.LEAD_TIME_FOR_CHANGES, mappedData.leadTimeForChangesList)} + {showSection(METRICS_TITLE.CHANGE_FAILURE_RATE, mappedData.changeFailureRateList)} + {showSection(METRICS_TITLE.MEAN_TIME_TO_RECOVERY, mappedData.meanTimeToRecoveryList)} ) }) From 981c13df6d552addd99050869c0971eb38f902ac Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 15:42:54 +0800 Subject: [PATCH 41/90] ADM-652 [frontend] feat: delete PageType --- frontend/src/components/Metrics/ReportStep/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index c805ade033..30bd9fa257 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -23,8 +23,6 @@ export interface ReportStepProps { handleSave: () => void } -export type PageType = 'Summary' | 'BoardReport' | 'DoraReport' - const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const navigate = useNavigate() const dispatch = useAppDispatch() @@ -38,7 +36,7 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { } = useGenerateReportEffect() const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) - const [pageType, setPageType] = useState(REPORT_PAGE_TYPE.SUMMARY) + const [pageType, setPageType] = useState(REPORT_PAGE_TYPE.SUMMARY) const [isBackFromDetail, setIsBackFromDetail] = useState(false) const configData = useAppSelector(selectConfig) const csvTimeStamp = useAppSelector(selectTimeStamp) From e17070546867308188355864eaa706c67368ae7b Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 16:04:08 +0800 Subject: [PATCH 42/90] ADM-652 [frontend] feat: add isShowExportMetrics --- frontend/src/components/Metrics/ReportButtonGroup/index.tsx | 6 ++++-- frontend/src/components/Metrics/ReportStep/index.tsx | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 29c4119484..2317274fbc 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -25,6 +25,7 @@ interface ReportButtonGroupProps { isShowSave: boolean isShowExportBoardButton: boolean isShowExportPipelineButton: boolean + isShowExportMetrics: boolean } export const ReportButtonGroup = ({ @@ -36,6 +37,7 @@ export const ReportButtonGroup = ({ setErrorMessage, reportData, isShowSave, + isShowExportMetrics, isShowExportBoardButton, isShowExportPipelineButton, }: ReportButtonGroupProps) => { @@ -70,14 +72,14 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.BACK} - { + {isShowExportMetrics && ( handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} > {COMMON_BUTTONS.EXPORT_METRIC_DATA} - } + )} {isShowExportBoardButton && ( { (pageType === REPORT_PAGE_TYPE.BOARD ? showBoardDetail(reportData) : showDoraDetail(reportData))} Date: Tue, 9 Jan 2024 16:24:48 +0800 Subject: [PATCH 43/90] ADM-708:[backend]refactor: add deprecated annotation for board old api,service,tests --- .../controller/board/JiraController.java | 1 + .../heartbeat/service/board/jira/JiraService.java | 1 + .../controller/board/JiraControllerTest.java | 2 ++ .../heartbeat/service/jira/JiraServiceTest.java | 15 +++++++++++++++ 4 files changed, 19 insertions(+) diff --git a/backend/src/main/java/heartbeat/controller/board/JiraController.java b/backend/src/main/java/heartbeat/controller/board/JiraController.java index fbcd51f97a..45725c10d2 100644 --- a/backend/src/main/java/heartbeat/controller/board/JiraController.java +++ b/backend/src/main/java/heartbeat/controller/board/JiraController.java @@ -22,6 +22,7 @@ public class JiraController { private final JiraService jiraService; + @Deprecated @PostMapping("/{boardType}") public BoardConfigDTO getBoard(@PathVariable @NotBlank BoardType boardType, @Valid @RequestBody BoardRequestParam boardRequestParam) { 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 565315d074..66b00438f4 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -168,6 +168,7 @@ public BoardConfigDTO getInfo(BoardType boardType, BoardRequestParam boardReques } } + @Deprecated public BoardConfigDTO getJiraConfiguration(BoardType boardType, BoardRequestParam boardRequestParam) { URI baseUrl = urlGenerator.getUri(boardRequestParam.getSite()); try { diff --git a/backend/src/test/java/heartbeat/controller/board/JiraControllerTest.java b/backend/src/test/java/heartbeat/controller/board/JiraControllerTest.java index bdc58f7ec1..6ce6ea355c 100644 --- a/backend/src/test/java/heartbeat/controller/board/JiraControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/board/JiraControllerTest.java @@ -40,6 +40,7 @@ public class JiraControllerTest { private MockMvc mockMvc; @Test + @Deprecated void shouldReturnCorrectBoardConfigResponseWhenGivenTheCorrectBoardRequest() throws Exception { BoardConfigDTO boardConfigDTO = BOARD_CONFIG_RESPONSE_BUILDER().build(); BoardRequestParam boardRequestParam = BOARD_REQUEST_BUILDER().build(); @@ -85,6 +86,7 @@ void shouldReturnCorrectBoardVerificationResponseWhenGivenTheCorrectBoardRequest } @Test + @Deprecated void shouldHandleServiceExceptionAndReturnWithStatusAndMessage() throws Exception { RequestFailedException mockException = mock(RequestFailedException.class); String message = "message"; diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index 772cfe3e6f..f0c3464510 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -150,6 +150,7 @@ public ThreadPoolTaskExecutor getTaskExecutor() { } @Test + @Deprecated void shouldCallJiraFeignClientAndReturnBoardConfigResponseWhenGetJiraBoardConfig() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -251,6 +252,7 @@ void shouldCallJiraFeignClientAndReturnBoardVerifyResponseWhenVerifyJiraBoard() } @Test + @Deprecated void shouldCallJiraFeignClientAndReturnBoardConfigResponseWhenGetJiraBoardConfigHasTwoPage() throws IOException { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -331,6 +333,7 @@ void shouldCallJiraFeignClientAndReturnBoardInfoResponseWhenGetJiraBoardInfoHasT } @Test + @Deprecated void shouldCallJiraFeignClientAndReturnBoardConfigResponseWhenGetClassicJiraBoardConfig() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = CLASSIC_JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); @@ -415,6 +418,7 @@ void shouldCallJiraFeignClientAndReturnBoardInfoResponseWhenGetClassicJiraBoardI } @Test + @Deprecated void shouldCallJiraFeignClientAndThrowParamExceptionWhenGetJiraBoardConfig() { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -467,6 +471,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenVerifyJiraBoard() { } @Test + @Deprecated void shouldCallJiraFeignClientAndThrowNotFoundExceptionWhenGetJiraBoardConfig() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -521,6 +526,7 @@ void shouldCallJiraFeignClientAndThrowNotFoundExceptionWhenGetJiraBoardInfo() th } @Test + @Deprecated void shouldCallJiraFeignClientAndThrowNonContentCodeWhenGetJiraBoardConfig() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -571,6 +577,7 @@ void shouldCallJiraFeignClientAndThrowNonContentCodeWhenGetJiraBoardInfo() throw } @Test + @Deprecated void shouldCallJiraFeignClientAndThrowNonColumnWhenGetJiraBoardConfig() { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO noneStatusSelf = NONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -650,6 +657,7 @@ void shouldGetCardsWhenCallGetStoryPointsAndCycleTimeGiveStoryPointKey() throws } @Test + @Deprecated void shouldThrowExceptionWhenGetJiraConfigurationThrowsUnExpectedException() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); BoardRequestParam boardRequestParam = BOARD_REQUEST_BUILDER().build(); @@ -681,6 +689,7 @@ void shouldThrowExceptionWhenGetJiraInfoThrowsUnExpectedException() { } @Test + @Deprecated void shouldReturnAssigneeNameFromDoneCardWhenGetAssigneeSet() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); @@ -739,6 +748,7 @@ void shouldReturnAssigneeNameFromDoneCardWhenGetBoardInfoAndGetAssigneeSet() thr } @Test + @Deprecated void shouldThrowExceptionWhenGetTargetFieldFailed() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); String token = "token"; @@ -771,6 +781,7 @@ void shouldThrowExceptionWhenGetBoardInfoAndGetTargetFieldFailed() { } @Test + @Deprecated void shouldThrowExceptionWhenGetTargetFieldReturnNull() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); String token = "token"; @@ -801,6 +812,7 @@ void shouldThrowExceptionWhenGetBoardInfoAndGetTargetFieldReturnNull() { } @Test + @Deprecated void shouldThrowExceptionWhenGetTargetFieldReturnEmpty() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); String token = "token"; @@ -839,6 +851,7 @@ void shouldThrowExceptionWhenGetBoardInfoAndGetTargetFieldReturnEmpty() { } @Test + @Deprecated void shouldThrowCustomExceptionWhenGetJiraBoardConfig() { when(urlGenerator.getUri(any())).thenReturn(URI.create(SITE_ATLASSIAN_NET)); @@ -861,6 +874,7 @@ void shouldThrowCustomExceptionWhenGetJiraBoardInfo() { } @Test + @Deprecated void shouldThrowCustomExceptionWhenCallJiraFeignClientToGetBoardConfigFailed() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); when(urlGenerator.getUri(any())).thenReturn(URI.create(SITE_ATLASSIAN_NET)); @@ -1240,6 +1254,7 @@ void shouldGetRealDoneCardWhenCallGetStoryPointsAndCycleTimeWhenUseLastAssigneeF } @Test + @Deprecated void shouldFilterOutUnreasonableTargetField() throws JsonProcessingException { JiraBoardConfigDTO jiraBoardConfigDTO = CLASSIC_JIRA_BOARD_CONFIG_RESPONSE_BUILDER().build(); StatusSelfDTO doneStatusSelf = DONE_STATUS_SELF_RESPONSE_BUILDER().build(); From aceabbf6438b20c6de41368ba0811d92443b0490 Mon Sep 17 00:00:00 2001 From: LEI WANG Date: Mon, 8 Jan 2024 14:04:39 +0800 Subject: [PATCH 44/90] ADM-741:[backend]feat: remove plain text token --- .../main/java/heartbeat/config/WebConfig.java | 8 + .../report/GenerateReportController.java | 29 +++- .../dto/request/GenerateReportRequest.java | 21 +++ .../report/dto/request/ReportType.java | 15 ++ .../report/GenerateReporterService.java | 126 ++++++++++----- .../src/main/java/heartbeat/util/IdUtil.java | 6 + .../main/java/heartbeat/util/MetricsUtil.java | 39 +++++ .../GenerateReporterControllerTest.java | 63 +++++++- .../report/dto/request/ReportTypeTest.java | 25 +++ .../heartbeat/controller/report/request.json | 6 + .../report/GenerateReporterServiceTest.java | 144 +++++++++++++++++- 11 files changed, 425 insertions(+), 57 deletions(-) create mode 100644 backend/src/main/java/heartbeat/controller/report/dto/request/ReportType.java create mode 100644 backend/src/main/java/heartbeat/util/MetricsUtil.java create mode 100644 backend/src/test/java/heartbeat/controller/report/dto/request/ReportTypeTest.java diff --git a/backend/src/main/java/heartbeat/config/WebConfig.java b/backend/src/main/java/heartbeat/config/WebConfig.java index 36f42ef305..91445b5f5e 100644 --- a/backend/src/main/java/heartbeat/config/WebConfig.java +++ b/backend/src/main/java/heartbeat/config/WebConfig.java @@ -2,6 +2,7 @@ import heartbeat.controller.board.dto.request.BoardType; import heartbeat.controller.report.dto.request.DataType; +import heartbeat.controller.report.dto.request.ReportType; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.format.FormatterRegistry; @@ -25,6 +26,13 @@ public DataType convert(String source) { return DataType.fromValue(source); } }); + + registry.addConverter(new Converter() { + @Override + public ReportType convert(String type) { + return ReportType.fromValue(type); + } + }); } } diff --git a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java index 7dd11eca1e..a2cb4deecd 100644 --- a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java +++ b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java @@ -1,11 +1,14 @@ package heartbeat.controller.report; import heartbeat.controller.report.dto.request.DataType; -import heartbeat.controller.report.dto.request.ExportCSVRequest; -import heartbeat.controller.report.dto.request.GenerateBoardReportRequest; +import heartbeat.controller.report.dto.request.ReportType; import heartbeat.controller.report.dto.request.GenerateDoraReportRequest; +import heartbeat.controller.report.dto.request.GenerateBoardReportRequest; +import heartbeat.controller.report.dto.request.GenerateReportRequest; +import heartbeat.controller.report.dto.request.ExportCSVRequest; import heartbeat.controller.report.dto.response.CallbackResponse; import heartbeat.controller.report.dto.response.ReportResponse; +import heartbeat.exception.BadRequestException; import heartbeat.exception.BaseException; import heartbeat.handler.AsyncExceptionHandler; import heartbeat.service.report.GenerateReporterService; @@ -17,12 +20,12 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.PostMapping; import java.util.concurrent.CompletableFuture; @@ -62,7 +65,7 @@ public ResponseEntity generateReport(@PathVariable String report return ResponseEntity.status(HttpStatus.OK).body(reportResponse); } - @PostMapping("/board") + @PostMapping("/v1/board") public ResponseEntity generateBoardReport(@RequestBody GenerateBoardReportRequest request) { log.info( "Start to generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", @@ -93,7 +96,7 @@ public ResponseEntity generateBoardReport(@RequestBody Generat .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); } - @PostMapping("/dora") + @PostMapping("/v1/dora") public ResponseEntity generateDoraReport(@RequestBody GenerateDoraReportRequest request) { log.info( "Start to generate dora report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", @@ -123,4 +126,18 @@ public ResponseEntity generateDoraReport(@RequestBody Generate .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); } + @PostMapping("{reportType}") + public ResponseEntity generateReport(@PathVariable ReportType reportType, + @RequestBody GenerateReportRequest request) { + CompletableFuture.runAsync(() -> { + switch (reportType) { + case BOARD -> generateReporterService.generateBoardReport(request); + case DORA -> generateReporterService.generateDoraReport(request); + } + }); + String callbackUrl = "/reports/" + request.getCsvTimeStamp(); + return ResponseEntity.status(HttpStatus.ACCEPTED) + .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); + } + } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java index a4fdb9f0ca..4b8784ce29 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java @@ -1,6 +1,8 @@ package heartbeat.controller.report.dto.request; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.google.gson.Gson; +import heartbeat.util.MetricsUtil; import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; @@ -35,4 +37,23 @@ public class GenerateReportRequest { @NotBlank private String csvTimeStamp; + public GenerateReportRequest convertToPipelineRequest(GenerateReportRequest request) { + List pipelineMetrics = MetricsUtil + .getPipelineMetrics(request.getMetrics().stream().map(String::toLowerCase).toList()); + Gson gson = new Gson(); + GenerateReportRequest result = gson.fromJson(gson.toJson(request), GenerateReportRequest.class); + + result.setMetrics(pipelineMetrics); + return result; + } + + public GenerateReportRequest convertToCodeBaseRequest(GenerateReportRequest request) { + List codebaseMetrics = MetricsUtil + .getCodeBaseMetrics(request.getMetrics().stream().map(String::toLowerCase).toList()); + Gson gson = new Gson(); + GenerateReportRequest result = gson.fromJson(gson.toJson(request), GenerateReportRequest.class); + result.setMetrics(codebaseMetrics); + return result; + } + } diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/ReportType.java b/backend/src/main/java/heartbeat/controller/report/dto/request/ReportType.java new file mode 100644 index 0000000000..2ef7a05d75 --- /dev/null +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/ReportType.java @@ -0,0 +1,15 @@ +package heartbeat.controller.report.dto.request; + +public enum ReportType { + + BOARD, DORA; + + public static ReportType fromValue(String type) { + return switch (type) { + case "board" -> BOARD; + case "dora" -> DORA; + default -> throw new IllegalArgumentException("ReportType not found!"); + }; + } + +} diff --git a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java index 3fa0bd27a8..0d46a26a4f 100644 --- a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java +++ b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java @@ -71,16 +71,19 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import heartbeat.util.IdUtil; +import heartbeat.util.MetricsUtil; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import lombok.val; +import org.apache.commons.collections.CollectionUtils; import org.springframework.core.io.InputStreamResource; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -127,21 +130,6 @@ public class GenerateReporterService { private final AsyncExceptionHandler asyncExceptionHandler; - private final List kanbanMetrics = Stream - .of(RequireDataEnum.VELOCITY, RequireDataEnum.CYCLE_TIME, RequireDataEnum.CLASSIFICATION) - .map(RequireDataEnum::getValue) - .toList(); - - private final List buildKiteMetrics = Stream - .of(RequireDataEnum.CHANGE_FAILURE_RATE, RequireDataEnum.DEPLOYMENT_FREQUENCY, - RequireDataEnum.MEAN_TIME_TO_RECOVERY) - .map(RequireDataEnum::getValue) - .toList(); - - private final List codebaseMetrics = Stream.of(RequireDataEnum.LEAD_TIME_FOR_CHANGES) - .map(RequireDataEnum::getValue) - .toList(); - private static StoryPointsAndCycleTimeRequest buildStoryPointsAndCycleTimeRequest(JiraBoardSetting jiraBoardSetting, String startTime, String endTime) { return StoryPointsAndCycleTimeRequest.builder() @@ -213,17 +201,81 @@ else if (jsonObject.has("key")) { return result; } + public void generateBoardReport(GenerateReportRequest request) { + initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); + + CompletableFuture.runAsync(() -> { + try { + saveReporterInHandler(generateReporter(request), IdUtil.getBoardReportId(request.getCsvTimeStamp())); + updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); + log.info( + "Successfully generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", + request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), + request.getEndTime(), IdUtil.getBoardReportId(request.getCsvTimeStamp())); + } + catch (BaseException e) { + asyncExceptionHandler.put(IdUtil.getBoardReportId(request.getCsvTimeStamp()), e); + } + }); + } + + public void generateDoraReport(GenerateReportRequest request) { + MetricsDataReady metricsDataStatus = getMetricsStatus(request.getMetrics(), Boolean.TRUE); + initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); + if (Objects.nonNull(metricsDataStatus.isSourceControlMetricsReady()) + && metricsDataStatus.isSourceControlMetricsReady()) { + generateCodeBaseReport(request); + } + if (Objects.nonNull(metricsDataStatus.isPipelineMetricsReady()) && metricsDataStatus.isPipelineMetricsReady()) { + generatePipelineReport(request); + } + generateCsvForDora(request); + } + + private void generatePipelineReport(GenerateReportRequest request) { + CompletableFuture.runAsync(() -> { + try { + GenerateReportRequest pipelineRequest = request.convertToPipelineRequest(request); + saveReporterInHandler(generateReporter(pipelineRequest), + IdUtil.getDoraReportId(pipelineRequest.getCsvTimeStamp())); + updateMetricsDataReadyInHandler(pipelineRequest.getCsvTimeStamp(), pipelineRequest.getMetrics()); + log.info( + "Successfully generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _pipelineReportId: {}", + pipelineRequest.getMetrics(), pipelineRequest.getConsiderHoliday(), + pipelineRequest.getStartTime(), pipelineRequest.getEndTime(), + IdUtil.getDoraReportId(request.getCsvTimeStamp())); + } + catch (BaseException e) { + asyncExceptionHandler.put(IdUtil.getDoraReportId(request.getCsvTimeStamp()), e); + } + }); + } + + private void generateCodeBaseReport(GenerateReportRequest request) { + CompletableFuture.runAsync(() -> { + try { + GenerateReportRequest codebaseRequest = request.convertToCodeBaseRequest(request); + saveReporterInHandler(generateReporter(codebaseRequest), + IdUtil.getCodeBaseReportId(codebaseRequest.getCsvTimeStamp())); + updateMetricsDataReadyInHandler(codebaseRequest.getCsvTimeStamp(), codebaseRequest.getMetrics()); + log.info( + "Successfully generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _codeBaseReportId: {}", + codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), + codebaseRequest.getStartTime(), codebaseRequest.getEndTime(), + IdUtil.getCodeBaseReportId(request.getCsvTimeStamp())); + } + catch (BaseException e) { + asyncExceptionHandler.put(IdUtil.getCodeBaseReportId(request.getCsvTimeStamp()), e); + } + }); + } + public synchronized ReportResponse generateReporter(GenerateReportRequest request) { workDay.changeConsiderHolidayMode(request.getConsiderHoliday()); // fetch data for calculate List lowMetrics = request.getMetrics().stream().map(String::toLowerCase).toList(); FetchedData fetchedData = fetchOriginalData(request, lowMetrics); - if (lowMetrics.stream().anyMatch(this.codebaseMetrics::contains) - || lowMetrics.stream().anyMatch(this.buildKiteMetrics::contains)) { - generateCSVForPipeline(request, fetchedData.getBuildKiteData()); - } - ReportResponse reportResponse = new ReportResponse(EXPORT_CSV_VALIDITY_TIME); JiraBoardSetting jiraBoardSetting = request.getJiraBoardSetting(); @@ -258,21 +310,21 @@ public synchronized ReportResponse generateReporter(GenerateReportRequest reques private FetchedData fetchOriginalData(GenerateReportRequest request, List lowMetrics) { FetchedData fetchedData = new FetchedData(); - if (lowMetrics.stream().anyMatch(this.kanbanMetrics::contains)) { + if (CollectionUtils.isNotEmpty(MetricsUtil.getBoardMetrics(lowMetrics))) { if (request.getJiraBoardSetting() == null) throw new BadRequestException("Failed to fetch Jira info due to Jira board setting is null."); CardCollectionInfo cardCollectionInfo = fetchDataFromKanban(request); fetchedData.setCardCollectionInfo(cardCollectionInfo); } - if (lowMetrics.stream().anyMatch(this.codebaseMetrics::contains)) { + if (CollectionUtils.isNotEmpty(MetricsUtil.getCodeBaseMetrics(lowMetrics))) { if (request.getCodebaseSetting() == null) throw new BadRequestException("Failed to fetch Github info due to code base setting is null."); BuildKiteData buildKiteData = fetchGithubData(request); fetchedData.setBuildKiteData(buildKiteData); } - if (lowMetrics.stream().anyMatch(this.buildKiteMetrics::contains)) { + if (CollectionUtils.isNotEmpty(MetricsUtil.getPipelineMetrics(lowMetrics))) { if (request.getBuildKiteSetting() == null) throw new BadRequestException("Failed to fetch BuildKite info due to BuildKite setting is null."); FetchedData.BuildKiteData buildKiteData = fetchBuildKiteInfo(request); @@ -287,6 +339,14 @@ private FetchedData fetchOriginalData(GenerateReportRequest request, List lowMetrics = request.getMetrics().stream().map(String::toLowerCase).toList(); + FetchedData fetchedData = fetchOriginalData(request, lowMetrics); + + generateCSVForPipeline(request, fetchedData.getBuildKiteData()); + + } + private CardCollection fetchRealDoneCardCollection(GenerateReportRequest request) { JiraBoardSetting jiraBoardSetting = request.getJiraBoardSetting(); StoryPointsAndCycleTimeRequest storyPointsAndCycleTimeRequest = buildStoryPointsAndCycleTimeRequest( @@ -638,13 +698,10 @@ private Boolean checkCurrentMetricsReadyState(Boolean exist, Boolean previousVal } private MetricsDataReady getMetricsStatus(List metrics, Boolean flag) { - boolean boardMetricsExist = metrics.stream().map(String::toLowerCase).anyMatch(this.kanbanMetrics::contains); - boolean codebaseMetricsExist = metrics.stream() - .map(String::toLowerCase) - .anyMatch(this.codebaseMetrics::contains); - boolean buildKiteMetricsExist = metrics.stream() - .map(String::toLowerCase) - .anyMatch(this.buildKiteMetrics::contains); + List lowerMetrics = metrics.stream().map(String::toLowerCase).collect(Collectors.toList()); + boolean boardMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getBoardMetrics(lowerMetrics)); + boolean codebaseMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getCodeBaseMetrics(lowerMetrics)); + boolean buildKiteMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getPipelineMetrics(lowerMetrics)); Boolean isBoardMetricsReady = boardMetricsExist ? flag : null; Boolean isCodebaseMetricsReady = codebaseMetricsExist ? flag : null; Boolean isBuildKiteMetricsReady = buildKiteMetricsExist ? flag : null; @@ -691,10 +748,6 @@ private List generateCSVForPipelineWithCodebase(CodebaseSetting String endTime, BuildKiteData buildKiteData, List deploymentEnvironments) { List pipelineCSVInfos = new ArrayList<>(); - if (codebaseSetting == null && CollectionUtils.isEmpty(deploymentEnvironments)) { - return pipelineCSVInfos; - } - Map repoIdMap = getRepoMap(deploymentEnvironments); for (DeploymentEnvironment deploymentEnvironment : deploymentEnvironments) { String repoId = GithubUtil.getGithubUrlFullName(repoIdMap.get(deploymentEnvironment.getId())); @@ -806,6 +859,7 @@ public ReportResponse getReportFromHandler(String reportId) { public ReportResponse getComposedReportResponse(String reportId, boolean isReportReady) { ReportResponse boardReportResponse = getReportFromHandler(IdUtil.getBoardReportId(reportId)); ReportResponse doraReportResponse = getReportFromHandler(IdUtil.getDoraReportId(reportId)); + ReportResponse codebaseReportResponse = getReportFromHandler(IdUtil.getCodeBaseReportId(reportId)); MetricsDataReady metricsDataReady = asyncReportRequestHandler.getMetricsDataReady(reportId); ReportResponse response = Optional.ofNullable(boardReportResponse).orElse(doraReportResponse); @@ -817,7 +871,7 @@ public ReportResponse getComposedReportResponse(String reportId, boolean isRepor .deploymentFrequency(getValueOrNull(doraReportResponse, ReportResponse::getDeploymentFrequency)) .changeFailureRate(getValueOrNull(doraReportResponse, ReportResponse::getChangeFailureRate)) .meanTimeToRecovery(getValueOrNull(doraReportResponse, ReportResponse::getMeanTimeToRecovery)) - .leadTimeForChanges(getValueOrNull(doraReportResponse, ReportResponse::getLeadTimeForChanges)) + .leadTimeForChanges(getValueOrNull(codebaseReportResponse, ReportResponse::getLeadTimeForChanges)) .isBoardMetricsReady(getValueOrNull(metricsDataReady, MetricsDataReady::isBoardMetricsReady)) .isPipelineMetricsReady(getValueOrNull(metricsDataReady, MetricsDataReady::isPipelineMetricsReady)) .isSourceControlMetricsReady( diff --git a/backend/src/main/java/heartbeat/util/IdUtil.java b/backend/src/main/java/heartbeat/util/IdUtil.java index 6d74881644..176a1b773d 100644 --- a/backend/src/main/java/heartbeat/util/IdUtil.java +++ b/backend/src/main/java/heartbeat/util/IdUtil.java @@ -6,6 +6,8 @@ public interface IdUtil { String DORA_REPORT_PREFIX = "dora-"; + String CODE_BASE_PREFIX = "github-"; + static String getBoardReportId(String timeStamp) { return BOARD_REPORT_PREFIX + timeStamp; } @@ -14,6 +16,10 @@ static String getDoraReportId(String timeStamp) { return DORA_REPORT_PREFIX + timeStamp; } + static String getCodeBaseReportId(String timeStamp) { + return CODE_BASE_PREFIX + timeStamp; + } + static String getTimeStampFromReportId(String reportId) { String[] splitResult = reportId.split("\\s*\\-|\\.\\s*"); return splitResult[1]; diff --git a/backend/src/main/java/heartbeat/util/MetricsUtil.java b/backend/src/main/java/heartbeat/util/MetricsUtil.java new file mode 100644 index 0000000000..58d6dee5eb --- /dev/null +++ b/backend/src/main/java/heartbeat/util/MetricsUtil.java @@ -0,0 +1,39 @@ +package heartbeat.util; + +import heartbeat.controller.report.dto.request.RequireDataEnum; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public interface MetricsUtil { + + List kanbanMetrics = Stream + .of(RequireDataEnum.VELOCITY, RequireDataEnum.CYCLE_TIME, RequireDataEnum.CLASSIFICATION) + .map(RequireDataEnum::getValue) + .toList(); + + List buildKiteMetrics = Stream + .of(RequireDataEnum.CHANGE_FAILURE_RATE, RequireDataEnum.DEPLOYMENT_FREQUENCY, + RequireDataEnum.MEAN_TIME_TO_RECOVERY) + .map(RequireDataEnum::getValue) + .toList(); + + List codebaseMetrics = Stream.of(RequireDataEnum.LEAD_TIME_FOR_CHANGES) + .map(RequireDataEnum::getValue) + .toList(); + + static List getPipelineMetrics(List metrics) { + return metrics.stream().filter(buildKiteMetrics::contains).collect(Collectors.toList()); + } + + static List getCodeBaseMetrics(List metrics) { + return metrics.stream().filter(codebaseMetrics::contains).collect(Collectors.toList()); + + } + + static List getBoardMetrics(List metrics) { + return metrics.stream().filter(kanbanMetrics::contains).collect(Collectors.toList()); + } + +} diff --git a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java index d2b939e491..b831665eb4 100644 --- a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java @@ -5,6 +5,7 @@ import heartbeat.controller.report.dto.request.ExportCSVRequest; import heartbeat.controller.report.dto.request.GenerateBoardReportRequest; import heartbeat.controller.report.dto.request.GenerateDoraReportRequest; +import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.response.AvgDeploymentFrequency; import heartbeat.controller.report.dto.response.DeploymentFrequency; import heartbeat.controller.report.dto.response.ReportResponse; @@ -28,7 +29,6 @@ import java.io.ByteArrayInputStream; import java.io.File; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -161,7 +161,7 @@ void shouldReturnAcceptedStatusAndCallbackUrlAndIntervalWhenCallBoardReports() t doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); MockHttpServletResponse response = mockMvc - .perform(post("/reports/board").contentType(MediaType.APPLICATION_JSON) + .perform(post("/reports/v1/board").contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(request))) .andExpect(status().isAccepted()) .andReturn() @@ -189,7 +189,7 @@ void shouldGetExceptionAndPutInExceptionMapWhenCallBoardReport() throws Exceptio doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); MockHttpServletResponse response = mockMvc - .perform(post("/reports/board").contentType(MediaType.APPLICATION_JSON) + .perform(post("/reports/v1/board").contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(request))) .andExpect(status().isAccepted()) .andReturn() @@ -201,12 +201,12 @@ void shouldGetExceptionAndPutInExceptionMapWhenCallBoardReport() throws Exceptio assertEquals("10", interval); Thread.sleep(2000L); - verify(generateReporterService).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), + verify(generateReporterService, times(1)).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); verify(generateReporterService, times(0)).saveReporterInHandler(any(), any()); verify(generateReporterService, times(0)).updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); - verify(asyncExceptionHandler).put(IdUtil.getBoardReportId(currentTimeStamp), requestFailedException); + verify(asyncExceptionHandler, times(1)).put(IdUtil.getBoardReportId(currentTimeStamp), requestFailedException); } @Test @@ -225,7 +225,7 @@ void shouldGetExceptionAndPutInExceptionMapWhenCallDoraReport() throws Exception doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); MockHttpServletResponse response = mockMvc - .perform(post("/reports/dora").contentType(MediaType.APPLICATION_JSON) + .perform(post("/reports/v1/dora").contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(request))) .andExpect(status().isAccepted()) .andReturn() @@ -237,12 +237,12 @@ void shouldGetExceptionAndPutInExceptionMapWhenCallDoraReport() throws Exception assertEquals("10", interval); Thread.sleep(2000L); - verify(generateReporterService).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), + verify(generateReporterService, times(1)).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); verify(generateReporterService, times(0)).saveReporterInHandler(any(), any()); verify(generateReporterService, times(0)).updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); - verify(asyncExceptionHandler).put(IdUtil.getDoraReportId(currentTimeStamp), requestFailedException); + verify(asyncExceptionHandler, times(1)).put(IdUtil.getDoraReportId(currentTimeStamp), requestFailedException); } @Test @@ -265,6 +265,26 @@ void shouldReturnAcceptedStatusAndCallbackUrlAndIntervalWhenCallDoraReports() th when(generateReporterService.generateReporter(request.convertToReportRequest())).thenReturn(expectedResponse); + MockHttpServletResponse response = mockMvc + .perform(post("/reports/v1/dora").contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(request))) + .andExpect(status().isAccepted()) + .andReturn() + .getResponse(); + + final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); + final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); + assertEquals("/reports/" + currentTimeStamp, callbackUrl); + assertEquals("10", interval); + } + + @Test + void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateBoardReportWhenReportTypeIsBoard() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); + String currentTimeStamp = "1685010080107"; + request.setCsvTimeStamp(currentTimeStamp); + MockHttpServletResponse response = mockMvc .perform(post("/reports/dora").contentType(MediaType.APPLICATION_JSON) .content(mapper.writeValueAsString(request))) @@ -272,6 +292,33 @@ void shouldReturnAcceptedStatusAndCallbackUrlAndIntervalWhenCallDoraReports() th .andReturn() .getResponse(); + Thread.sleep(2000); + verify(generateReporterService, times(1)).generateDoraReport(request); + verify(generateReporterService, times(0)).generateBoardReport(request); + + final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); + final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); + assertEquals("/reports/" + currentTimeStamp, callbackUrl); + assertEquals("10", interval); + } + + @Test + void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateDoraReportWhenReportTypeIsBoard() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); + String currentTimeStamp = "1685010080107"; + request.setCsvTimeStamp(currentTimeStamp); + + MockHttpServletResponse response = mockMvc + .perform(post("/reports/board").contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(request))) + .andExpect(status().isAccepted()) + .andReturn() + .getResponse(); + + verify(generateReporterService, times(0)).generateDoraReport(request); + verify(generateReporterService, times(1)).generateBoardReport(request); + final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); assertEquals("/reports/" + currentTimeStamp, callbackUrl); diff --git a/backend/src/test/java/heartbeat/controller/report/dto/request/ReportTypeTest.java b/backend/src/test/java/heartbeat/controller/report/dto/request/ReportTypeTest.java new file mode 100644 index 0000000000..1966306d96 --- /dev/null +++ b/backend/src/test/java/heartbeat/controller/report/dto/request/ReportTypeTest.java @@ -0,0 +1,25 @@ +package heartbeat.controller.report.dto.request; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ReportTypeTest { + + @Test + public void shouldConvertValueToType() { + ReportType boardType = ReportType.fromValue("board"); + ReportType doraType = ReportType.fromValue("dora"); + + assertEquals(boardType, ReportType.BOARD); + assertEquals(doraType, ReportType.DORA); + } + + @Test + public void shouldThrowExceptionWhenDateTypeNotSupported() { + assertThatThrownBy(() -> ReportType.fromValue("unknown")).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ReportType not found!"); + } + +} diff --git a/backend/src/test/java/heartbeat/controller/report/request.json b/backend/src/test/java/heartbeat/controller/report/request.json index e35ab296be..4b30ad4d09 100644 --- a/backend/src/test/java/heartbeat/controller/report/request.json +++ b/backend/src/test/java/heartbeat/controller/report/request.json @@ -9,6 +9,7 @@ ], "startTime": 1661702400000, "endTime": 1662739199000, + "considerHoliday": true, "buildKiteSetting": { "type": "BuildKite", "token": "0d6*********************************a57a", @@ -36,6 +37,11 @@ } ] }, + "codebaseSetting": { + "type": "GitHub", + "token": "ghp*********************************FX", + "leadTime": [] + }, "jiraBoardSetting": { "type": "classic jira", "token": "Basic exxxxxhhbmdxxxxvYixxxxxxZzlxxxxxx3Rqaxxxxxlodxxxxx9BRUU3", diff --git a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java index 571d69a7ed..f1657c1315 100644 --- a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java @@ -103,13 +103,16 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; + +import static heartbeat.TestFixtures.BUILDKITE_TOKEN; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; - -import static heartbeat.TestFixtures.BUILDKITE_TOKEN; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.doThrow; import static org.mockito.internal.verification.VerificationModeFactory.times; @ExtendWith(MockitoExtension.class) @@ -118,12 +121,19 @@ class GenerateReporterServiceTest { public static final String SITE_ATLASSIAN_NET = "https://site.atlassian.net"; + private static final String REQUEST_FILE_PATH = "src/test/java/heartbeat/controller/report/request.json"; + + private static final String RESPONSE_FILE_PATH = "src/test/java/heartbeat/controller/report/reportResponse.json"; + @InjectMocks GenerateReporterService generateReporterService; @Mock JiraService jiraService; + @Mock + IdUtil idUtil; + @Mock WorkDay workDay; @@ -577,7 +587,7 @@ void shouldGenerateCsvForPipelineWithPipelineMetricAndBuildInfoIsEmpty() throws return null; }).when(csvFileGenerator).convertPipelineDataToCSV(any(), any()); - generateReporterService.generateReporter(request); + generateReporterService.generateDoraReport(request); boolean isExists = Files.exists(csvFilePath); Assertions.assertTrue(isExists); @@ -627,7 +637,7 @@ void shouldGenerateCsvForPipelineWithPipelineMetric() throws IOException { return null; }).when(csvFileGenerator).convertPipelineDataToCSV(any(), any()); - generateReporterService.generateReporter(request); + generateReporterService.generateDoraReport(request); boolean isExists = Files.exists(mockPipelineCsvPath); Assertions.assertTrue(isExists); @@ -673,7 +683,7 @@ void shouldNotGenerateCsvForPipelineWithPipelineLeadTimeIsNull() throws IOExcept when(gitHubService.fetchPipelinesLeadTime(any(), any(), any())).thenReturn(null); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("xx")); - generateReporterService.generateReporter(request); + generateReporterService.generateDoraReport(request); boolean isExists = Files.exists(mockPipelineCsvPath); Assertions.assertFalse(isExists); @@ -718,7 +728,7 @@ void shouldNotGenerateCsvForPipelineWithCommitIsNull() throws IOException { when(gitHubService.fetchPipelinesLeadTime(any(), any(), any())).thenReturn(null); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("xx")); - generateReporterService.generateReporter(request); + generateReporterService.generateDoraReport(request); boolean isExists = Files.exists(mockPipelineCsvPath); Assertions.assertFalse(isExists); @@ -762,7 +772,7 @@ void shouldNotGenerateCsvForPipeline() throws IOException { when(gitHubService.fetchPipelinesLeadTime(any(), any(), any())).thenReturn(null); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("xx")); - generateReporterService.generateReporter(request); + generateReporterService.generateDoraReport(request); boolean isExists = Files.exists(mockPipelineCsvPath); Assertions.assertFalse(isExists); @@ -1266,6 +1276,126 @@ void shouldPutReportInHandlerWhenCallSaveReporterInHandler() throws IOException verify(asyncReportRequestHandler, times(1)).putReport(reportId, reportResponse); } + @Test + void shouldPutBoardReportIntoHandlerWhenCallGenerateBoardReport() throws IOException, InterruptedException { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), + GenerateReportRequest.class); + ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); + reportRequest.setCsvTimeStamp("1683734399999"); + MetricsDataReady previousMetricsReady = MetricsDataReady.builder() + .isBoardMetricsReady(true) + .isPipelineMetricsReady(false) + .isSourceControlMetricsReady(false) + .build(); + GenerateReporterService spyGenerateReporterService = spy(generateReporterService); + + doReturn(reportResponse).when(spyGenerateReporterService).generateReporter(reportRequest); + when(asyncReportRequestHandler.getMetricsDataReady(reportRequest.getCsvTimeStamp())) + .thenReturn(previousMetricsReady); + + spyGenerateReporterService.generateBoardReport(reportRequest); + + Thread.sleep(2000); + verify(spyGenerateReporterService, times(1)).generateReporter(reportRequest); + verify(spyGenerateReporterService, times(1)) + .initializeMetricsDataReadyInHandler(reportRequest.getCsvTimeStamp(), reportRequest.getMetrics()); + verify(spyGenerateReporterService, times(1)).saveReporterInHandler( + spyGenerateReporterService.generateReporter(reportRequest), reportRequest.getCsvTimeStamp()); + verify(spyGenerateReporterService, times(1)).updateMetricsDataReadyInHandler(reportRequest.getCsvTimeStamp(), + reportRequest.getMetrics()); + } + + @Test + void shouldPutExceptionInHandlerWhenCallGenerateBoardReportThrowException() + throws IOException, InterruptedException { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), + GenerateReportRequest.class); + ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); + reportRequest.setCsvTimeStamp("1683734399999"); + String boardTimeStamp = "board-1683734399999"; + GenerateReporterService spyGenerateReporterService = spy(generateReporterService); + GenerateReportException e = new GenerateReportException( + "Failed to update metrics data ready through this timestamp."); + + doReturn(reportResponse).when(spyGenerateReporterService).generateReporter(reportRequest); + doThrow(e).when(spyGenerateReporterService).updateMetricsDataReadyInHandler(any(), any()); + when(asyncReportRequestHandler.getMetricsDataReady(reportRequest.getCsvTimeStamp())).thenReturn(null); + + spyGenerateReporterService.generateBoardReport(reportRequest); + + Thread.sleep(2000L); + verify(spyGenerateReporterService, times(1)) + .initializeMetricsDataReadyInHandler(reportRequest.getCsvTimeStamp(), reportRequest.getMetrics()); + verify(spyGenerateReporterService, times(1)) + .saveReporterInHandler(spyGenerateReporterService.generateReporter(reportRequest), boardTimeStamp); + verify(asyncExceptionHandler, times(1)).put(boardTimeStamp, e); + } + + @Test + void shouldGeneratePipelineReportAndUpdatePipelineMetricsReadyWhenCallGeneratePipelineReport() + throws IOException, InterruptedException { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), + GenerateReportRequest.class); + reportRequest.setMetrics(List.of("Deployment frequency")); + ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); + GenerateReporterService spyGenerateReporterService = spy(generateReporterService); + reportRequest.setCsvTimeStamp("1683734399999"); + String doraTimeStamp = "dora-1683734399999"; + MetricsDataReady previousMetricsReady = MetricsDataReady.builder() + .isBoardMetricsReady(null) + .isPipelineMetricsReady(false) + .isSourceControlMetricsReady(false) + .build(); + + doReturn(reportResponse).when(spyGenerateReporterService).generateReporter(reportRequest); + when(asyncReportRequestHandler.getMetricsDataReady(reportRequest.getCsvTimeStamp())) + .thenReturn(previousMetricsReady); + + spyGenerateReporterService.generateDoraReport(reportRequest); + + Thread.sleep(2000); + verify(spyGenerateReporterService, times(1)).generateReporter(any()); + verify(spyGenerateReporterService, times(1)).initializeMetricsDataReadyInHandler(any(), any()); + verify(spyGenerateReporterService, times(1)) + .saveReporterInHandler(spyGenerateReporterService.generateReporter(any()), doraTimeStamp); + verify(spyGenerateReporterService, times(1)).updateMetricsDataReadyInHandler(any(), any()); + } + + @Test + void shouldGenerateCodebaseReportAndUpdateCodebaseMetricsReadyWhenCallGeneratePipelineReport() + throws IOException, InterruptedException { + ObjectMapper mapper = new ObjectMapper(); + GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), + GenerateReportRequest.class); + reportRequest.setMetrics(List.of("Lead time for changes")); + ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); + GenerateReporterService spyGenerateReporterService = spy(generateReporterService); + reportRequest.setCsvTimeStamp("1683734399999"); + String codebaseTimeStamp = "github-1683734399999"; + MetricsDataReady previousMetricsReady = MetricsDataReady.builder() + .isBoardMetricsReady(null) + .isPipelineMetricsReady(false) + .isSourceControlMetricsReady(false) + .build(); + + doReturn(reportResponse).when(spyGenerateReporterService).generateReporter(reportRequest); + when(asyncReportRequestHandler.getMetricsDataReady(reportRequest.getCsvTimeStamp())) + .thenReturn(previousMetricsReady); + + spyGenerateReporterService.generateDoraReport(reportRequest); + + Thread.sleep(2000); + verify(spyGenerateReporterService, times(1)).generateReporter(any()); + verify(spyGenerateReporterService, times(1)).initializeMetricsDataReadyInHandler(any(), any()); + verify(spyGenerateReporterService, times(1)) + .saveReporterInHandler(spyGenerateReporterService.generateReporter(any()), codebaseTimeStamp); + verify(spyGenerateReporterService, times(1)).updateMetricsDataReadyInHandler(any(), any()); + + } + private JiraBoardSetting buildJiraBoardSetting() { return JiraBoardSetting.builder() .treatFlagCardAsBlock(true) From be52d0fa10a6e5650870796a1fc8dd6b3faa9a52 Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Tue, 9 Jan 2024 17:04:42 +0800 Subject: [PATCH 45/90] ADM-708:[backend]test: fix the failed message --- .../board/dto/request/BoardTypeTest.java | 4 ++++ .../service/jira/JiraServiceTest.java | 23 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/backend/src/test/java/heartbeat/controller/board/dto/request/BoardTypeTest.java b/backend/src/test/java/heartbeat/controller/board/dto/request/BoardTypeTest.java index 5cec2d13a1..50d9eadfd3 100644 --- a/backend/src/test/java/heartbeat/controller/board/dto/request/BoardTypeTest.java +++ b/backend/src/test/java/heartbeat/controller/board/dto/request/BoardTypeTest.java @@ -9,8 +9,12 @@ public class BoardTypeTest { public void shouldConvertValueToType() { BoardType jiraBoardType = BoardType.fromValue("jira"); BoardType classicJiraBoardType = BoardType.fromValue("classic-jira"); + BoardType jiraBoardStyle = BoardType.fromStyle("next-gen"); + BoardType classicJiraBoardStyle = BoardType.fromStyle("classic"); assertEquals(jiraBoardType, BoardType.JIRA); assertEquals(classicJiraBoardType, BoardType.CLASSIC_JIRA); + assertEquals(jiraBoardStyle, BoardType.JIRA); + assertEquals(classicJiraBoardStyle, BoardType.CLASSIC_JIRA); } } diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index f0c3464510..2dd57aeae0 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -103,9 +103,9 @@ class JiraServiceTest { public static final String SITE_ATLASSIAN_NET = "https://site.atlassian.net"; - private final BoardType boardTypeJira = BoardType.fromValue("jira"); + private final BoardType boardTypeJira = BoardType.fromStyle("next-gen"); - private final BoardType boardTypeClassicJira = BoardType.fromValue("classic-jira"); + private final BoardType boardTypeClassicJira = BoardType.fromStyle("classic"); private static final String ALL_CARDS_JQL = "status changed during (%s, %s)"; @@ -467,7 +467,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenVerifyJiraBoard() { Throwable thrown = catchThrowable(() -> jiraService.verify(boardTypeJira, boardVerifyRequestParam)); assertThat(thrown).isInstanceOf(InternalServerErrorException.class) - .hasMessageContaining("Failed when call Jira to verify board, cause is"); + .hasMessageContaining("Failed to call Jira to verify board, cause is"); } @Test @@ -596,7 +596,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenGetJiraBoardConfig() { jiraService.getJiraConfiguration(boardTypeJira, boardRequestParam); }); assertThat(thrown).isInstanceOf(InternalServerErrorException.class) - .hasMessageContaining("Failed when call Jira to get board config, cause is"); + .hasMessageContaining("Failed to call Jira to get board config, cause is"); } @Test @@ -620,7 +620,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenGetJiraBoardInfo() { jiraService.getInfo(boardTypeJira, boardRequestParam); }); assertThat(thrown).isInstanceOf(InternalServerErrorException.class) - .hasMessageContaining("Failed when call Jira to get board config, cause is"); + .hasMessageContaining("Failed to call Jira to get board config, cause is"); } @Test @@ -1006,6 +1006,19 @@ void shouldReturnBadRequestExceptionWhenBoardTypeIsNotCorrect() { .hasMessageContaining("Board type does not find!"); } + @Test + void shouldReturnBadRequestExceptionWhenBoardStyleIsNotCorrect() { + // given + BoardRequestParam boardRequestParam = BOARD_REQUEST_BUILDER().build(); + // when + when(jiraFeignClient.getProject(any(), any(), any())) + .thenReturn(JiraBoardProject.builder().style("unknown").build()); + // then + assertThatThrownBy(() -> jiraService.getInfo(boardTypeJira, boardRequestParam)) + .isInstanceOf(InternalServerErrorException.class) + .hasMessageContaining("Board type does not find!"); + } + @Test public void shouldProcessCustomFieldsForCardsWhenCallGetStoryPointsAndCycleTime() { URI baseUrl = URI.create(SITE_ATLASSIAN_NET); From 0a2a653b8a8bd678b5c020ce2e81f334c971d96c Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 11:04:15 +0800 Subject: [PATCH 46/90] ADM-652 [frontend] feat: add show more button --- .../Common/ReportGrid/ReportTitle/style.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 11 +++++++++-- .../Metrics/ReportStep/BoradMetrics/style.tsx | 13 +++++++++++++ frontend/src/theme.ts | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx index 987d6ab8eb..8d397b2e7a 100644 --- a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx @@ -4,6 +4,7 @@ import { Typography } from '@mui/material' export const StyledMetricsTitleSection = styled('div')({ display: 'flex', + alignItems: 'center', }) export const StyledMetricsSign = styled('canvas')({ @@ -18,7 +19,6 @@ export const StyledMetricsTitle = styled(Typography)({ fontSize: '1rem', fontFamily: theme.main.font.secondary, textAlign: 'start', - marginBottom: '1rem', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index d6bd0bd4c4..d2a0386206 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -12,7 +12,11 @@ import { import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' import { selectMetricsContent } from '@src/context/Metrics/metricsSlice' import dayjs from 'dayjs' -import { StyledMetricsSection } from '@src/components/Metrics/ReportStep/BoradMetrics/style' +import { + StyledMetricsSection, + StyledShowMore, + StyledTitleWrapper, +} from '@src/components/Metrics/ReportStep/BoradMetrics/style' import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' import { ReportGrid } from '@src/components/Common/ReportGrid' @@ -120,7 +124,10 @@ const BoardMetrics = ({ return ( <> - + + + {boardReport && {'show more >'}} + diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index 84eb460497..f9a67947bc 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,5 +1,18 @@ import { styled } from '@mui/material/styles' +import { theme } from '@src/theme' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', }) + +export const StyledTitleWrapper = styled('div')({ + display: 'flex', + alignItems: 'center', + marginBottom: '1rem', +}) + +export const StyledShowMore = styled('div')({ + marginLeft: '0.5rem', + fontSize: '0.8rem', + color: theme.palette.link, +}) diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index dc18bbbd9c..25c60945b0 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -77,6 +77,7 @@ export const theme = createTheme({ main: '#3498db', light: '#b9c4cc', }, + link: '#4350AF', }, main: { backgroundColor: indigo[FIVE_HUNDRED], From 7b86705677b7527771c7534bc8249db331c70333 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 14:38:13 +0800 Subject: [PATCH 47/90] ADM-652 [frontend] feat: add detail router --- .../Metrics/ReportStep/BoradMetrics/index.tsx | 12 ++++++++++-- frontend/src/config/routes.ts | 5 +++++ frontend/src/constants/router.ts | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index d2a0386206..13428326ec 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -21,6 +21,8 @@ import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/uti import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { useNavigate } from 'react-router-dom' +import { ROUTE } from '@src/constants/router' interface BoardMetricsProps { startToRequestBoardData: (request: ReportRequestDTO) => void @@ -38,13 +40,15 @@ const BoardMetrics = ({ endDate, }: BoardMetricsProps) => { const configData = useAppSelector(selectConfig) + const navigate = useNavigate() const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = useAppSelector(selectMetricsContent) + const jiraColumns = useAppSelector(selectJiraColumns) + const { metrics, calendarType } = configData.basic const { board } = configData const { token, type, site, projectKey, boardId, email } = board.config const jiraToken = getJiraBoardToken(token, email) - const jiraColumns = useAppSelector(selectJiraColumns) const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value ) @@ -121,12 +125,16 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) + const handleShowMore = () => { + navigate(ROUTE.METRICS_DETAIL_PAGE) + } + return ( <> - {boardReport && {'show more >'}} + {boardReport && {'show more >'}} diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index 87cb5760d5..c6c283160a 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -12,6 +12,11 @@ export const routes = [ component: lazy(() => import('../pages/Metrics')), name: 'Metrics', }, + { + path: 'detail', + component: lazy(() => import('../components/Metrics/ReportStep/ReportDetail')), + name: 'Detail', + }, { path: '/error-page', component: lazy(() => import('../pages/ErrorPage')), diff --git a/frontend/src/constants/router.ts b/frontend/src/constants/router.ts index f421479251..fc715eaecb 100644 --- a/frontend/src/constants/router.ts +++ b/frontend/src/constants/router.ts @@ -2,4 +2,5 @@ export enum ROUTE { BASE_PAGE = '/', ERROR_PAGE = '/error-page', METRICS_PAGE = '/metrics', + METRICS_DETAIL_PAGE = '/detail', } From e0576b821170084ab26ebf60e61cd575e33eca60 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 15:51:46 +0800 Subject: [PATCH 48/90] ADM-652 [frontend] feat: add detail page --- .../Metrics/ReportButtonGroup/index.tsx | 32 +- .../Metrics/ReportStep/ReportDetail/index.tsx | 449 ++++++------------ .../components/Metrics/ReportStep/index.tsx | 10 +- frontend/src/context/config/configSlice.ts | 6 +- frontend/src/context/report/reportSlice.ts | 30 ++ frontend/src/hooks/useGenerateReportEffect.ts | 7 +- frontend/src/store.ts | 2 + 7 files changed, 210 insertions(+), 326 deletions(-) create mode 100644 frontend/src/context/report/reportSlice.ts diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 4ba10cd339..d20887ecaf 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -1,5 +1,5 @@ import { Tooltip } from '@mui/material' -import { REQUIRED_DATA, TIPS } from '@src/constants/resources' +import { TIPS } from '@src/constants/resources' import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style' import SaveAltIcon from '@mui/icons-material/SaveAlt' import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons' @@ -9,7 +9,7 @@ import { backStep } from '@src/context/stepper/StepperSlice' import { useAppDispatch } from '@src/hooks/useAppDispatch' import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' import { useAppSelector } from '@src/hooks' -import { selectMetrics } from '@src/context/config/configSlice' +import { isSelectBoardMetrics, isSelectDoraMetrics } from '@src/context/config/configSlice' import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' import { StyledButtonGroup, @@ -19,13 +19,13 @@ import { import { ReportResponseDTO } from '@src/clients/report/dto/response' interface ReportButtonGroupProps { - handleSave: () => void + handleSave?: () => void csvTimeStamp: number startDate: string endDate: string setErrorMessage: (message: string) => void - shouldShowBoardExportButton: boolean reportData: ReportResponseDTO | undefined + isShowSaveButton?: boolean } export const ReportButtonGroup = ({ @@ -35,16 +35,12 @@ export const ReportButtonGroup = ({ endDate, setErrorMessage, reportData, - shouldShowBoardExportButton, + isShowSaveButton = true, }: ReportButtonGroupProps) => { const dispatch = useAppDispatch() const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() - const requiredData = useAppSelector(selectMetrics) - const isShowExportPipelineButton = - requiredData.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) || - requiredData.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) || - requiredData.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES) || - requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) + const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) + const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) useEffect(() => { setErrorMessage(errorMessage) @@ -68,11 +64,13 @@ export const ReportButtonGroup = ({ return ( <> - - }> - {COMMON_BUTTONS.SAVE} - - + {isShowSaveButton && ( + + }> + {COMMON_BUTTONS.SAVE} + + + )} {COMMON_BUTTONS.BACK} @@ -83,7 +81,7 @@ export const ReportButtonGroup = ({ > {COMMON_BUTTONS.EXPORT_METRIC_DATA} - {shouldShowBoardExportButton && ( + {isShowExportBoardButton && ( handleDownload(DOWNLOAD_TYPES.BOARD, startDate, endDate)} diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 3f60fa0dab..b04bb55f3a 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,301 +1,148 @@ -// import { useEffect, useLayoutEffect, useState } from 'react' -// import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' -// import { Loading } from '@src/components/Loading' -// import { useAppSelector } from '@src/hooks' -// import { selectConfig, selectJiraColumns, selectMetrics } from '@src/context/config/configSlice' -// import { CALENDAR, PIPELINE_STEP, NAME, REQUIRED_DATA, MESSAGE, TIPS } from '@src/constants/resources' -// import { -// COMMON_BUTTONS, -// DOWNLOAD_TYPES, -// INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// INIT_REPORT_DATA_WITH_TWO_COLUMNS, -// } from '@src/constants/commons' -// import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -// import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -// import { CSVReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' -// import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice' -// import dayjs from 'dayjs' -// import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style' -// import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -// import { backStep, selectTimeStamp } from '@src/context/stepper/StepperSlice' -// import { useAppDispatch } from '@src/hooks/useAppDispatch' -// import { ErrorNotification } from '@src/components/ErrorNotification' -// import { useNavigate } from 'react-router-dom' -// import CollectionDuration from '@src/components/Common/CollectionDuration' -// import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' -// import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' -// import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -// import { ReportResponse } from '@src/clients/report/dto/response' -// import { ROUTE } from '@src/constants/router' -// import { Tooltip } from '@mui/material' -// import SaveAltIcon from '@mui/icons-material/SaveAlt' -// import { -// ButtonGroupStyle, -// ErrorNotificationContainer, -// ExportButton, -// } from '@src/components/Metrics/ReportStep/ReportDetail/style' -// -// interface ReportDetailInterface extends useNotificationLayoutEffectInterface { -// handleSave: () => void -// } -// -// const ReportDetail = ({ updateProps, handleSave }: ReportDetailInterface) => { -// const dispatch = useAppDispatch() -// const navigate = useNavigate() -// const { -// startPollingReports, -// stopPollingReports, -// reports, -// isLoading, -// isServerError, -// errorMessage: reportErrorMsg, -// } = useGenerateReportEffect() -// const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() -// const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) -// const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) -// const [classificationState, setClassificationState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [changeFailureRateState, setChangeFailureRateState] = useState({ -// value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, -// isShow: false, -// }) -// const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) -// const csvTimeStamp = useAppSelector(selectTimeStamp) -// const configData = useAppSelector(selectConfig) -// const { -// cycleTimeSettings, -// treatFlagCardAsBlock, -// users, -// pipelineCrews, -// targetFields, -// doneColumn, -// deploymentFrequencySettings, -// leadTimeForChanges, -// assigneeFilter, -// } = useAppSelector(selectMetricsContent) -// const { metrics, calendarType, dateRange } = configData.basic -// const { board, pipelineTool, sourceControl } = configData -// const { token, type, site, projectKey, boardId, email } = board.config -// const { startDate, endDate } = dateRange -// const requiredData = useAppSelector(selectMetrics) -// const isShowExportBoardButton = -// requiredData.includes(REQUIRED_DATA.VELOCITY) || -// requiredData.includes(REQUIRED_DATA.CYCLE_TIME) || -// requiredData.includes(REQUIRED_DATA.CLASSIFICATION) -// const isShowExportPipelineButton = -// requiredData.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) || -// requiredData.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) || -// requiredData.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES) || -// requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) -// -// const getPipelineConfig = (pipelineConfigs: IPipelineConfig[]) => { -// if (!pipelineConfigs[0].organization && pipelineConfigs.length === 1) { -// return [] -// } -// return pipelineConfigs.map(({ organization, pipelineName, step, branches }) => { -// const pipelineConfigFromPipelineList = configData.pipelineTool.verifiedResponse.pipelineList.find( -// (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organization -// ) -// if (pipelineConfigFromPipelineList != undefined) { -// const { orgName, orgId, name, id, repository } = pipelineConfigFromPipelineList -// return { -// orgId, -// orgName, -// id, -// name, -// step, -// repository, -// branches, -// } -// } -// }) as { -// id: string -// name: string -// orgId: string -// orgName: string -// repository: string -// step: string -// branches: string[] -// }[] -// } -// -// const jiraColumns = useAppSelector(selectJiraColumns) -// const jiraColumnsWithValue = jiraColumns?.map( -// (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value -// ) -// -// const jiraToken = getJiraBoardToken(token, email) -// const getReportRequestBody = (): ReportRequestDTO => ({ -// metrics: metrics, -// startTime: dayjs(startDate).valueOf().toString(), -// endTime: dayjs(endDate).valueOf().toString(), -// considerHoliday: calendarType === CALENDAR.CHINA, -// buildKiteSetting: { -// pipelineCrews, -// ...pipelineTool.config, -// deploymentEnvList: getPipelineConfig(deploymentFrequencySettings), -// }, -// codebaseSetting: { -// type: sourceControl.config.type, -// token: sourceControl.config.token, -// leadTime: getPipelineConfig(leadTimeForChanges), -// }, -// jiraBoardSetting: { -// token: jiraToken, -// type: type.toLowerCase().replace(' ', '-'), -// site, -// projectKey, -// boardId, -// boardColumns: filterAndMapCycleTimeSettings(cycleTimeSettings, jiraColumnsWithValue), -// treatFlagCardAsBlock, -// users, -// assigneeFilter, -// targetFields, -// doneColumn, -// }, -// csvTimeStamp: csvTimeStamp, -// }) -// -// const getExportCSV = ( -// dataType: DOWNLOAD_TYPES, -// startDate: string | null, -// endDate: string | null -// ): CSVReportRequestDTO => ({ -// dataType: dataType, -// csvTimeStamp: csvTimeStamp, -// startDate: startDate ?? '', -// endDate: endDate ?? '', -// }) -// -// useEffect(() => { -// startPollingReports(getReportRequestBody()) -// }, []) -// -// useEffect(() => { -// updateReportData(reports) -// return () => { -// stopPollingReports() -// } -// }, [reports]) -// -// const updateReportData = (res: ReportResponse | undefined) => { -// res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) -// res?.cycleTimeList && setCycleTimeState({ ...cycleTimeState, value: res.cycleTimeList, isShow: true }) -// res?.classification && setClassificationState({ value: res.classification, isShow: true }) -// res?.deploymentFrequencyList && -// setDeploymentFrequencyState({ -// ...deploymentFrequencyState, -// value: res.deploymentFrequencyList, -// isShow: true, -// }) -// res?.meanTimeToRecoveryList && -// setMeanTimeToRecoveryState({ -// ...meanTimeToRecoveryState, -// value: res.meanTimeToRecoveryList, -// isShow: true, -// }) -// res?.changeFailureRateList && -// setChangeFailureRateState({ -// ...changeFailureRateState, -// value: res.changeFailureRateList, -// isShow: true, -// }) -// res?.leadTimeForChangesList && -// setLeadTimeForChangesState({ -// ...leadTimeForChangesState, -// value: res.leadTimeForChangesList, -// isShow: true, -// }) -// res?.exportValidityTimeMin && setExportValidityTimeMin(res.exportValidityTimeMin) -// } -// -// const handleDownload = (dataType: DOWNLOAD_TYPES, startDate: string | null, endDate: string | null) => { -// fetchExportData(getExportCSV(dataType, startDate, endDate)) -// } -// -// const handleBack = () => { -// dispatch(backStep()) -// } -// -// return ( -// <> -// {isLoading ? ( -// -// ) : isServerError ? ( -// navigate(ROUTE.ERROR_PAGE) -// ) : ( -// <> -// {startDate && endDate && } -// {reportErrorMsg && ( -// -// -// -// )} -// {errorMessage && ( -// -// -// -// )} -// {velocityState.isShow && } -// {cycleTimeState.isShow && } -// {classificationState.isShow && ( -// -// )} -// {deploymentFrequencyState.isShow && ( -// -// )} -// {leadTimeForChangesState.isShow && ( -// -// )} -// {changeFailureRateState.isShow && ( -// -// )} -// {meanTimeToRecoveryState.isShow && ( -// -// )} -// -// )} -// -// ) -// } -// -// export default ReportDetail +import React, { useEffect, useState } from 'react' +import { useAppSelector } from '@src/hooks' +import { selectConfig } from '@src/context/config/configSlice' +import { PIPELINE_STEP, NAME } from '@src/constants/resources' +import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' +import { selectTimeStamp } from '@src/context/stepper/StepperSlice' +import { ErrorNotification } from '@src/components/ErrorNotification' +import CollectionDuration from '@src/components/Common/CollectionDuration' +import { ReportResponse } from '@src/clients/report/dto/response' +import { ErrorNotificationContainer } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { selectReportData } from '@src/context/report/reportSlice' +import { reportMapper } from '@src/hooks/reportMapper/report' +import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' +import Header from '@src/layouts/Header' + +const ReportDetail = () => { + const [errorMessage, setErrorMessage] = useState() + const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) + const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) + const [classificationState, setClassificationState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [deploymentFrequencyState, setDeploymentFrequencyState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [meanTimeToRecoveryState, setMeanTimeToRecoveryState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [leadTimeForChangesState, setLeadTimeForChangesState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const [changeFailureRateState, setChangeFailureRateState] = useState({ + value: INIT_REPORT_DATA_WITH_THREE_COLUMNS, + isShow: false, + }) + const configData = useAppSelector(selectConfig) + const { dateRange } = configData.basic + const { startDate, endDate } = dateRange + const csvTimeStamp = useAppSelector(selectTimeStamp) + + const reportData = useAppSelector(selectReportData) + + useEffect(() => { + updateReportData(reportMapper(reportData)) + }) + + const updateReportData = (res: ReportResponse | undefined) => { + res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) + res?.cycleTimeList && setCycleTimeState({ ...cycleTimeState, value: res.cycleTimeList, isShow: true }) + res?.classification && setClassificationState({ value: res.classification, isShow: true }) + res?.deploymentFrequencyList && + setDeploymentFrequencyState({ + ...deploymentFrequencyState, + value: res.deploymentFrequencyList, + isShow: true, + }) + res?.meanTimeToRecoveryList && + setMeanTimeToRecoveryState({ + ...meanTimeToRecoveryState, + value: res.meanTimeToRecoveryList, + isShow: true, + }) + res?.changeFailureRateList && + setChangeFailureRateState({ + ...changeFailureRateState, + value: res.changeFailureRateList, + isShow: true, + }) + res?.leadTimeForChangesList && + setLeadTimeForChangesState({ + ...leadTimeForChangesState, + value: res.leadTimeForChangesList, + isShow: true, + }) + } + + return ( + <> +
+ {startDate && endDate && } + {errorMessage && ( + + + + )} + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} + /> + + ) +} + +export default ReportDetail diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index 04d82f1e8a..0ae8faa0a8 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useLayoutEffect, useState } from 'react' import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' import { useAppSelector } from '@src/hooks' -import { selectConfig } from '@src/context/config/configSlice' -import { BOARD_METRICS, DORA_METRICS, MESSAGE } from '@src/constants/resources' +import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' +import { MESSAGE } from '@src/constants/resources' import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' import { ErrorNotification } from '@src/components/ErrorNotification' import { useNavigate } from 'react-router-dom' @@ -39,9 +39,8 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { const { updateProps } = notification const [errorMessage, setErrorMessage] = useState() - const { metrics } = configData.basic - const shouldShowBoardMetrics = metrics.some((metric) => BOARD_METRICS.includes(metric)) - const shouldShowDoraMetrics = metrics.some((metric) => DORA_METRICS.includes(metric)) + const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics) + const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics) useLayoutEffect(() => { exportValidityTimeMin && @@ -125,7 +124,6 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { handleSave()} reportData={reportData} - shouldShowBoardExportButton={shouldShowBoardMetrics} startDate={startDate} endDate={endDate} csvTimeStamp={csvTimeStamp} diff --git a/frontend/src/context/config/configSlice.ts b/frontend/src/context/config/configSlice.ts index 256f747c47..eac7b16ce8 100644 --- a/frontend/src/context/config/configSlice.ts +++ b/frontend/src/context/config/configSlice.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit' import type { RootState } from '@src/store' -import { CALENDAR, MESSAGE } from '@src/constants/resources' +import { BOARD_METRICS, CALENDAR, DORA_METRICS, MESSAGE } from '@src/constants/resources' import { REQUIRED_DATA } from '@src/constants/resources' import { IBoardState, initialBoardState } from '@src/context/config/board/boardSlice' import { initialPipelineToolState, IPipelineToolState } from '@src/context/config/pipelineTool/pipelineToolSlice' @@ -183,6 +183,10 @@ export const selectProjectName = (state: RootState) => state.config.basic.projec export const selectCalendarType = (state: RootState) => state.config.basic.calendarType export const selectDateRange = (state: RootState) => state.config.basic.dateRange export const selectMetrics = (state: RootState) => state.config.basic.metrics +export const isSelectBoardMetrics = (state: RootState) => + state.config.basic.metrics.some((metric) => BOARD_METRICS.includes(metric)) +export const isSelectDoraMetrics = (state: RootState) => + state.config.basic.metrics.some((metric) => DORA_METRICS.includes(metric)) export const selectBoard = (state: RootState) => state.config.board.config export const isPipelineToolVerified = (state: RootState) => state.config.pipelineTool.isVerified export const selectPipelineTool = (state: RootState) => state.config.pipelineTool.config diff --git a/frontend/src/context/report/reportSlice.ts b/frontend/src/context/report/reportSlice.ts new file mode 100644 index 0000000000..5152163c24 --- /dev/null +++ b/frontend/src/context/report/reportSlice.ts @@ -0,0 +1,30 @@ +import { createSlice } from '@reduxjs/toolkit' +import type { RootState } from '@src/store' +import { ReportResponseDTO } from '@src/clients/report/dto/response' + +export interface ReportState { + reportData: ReportResponseDTO | null +} + +export const initialBoardState: ReportState = { + reportData: null, +} + +export const reportSlice = createSlice({ + name: 'report', + initialState: { + ...initialBoardState, + }, + reducers: { + updateReportData: (state, action) => { + state.reportData = action.payload + }, + }, +}) +export const { updateReportData } = reportSlice.actions + +export const selectReportData = (state: RootState) => { + return state.report.reportData +} + +export default reportSlice.reducer diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 5d0210b91b..21d2ca1ab7 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -6,6 +6,8 @@ import { InternalServerException } from '@src/exceptions/InternalServerException import { ReportResponseDTO } from '@src/clients/report/dto/response' import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons' import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' +import { useAppDispatch } from '@src/hooks/useAppDispatch' +import { updateReportData } from '@src/context/report/reportSlice' export interface useGenerateReportEffectInterface { startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void @@ -18,6 +20,7 @@ export interface useGenerateReportEffectInterface { export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const reportPath = '/reports' + const dispatch = useAppDispatch() const [isServerError, setIsServerError] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [reportData, setReportData] = useState() @@ -88,7 +91,9 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const handleAndUpdateData = (response: ReportResponseDTO) => { const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime) - setReportData({ ...response, exportValidityTime: exportValidityTime }) + const report = { ...response, exportValidityTime: exportValidityTime } + setReportData(report) + dispatch(updateReportData(report)) } return { diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 385fba18a7..6296065eba 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -3,6 +3,7 @@ import stepperReducer from './context/stepper/StepperSlice' import configReducer from './context/config/configSlice' import metricsSlice from './context/Metrics/metricsSlice' import headerSlice from '@src/context/header/headerSlice' +import reportSlice from '@src/context/report/reportSlice' export const store = configureStore({ reducer: { @@ -10,6 +11,7 @@ export const store = configureStore({ config: configReducer, metrics: metricsSlice, header: headerSlice, + report: reportSlice, }, }) From cb1d4cea935c09060076be8f019d6df3af714422 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 3 Jan 2024 16:05:38 +0800 Subject: [PATCH 49/90] ADM-652 [frontend] feat: add deep for useEffect --- .../src/components/Metrics/ReportStep/ReportDetail/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index b04bb55f3a..0be08bca67 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -16,7 +16,7 @@ import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' const ReportDetail = () => { - const [errorMessage, setErrorMessage] = useState() + const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [classificationState, setClassificationState] = useState({ @@ -48,7 +48,7 @@ const ReportDetail = () => { useEffect(() => { updateReportData(reportMapper(reportData)) - }) + }, [reportData]) const updateReportData = (res: ReportResponse | undefined) => { res?.velocityList && setVelocityState({ ...velocityState, value: res.velocityList, isShow: true }) From ac13a1f34609e3dfa7d471aeafd22d32d61414eb Mon Sep 17 00:00:00 2001 From: xuebing Date: Thu, 4 Jan 2024 11:09:55 +0800 Subject: [PATCH 50/90] ADM-652 [frontend] feat: enhance style for detail page --- .../Metrics/ReportStep/ReportDetail/index.tsx | 108 +++++++++--------- .../Metrics/ReportStep/ReportDetail/style.tsx | 20 +--- 2 files changed, 59 insertions(+), 69 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 0be08bca67..a2cc94cf52 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -7,9 +7,8 @@ import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import { ErrorNotification } from '@src/components/ErrorNotification' -import CollectionDuration from '@src/components/Common/CollectionDuration' import { ReportResponse } from '@src/clients/report/dto/response' -import { ErrorNotificationContainer } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { ErrorNotificationContainer, StyledTableWrapper } from '@src/components/Metrics/ReportStep/ReportDetail/style' import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' @@ -83,64 +82,65 @@ const ReportDetail = () => { return ( <>
- {startDate && endDate && } {errorMessage && ( )} - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} /> - )} - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - { - setErrorMessage(message) - }} - csvTimeStamp={csvTimeStamp} - /> + ) } diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx index 709f0d816c..472aa7c78c 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx @@ -1,20 +1,5 @@ import { styled } from '@mui/material/styles' -import { Button } from '@mui/material' -import { theme } from '@src/theme' import { Z_INDEX } from '@src/constants/commons' -import { basicButtonStyle } from '@src/components/Metrics/ReportStep/style' - -export const ExportButton = styled(Button)({ - ...basicButtonStyle, - width: '12rem', - backgroundColor: theme.main.backgroundColor, - color: theme.main.color, - '&:hover': { - ...basicButtonStyle, - backgroundColor: theme.main.backgroundColor, - color: theme.main.color, - }, -}) export const ErrorNotificationContainer = styled('div')({ position: 'fixed', @@ -24,3 +9,8 @@ export const ErrorNotificationContainer = styled('div')({ zIndex: Z_INDEX.MODAL_BACKDROP, width: '80%', }) + +export const StyledTableWrapper = styled('div')({ + width: '80%', + margin: '2rem auto', +}) From dd7abc3863d19c4742cc0ebc08b7fc8782609fb4 Mon Sep 17 00:00:00 2001 From: xuebing Date: Thu, 4 Jan 2024 14:31:57 +0800 Subject: [PATCH 51/90] ADM-652 [frontend] feat: enhance button style --- .../Metrics/ReportButtonGroup/index.tsx | 2 +- .../Metrics/ReportButtonGroup/style.tsx | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index d20887ecaf..9694c05e5c 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -63,7 +63,7 @@ export const ReportButtonGroup = ({ return ( <> - + {isShowSaveButton && ( }> diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index 6a1745465f..c0e764bed9 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -12,16 +12,16 @@ export const StyledRightButtonGroup = styled('div')({ }, }) -export const StyledButtonGroup = styled('div')({ - boxSizing: 'border-box', - display: 'flex', - alignItems: 'center', - textAlign: 'center', - margin: '0 auto', - justifyContent: 'space-between', - width: '100%', - paddingTop: '2rem', -}) +export const StyledButtonGroup = styled('div')` + box-sizing: border-box; + display: flex; + align-items: center; + text-align: center; + margin: 0 auto; + justify-content: ${(props) => (props.isShowSaveButton ? 'space-between' : 'flex-end')}; + width: 100%; + padding-top: 2rem; +` export const StyledExportButton = styled(Button)({ ...basicButtonStyle, From eb6ef71031887061ff97af192bf98e6c52037eb8 Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 09:36:18 +0800 Subject: [PATCH 52/90] ADM-652 [frontend] feat: enhance router logic --- .../Metrics/ReportButtonGroup/index.tsx | 12 ++- .../Metrics/ReportButtonGroup/style.tsx | 2 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 9 +- .../Metrics/ReportStep/BoradMetrics/style.tsx | 4 +- .../Metrics/ReportStep/ReportDetail/index.tsx | 100 ++++++++++-------- 5 files changed, 72 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 9694c05e5c..1be0f8c666 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -17,6 +17,7 @@ import { StyledRightButtonGroup, } from '@src/components/Metrics/ReportButtonGroup/style' import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { useNavigate } from 'react-router-dom' interface ReportButtonGroupProps { handleSave?: () => void @@ -25,7 +26,7 @@ interface ReportButtonGroupProps { endDate: string setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined - isShowSaveButton?: boolean + isFromDetailPage?: boolean } export const ReportButtonGroup = ({ @@ -35,9 +36,10 @@ export const ReportButtonGroup = ({ endDate, setErrorMessage, reportData, - isShowSaveButton = true, + isFromDetailPage = false, }: ReportButtonGroupProps) => { const dispatch = useAppDispatch() + const navigate = useNavigate() const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() const isShowExportBoardButton = useAppSelector(isSelectBoardMetrics) const isShowExportPipelineButton = useAppSelector(isSelectDoraMetrics) @@ -58,13 +60,13 @@ export const ReportButtonGroup = ({ } const handleBack = () => { - dispatch(backStep()) + isFromDetailPage ? navigate(-1) : dispatch(backStep()) } return ( <> - - {isShowSaveButton && ( + + {!isFromDetailPage && ( }> {COMMON_BUTTONS.SAVE} diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index c0e764bed9..d33b638e68 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -18,7 +18,7 @@ export const StyledButtonGroup = styled('div')` align-items: center; text-align: center; margin: 0 auto; - justify-content: ${(props) => (props.isShowSaveButton ? 'space-between' : 'flex-end')}; + justify-content: ${(props) => (props.isFromDetailPage ? 'flex-end' : 'space-between')}; width: 100%; padding-top: 2rem; ` diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 13428326ec..364b8223c5 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -23,6 +23,7 @@ import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' import { useNavigate } from 'react-router-dom' import { ROUTE } from '@src/constants/router' +import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' interface BoardMetricsProps { startToRequestBoardData: (request: ReportRequestDTO) => void @@ -125,16 +126,14 @@ const BoardMetrics = ({ startToRequestBoardData(getBoardReportRequestBody()) }, []) - const handleShowMore = () => { - navigate(ROUTE.METRICS_DETAIL_PAGE) - } - return ( <> - {boardReport && {'show more >'}} + + {'show more >'} + diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index f9a67947bc..015987f1c7 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,5 +1,6 @@ import { styled } from '@mui/material/styles' import { theme } from '@src/theme' +import { Link } from 'react-router-dom' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', @@ -11,8 +12,9 @@ export const StyledTitleWrapper = styled('div')({ marginBottom: '1rem', }) -export const StyledShowMore = styled('div')({ +export const StyledShowMore = styled(Link)({ marginLeft: '0.5rem', fontSize: '0.8rem', + textDecoration: 'none', color: theme.palette.link, }) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index a2cc94cf52..ea58bf481f 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,8 +1,12 @@ import React, { useEffect, useState } from 'react' import { useAppSelector } from '@src/hooks' import { selectConfig } from '@src/context/config/configSlice' -import { PIPELINE_STEP, NAME } from '@src/constants/resources' -import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS } from '@src/constants/commons' +import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { + INIT_REPORT_DATA_WITH_THREE_COLUMNS, + INIT_REPORT_DATA_WITH_TWO_COLUMNS, + RETRIEVE_REPORT_TYPES, +} from '@src/constants/commons' import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' @@ -13,8 +17,10 @@ import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' +import { useLocation } from 'react-router-dom' const ReportDetail = () => { + const { state } = useLocation() const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) @@ -88,50 +94,58 @@ const ReportDetail = () => { )} - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - + {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + <> + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + )} - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - + {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + <> + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + )} Date: Fri, 5 Jan 2024 10:15:35 +0800 Subject: [PATCH 53/90] ADM-652 [frontend] feat: add Breadcrumbs --- .../Metrics/ReportStep/ReportDetail/index.tsx | 158 ++++++++++-------- .../Metrics/ReportStep/ReportDetail/style.tsx | 11 +- 2 files changed, 100 insertions(+), 69 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index ea58bf481f..03e0a7a0af 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -12,15 +12,22 @@ import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' import { selectTimeStamp } from '@src/context/stepper/StepperSlice' import { ErrorNotification } from '@src/components/ErrorNotification' import { ReportResponse } from '@src/clients/report/dto/response' -import { ErrorNotificationContainer, StyledTableWrapper } from '@src/components/Metrics/ReportStep/ReportDetail/style' +import { + ErrorNotificationContainer, + StyledContainer, + StyledNavigator, + StyledTableWrapper, +} from '@src/components/Metrics/ReportStep/ReportDetail/style' import { selectReportData } from '@src/context/report/reportSlice' import { reportMapper } from '@src/hooks/reportMapper/report' import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' import Header from '@src/layouts/Header' -import { useLocation } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' +import { Breadcrumbs, Link, Typography } from '@mui/material' const ReportDetail = () => { const { state } = useLocation() + const navigate = useNavigate() const [errorMessage, setErrorMessage] = useState('') const [velocityState, setVelocityState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) const [cycleTimeState, setCycleTimeState] = useState({ value: INIT_REPORT_DATA_WITH_TWO_COLUMNS, isShow: false }) @@ -85,76 +92,91 @@ const ReportDetail = () => { }) } + const handleBack = (event) => { + event.preventDefault() + navigate(-1) + } + return ( <>
- {errorMessage && ( - - - - )} - - {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( - <> - {velocityState.isShow && } - {cycleTimeState.isShow && } - {classificationState.isShow && ( - - )} - - )} - {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( - <> - {deploymentFrequencyState.isShow && ( - - )} - {leadTimeForChangesState.isShow && ( - - )} - {changeFailureRateState.isShow && ( - - )} - {meanTimeToRecoveryState.isShow && ( - - )} - + + {errorMessage && ( + + + )} - { - setErrorMessage(message) - }} - csvTimeStamp={csvTimeStamp} - /> - + + + + Report + + board + + + + {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + <> + {velocityState.isShow && } + {cycleTimeState.isShow && } + {classificationState.isShow && ( + + )} + + )} + {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + <> + {deploymentFrequencyState.isShow && ( + + )} + {leadTimeForChangesState.isShow && ( + + )} + {changeFailureRateState.isShow && ( + + )} + {meanTimeToRecoveryState.isShow && ( + + )} + + )} + { + setErrorMessage(message) + }} + csvTimeStamp={csvTimeStamp} + /> + + ) } diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx index 472aa7c78c..320cb82993 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx @@ -11,6 +11,15 @@ export const ErrorNotificationContainer = styled('div')({ }) export const StyledTableWrapper = styled('div')({ + width: '100%', + margin: '2rem 0', +}) + +export const StyledNavigator = styled('div')({ + margin: '1rem 0', +}) + +export const StyledContainer = styled('div')({ width: '80%', - margin: '2rem auto', + margin: '0 auto', }) From 4345e21979dd07227cabdeb514d7fa5b91b415bc Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 10:44:40 +0800 Subject: [PATCH 54/90] ADM-652 [frontend] feat: extra REPORT_METRICS const --- .../Metrics/ReportStep/ReportDetail/index.tsx | 24 +++++++++++-------- frontend/src/constants/resources.ts | 6 +++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx index 03e0a7a0af..1a52505811 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react' import { useAppSelector } from '@src/hooks' import { selectConfig } from '@src/context/config/configSlice' -import { NAME, PIPELINE_STEP } from '@src/constants/resources' +import { NAME, PIPELINE_STEP, REPORT_METRICS, REQUIRED_DATA } from '@src/constants/resources' import { INIT_REPORT_DATA_WITH_THREE_COLUMNS, INIT_REPORT_DATA_WITH_TWO_COLUMNS, @@ -109,19 +109,23 @@ const ReportDetail = () => { - Report + {REPORT_METRICS.REPORT} - board + {REPORT_METRICS.BOARD} {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( <> - {velocityState.isShow && } - {cycleTimeState.isShow && } + {velocityState.isShow && ( + + )} + {cycleTimeState.isShow && ( + + )} {classificationState.isShow && ( { <> {deploymentFrequencyState.isShow && ( { )} {leadTimeForChangesState.isShow && ( { )} {changeFailureRateState.isShow && ( { )} {meanTimeToRecoveryState.isShow && ( Date: Fri, 5 Jan 2024 11:00:05 +0800 Subject: [PATCH 55/90] ADM-652 [frontend] feat: add show more for Dora metrics --- .../Metrics/ReportStep/DoraMetrics/index.tsx | 10 +++++++++- .../Metrics/ReportStep/DoraMetrics/style.tsx | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index c8ab222eec..0c8984ec1a 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -18,6 +18,9 @@ import { ReportGrid } from '@src/components/Common/ReportGrid' import { ReportResponseDTO } from '@src/clients/report/dto/response' import { StyledSpacing } from '@src/components/Metrics/ReportStep/style' import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util' +import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style' +import { ROUTE } from '@src/constants/router' +import { RETRIEVE_REPORT_TYPES } from '@src/constants/commons' interface DoraMetricsProps { startToRequestDoraData: (request: ReportRequestDTO) => void @@ -168,7 +171,12 @@ const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDa return ( <> - + + + + {'show more >'} + + {shouldShowSourceControl && } diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index 84eb460497..d400534e26 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -1,5 +1,20 @@ import { styled } from '@mui/material/styles' +import { Link } from 'react-router-dom' +import { theme } from '@src/theme' export const StyledMetricsSection = styled('div')({ marginTop: '2rem', }) + +export const StyledTitleWrapper = styled('div')({ + display: 'flex', + alignItems: 'center', + marginBottom: '1rem', +}) + +export const StyledShowMore = styled(Link)({ + marginLeft: '0.5rem', + fontSize: '0.8rem', + textDecoration: 'none', + color: theme.palette.link, +}) From 6166e64ded1857cfef4a147b7928e993051666cd Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 11:02:42 +0800 Subject: [PATCH 56/90] ADM-652 [frontend] feat: add SHOW_MORE constant --- .../src/components/Metrics/ReportStep/BoradMetrics/index.tsx | 3 ++- .../src/components/Metrics/ReportStep/DoraMetrics/index.tsx | 3 ++- frontend/src/constants/resources.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 364b8223c5..d14e334f3e 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -8,6 +8,7 @@ import { REPORT_PAGE, METRICS_TITLE, REQUIRED_DATA, + SHOW_MORE, } from '@src/constants/resources' import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' import { selectMetricsContent } from '@src/context/Metrics/metricsSlice' @@ -132,7 +133,7 @@ const BoardMetrics = ({ - {'show more >'} + {SHOW_MORE} diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index 0c8984ec1a..0152a6b045 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -8,6 +8,7 @@ import { REPORT_PAGE, METRICS_TITLE, REQUIRED_DATA, + SHOW_MORE, } from '@src/constants/resources' import { ReportRequestDTO } from '@src/clients/report/dto/request' import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice' @@ -174,7 +175,7 @@ const DoraMetrics = ({ startToRequestDoraData, doraReport, csvTimeStamp, startDa - {'show more >'} + {SHOW_MORE} {shouldShowSourceControl && } diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index de095a7f17..0a4bbd7a55 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -9,6 +9,8 @@ export const REPORT_METRICS = { DORA: 'Dora Metrics', } +export const SHOW_MORE = 'show more >' + export enum REQUIRED_DATA { All = 'All', VELOCITY = 'Velocity', From 4a9c48257406cd9e0e14e19d5b07c990c266ae86 Mon Sep 17 00:00:00 2001 From: xuebing Date: Fri, 5 Jan 2024 11:16:09 +0800 Subject: [PATCH 57/90] ADM-652 [frontend] feat: enhance show export button logic --- .../Metrics/ReportButtonGroup/index.tsx | 22 ++++++++++++------- .../Metrics/ReportStep/ReportDetail/index.tsx | 10 +++++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 1be0f8c666..15c558c368 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -27,10 +27,14 @@ interface ReportButtonGroupProps { setErrorMessage: (message: string) => void reportData: ReportResponseDTO | undefined isFromDetailPage?: boolean + isShowDoraMetrics?: boolean + isShowBoardMetrics?: boolean } export const ReportButtonGroup = ({ handleSave, + isShowDoraMetrics, + isShowBoardMetrics, csvTimeStamp, startDate, endDate, @@ -77,13 +81,15 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.BACK} - handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} - > - {COMMON_BUTTONS.EXPORT_METRIC_DATA} - - {isShowExportBoardButton && ( + {!isFromDetailPage && ( + handleDownload(DOWNLOAD_TYPES.METRICS, startDate, endDate)} + > + {COMMON_BUTTONS.EXPORT_METRIC_DATA} + + )} + {(isFromDetailPage ? isShowBoardMetrics : isShowExportBoardButton) && ( handleDownload(DOWNLOAD_TYPES.BOARD, startDate, endDate)} @@ -91,7 +97,7 @@ export const ReportButtonGroup = ({ {COMMON_BUTTONS.EXPORT_BOARD_DATA} )} - {isShowExportPipelineButton && ( + {(isFromDetailPage ? isShowDoraMetrics : isShowExportPipelineButton) && ( { navigate(-1) } + const isShowBoardMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.BOARD + + const isShowDoraMetrics = () => state.reportType === RETRIEVE_REPORT_TYPES.DORA + return ( <>
@@ -115,7 +119,7 @@ const ReportDetail = () => { - {state.reportType === RETRIEVE_REPORT_TYPES.BOARD && ( + {isShowBoardMetrics() && ( <> {velocityState.isShow && ( @@ -133,7 +137,7 @@ const ReportDetail = () => { )} )} - {state.reportType === RETRIEVE_REPORT_TYPES.DORA && ( + {isShowDoraMetrics() && ( <> {deploymentFrequencyState.isShow && ( { )} Date: Tue, 9 Jan 2024 18:06:10 +0800 Subject: [PATCH 58/90] ADM-652 [frontend] test: fix test --- .../Metrics/ReportStep/ReportStep.test.tsx | 13 ++++++------- .../Metrics/ReportStep/ReportDetail/index.tsx | 0 .../Metrics/ReportStep/ReportDetail/style.tsx | 0 frontend/src/theme.ts | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx delete mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index 07faec5c9d..d829dee728 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -1,4 +1,4 @@ -import { act, render, renderHook, waitFor, waitForElementToBeRemoved } from '@testing-library/react' +import { act, render, renderHook, waitFor, waitForElementToBeRemoved, screen } from '@testing-library/react' import ReportStep from '@src/components/Metrics/ReportStep' import { BACK, @@ -243,17 +243,16 @@ describe('Report Step', () => { title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, }) - await act(async () => { - return jest.advanceTimersByTime(500000) - }) + + jest.advanceTimersByTime(500000) + expect(updateProps).not.toBeCalledWith({ open: true, title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, }) - await act(async () => { - return jest.advanceTimersByTime(1000000) - }) + + jest.advanceTimersByTime(1000000) expect(updateProps).toBeCalledWith({ open: true, diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.tsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index 25c60945b0..dc18bbbd9c 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -77,7 +77,6 @@ export const theme = createTheme({ main: '#3498db', light: '#b9c4cc', }, - link: '#4350AF', }, main: { backgroundColor: indigo[FIVE_HUNDRED], From 5b9dc26d64c762864cce89183e89f77aee83ba3e Mon Sep 17 00:00:00 2001 From: xuebing Date: Tue, 9 Jan 2024 18:27:28 +0800 Subject: [PATCH 59/90] ADM-652 [frontend] test: delete unused code --- frontend/src/config/routes.ts | 5 ---- frontend/src/constants/router.ts | 1 - frontend/src/context/report/reportSlice.ts | 30 ------------------- frontend/src/hooks/useGenerateReportEffect.ts | 7 +---- frontend/src/store.ts | 2 -- 5 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 frontend/src/context/report/reportSlice.ts diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index c6c283160a..87cb5760d5 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -12,11 +12,6 @@ export const routes = [ component: lazy(() => import('../pages/Metrics')), name: 'Metrics', }, - { - path: 'detail', - component: lazy(() => import('../components/Metrics/ReportStep/ReportDetail')), - name: 'Detail', - }, { path: '/error-page', component: lazy(() => import('../pages/ErrorPage')), diff --git a/frontend/src/constants/router.ts b/frontend/src/constants/router.ts index fc715eaecb..f421479251 100644 --- a/frontend/src/constants/router.ts +++ b/frontend/src/constants/router.ts @@ -2,5 +2,4 @@ export enum ROUTE { BASE_PAGE = '/', ERROR_PAGE = '/error-page', METRICS_PAGE = '/metrics', - METRICS_DETAIL_PAGE = '/detail', } diff --git a/frontend/src/context/report/reportSlice.ts b/frontend/src/context/report/reportSlice.ts deleted file mode 100644 index 5152163c24..0000000000 --- a/frontend/src/context/report/reportSlice.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' -import type { RootState } from '@src/store' -import { ReportResponseDTO } from '@src/clients/report/dto/response' - -export interface ReportState { - reportData: ReportResponseDTO | null -} - -export const initialBoardState: ReportState = { - reportData: null, -} - -export const reportSlice = createSlice({ - name: 'report', - initialState: { - ...initialBoardState, - }, - reducers: { - updateReportData: (state, action) => { - state.reportData = action.payload - }, - }, -}) -export const { updateReportData } = reportSlice.actions - -export const selectReportData = (state: RootState) => { - return state.report.reportData -} - -export default reportSlice.reducer diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 7822697280..f117446e23 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -6,8 +6,6 @@ import { InternalServerException } from '@src/exceptions/InternalServerException import { ReportResponseDTO } from '@src/clients/report/dto/response' import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons' import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { updateReportData } from '@src/context/report/reportSlice' export interface useGenerateReportEffectInterface { startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void @@ -20,7 +18,6 @@ export interface useGenerateReportEffectInterface { export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const reportPath = '/reports' - const dispatch = useAppDispatch() const [isServerError, setIsServerError] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [reportData, setReportData] = useState() @@ -91,9 +88,7 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { const handleAndUpdateData = (response: ReportResponseDTO) => { const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime) - const report = { ...response, exportValidityTime: exportValidityTime } - setReportData(report) - dispatch(updateReportData(report)) + setReportData({ ...response, exportValidityTime: exportValidityTime }) } return { diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 6296065eba..385fba18a7 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -3,7 +3,6 @@ import stepperReducer from './context/stepper/StepperSlice' import configReducer from './context/config/configSlice' import metricsSlice from './context/Metrics/metricsSlice' import headerSlice from '@src/context/header/headerSlice' -import reportSlice from '@src/context/report/reportSlice' export const store = configureStore({ reducer: { @@ -11,7 +10,6 @@ export const store = configureStore({ config: configReducer, metrics: metricsSlice, header: headerSlice, - report: reportSlice, }, }) From d641164badc7a55b0e16c162d4ec6cf656daf398 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Thu, 4 Jan 2024 11:24:30 +0800 Subject: [PATCH 60/90] ADM-675: [backend]feat:config cache for getJiraCards method in JiraFeignClient --- backend/src/main/java/heartbeat/client/JiraFeignClient.java | 1 + backend/src/main/java/heartbeat/config/CacheConfig.java | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/main/java/heartbeat/client/JiraFeignClient.java b/backend/src/main/java/heartbeat/client/JiraFeignClient.java index 1e62c0c44f..0cdf8aeb63 100644 --- a/backend/src/main/java/heartbeat/client/JiraFeignClient.java +++ b/backend/src/main/java/heartbeat/client/JiraFeignClient.java @@ -28,6 +28,7 @@ JiraBoardConfigDTO getJiraBoardConfiguration(URI baseUrl, @PathVariable String b StatusSelfDTO getColumnStatusCategory(URI baseUrl, @PathVariable String statusNum, @RequestHeader String authorization); + @Cacheable(cacheNames = "jiraCards", key = "#boardId+'-'+#queryCount+'-'+#startAt+'-'+#jql") @GetMapping(path = "/rest/agile/1.0/board/{boardId}/issue?maxResults={queryCount}&startAt={startAt}&jql={jql}") String getJiraCards(URI baseUrl, @PathVariable String boardId, @PathVariable int queryCount, @PathVariable int startAt, @PathVariable String jql, @RequestHeader String authorization); diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 783c9e46a2..61d45ab0fb 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -35,6 +35,7 @@ public CacheManager ehCacheManager() { cacheManager.createCache("targetField", getCacheConfiguration(FieldResponseDTO.class)); cacheManager.createCache("boardVerification", getCacheConfiguration(JiraBoardVerifyDTO.class)); cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); + cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); return cacheManager; } From f691d7ab791e6900cfc8a1242556e5face54f742 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Thu, 4 Jan 2024 11:33:53 +0800 Subject: [PATCH 61/90] ADM-675: [backend]feat:config cache for getHolidays method in HolidayFeignClient --- backend/src/main/java/heartbeat/client/HolidayFeignClient.java | 2 ++ backend/src/main/java/heartbeat/config/CacheConfig.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/backend/src/main/java/heartbeat/client/HolidayFeignClient.java b/backend/src/main/java/heartbeat/client/HolidayFeignClient.java index 51ba6c75a9..e8a73b3db1 100644 --- a/backend/src/main/java/heartbeat/client/HolidayFeignClient.java +++ b/backend/src/main/java/heartbeat/client/HolidayFeignClient.java @@ -2,6 +2,7 @@ import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import heartbeat.config.HolidayFeignClientConfiguration; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -10,6 +11,7 @@ configuration = HolidayFeignClientConfiguration.class) public interface HolidayFeignClient { + @Cacheable(cacheNames = "holidayResult", key = "#year") @GetMapping(path = "/{year}.json") HolidaysResponseDTO getHolidays(@PathVariable String year); diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 61d45ab0fb..295eaa5807 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -10,6 +10,8 @@ import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; + +import heartbeat.client.dto.board.jira.HolidaysResponseDTO; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; @@ -36,6 +38,7 @@ public CacheManager ehCacheManager() { cacheManager.createCache("boardVerification", getCacheConfiguration(JiraBoardVerifyDTO.class)); cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); + cacheManager.createCache("holidayResult", getCacheConfiguration(HolidaysResponseDTO.class)); return cacheManager; } From 387de52aff51486b70daaf927c5e8b384470ba9c Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 11:43:07 +0800 Subject: [PATCH 62/90] ADM-675: [backend]fix:fix the error that holidayDTO can not be serialized --- .../main/java/heartbeat/client/dto/board/jira/HolidayDTO.java | 4 +++- .../heartbeat/client/dto/board/jira/HolidaysResponseDTO.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java index 345fec0391..7610d6e01e 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidayDTO.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class HolidayDTO { +public class HolidayDTO implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java index 07e5e11e0e..2a0896791f 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/HolidaysResponseDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @@ -13,7 +14,7 @@ @NoArgsConstructor @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class HolidaysResponseDTO { +public class HolidaysResponseDTO implements Serializable { private List days; From 6bbe636e81f3a6bd24c5c3506aac0697935a90d1 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 16:30:11 +0800 Subject: [PATCH 63/90] ADM-675: [backend]feat:config cache for all method in BuildKiteFeignClient --- .../main/java/heartbeat/client/BuildKiteFeignClient.java | 7 +++++++ .../client/dto/pipeline/buildkite/BuildKiteBuildInfo.java | 3 ++- .../client/dto/pipeline/buildkite/BuildKiteJob.java | 4 +++- .../dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java | 4 +++- .../dto/pipeline/buildkite/BuildKitePipelineDTO.java | 3 ++- .../client/dto/pipeline/buildkite/BuildKiteTokenInfo.java | 3 ++- .../client/dto/pipeline/buildkite/CreatedByDTO.java | 3 ++- .../heartbeat/client/dto/pipeline/buildkite/EnvDTO.java | 4 +++- .../client/dto/pipeline/buildkite/ProviderDTO.java | 4 +++- .../client/dto/pipeline/buildkite/ProviderSettingsDTO.java | 4 +++- .../heartbeat/client/dto/pipeline/buildkite/StepsDTO.java | 4 +++- backend/src/main/java/heartbeat/config/CacheConfig.java | 6 ++++++ 12 files changed, 39 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java index f2b26be7b9..74e30c2b61 100644 --- a/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java +++ b/backend/src/main/java/heartbeat/client/BuildKiteFeignClient.java @@ -8,6 +8,7 @@ import java.util.List; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -21,14 +22,17 @@ @FeignClient(name = "buildKiteFeignClient", url = "${buildKite.url}", configuration = BuildKiteFeignClientDecoder.class) public interface BuildKiteFeignClient { + @Cacheable(cacheNames = "tokenInfo", key = "#token") @GetMapping(path = "v2/access-token") @ResponseStatus(HttpStatus.OK) BuildKiteTokenInfo getTokenInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "buildKiteOrganizationInfo", key = "#token") @GetMapping(path = "v2/organizations") @ResponseStatus(HttpStatus.OK) List getBuildKiteOrganizationsInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pipelineInfo", key = "#organizationId+'-'+#page+'-'+#perPage+'-'+#startTime+'-'+#endTime") @GetMapping(path = "v2/organizations/{organizationId}/pipelines?page={page}&per_page={perPage}") @ResponseStatus(HttpStatus.OK) List getPipelineInfo(@RequestHeader("Authorization") String token, @@ -43,6 +47,9 @@ ResponseEntity> getPipelineSteps(@RequestHeader("Author @RequestParam("per_page") String perPage, @RequestParam("created_from") String createdFrom, @RequestParam("created_to") String createdTo, @RequestParam("branch[]") List branch); + @Cacheable(cacheNames = "pipelineStepsInfo", + key = "#organizationId+'-'+#pipelineId+'-'+#page+'-'+#perPage+'-'" + + "+#createdFrom+'-'+#createdTo+'-'+(#branch!=null ? branch.toString() : '')") @GetMapping(path = "v2/organizations/{organizationId}/pipelines/{pipelineId}/builds", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseStatus(HttpStatus.OK) diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java index 078fa80926..4afadd19f6 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java @@ -8,6 +8,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.time.Instant; import java.util.Comparator; import java.util.List; @@ -18,7 +19,7 @@ @AllArgsConstructor @NoArgsConstructor @Builder -public class BuildKiteBuildInfo { +public class BuildKiteBuildInfo implements Serializable { private List jobs; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java index d9fb5b84ce..204c60d905 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteJob.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @JsonIgnoreProperties(ignoreUnknown = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class BuildKiteJob { +public class BuildKiteJob implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java index 80e5a66a9b..c643b710f7 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteOrganizationsInfo.java @@ -6,12 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKiteOrganizationsInfo { +public class BuildKiteOrganizationsInfo implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java index 9acdab32f6..4c6e0ac6bf 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKitePipelineDTO.java @@ -7,6 +7,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.Date; import java.util.List; @@ -15,7 +16,7 @@ @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKitePipelineDTO { +public class BuildKitePipelineDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java index b217c3fa13..ae65912e54 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteTokenInfo.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @AllArgsConstructor @@ -13,7 +14,7 @@ @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class BuildKiteTokenInfo { +public class BuildKiteTokenInfo implements Serializable { private List scopes; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java index 39cd559501..5c32e9e343 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/CreatedByDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.Date; @Data @@ -13,7 +14,7 @@ @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class CreatedByDTO { +public class CreatedByDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java index 489e21e75f..011eaa1112 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/EnvDTO.java @@ -5,10 +5,12 @@ import lombok.Builder; import lombok.Data; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class EnvDTO { +public class EnvDTO implements Serializable { } diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java index 38522b3cb1..cdaa576862 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class ProviderDTO { +public class ProviderDTO implements Serializable { private String id; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java index b6aaa2eaf5..ff8c893866 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/ProviderSettingsDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class ProviderSettingsDTO { +public class ProviderSettingsDTO implements Serializable { @JsonProperty("trigger_mode") private String triggerMode; diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java index 00c9323201..df1c5d9c2a 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/StepsDTO.java @@ -7,12 +7,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class StepsDTO { +public class StepsDTO implements Serializable { private String type; diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 295eaa5807..0b5c196b44 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -7,11 +7,13 @@ import heartbeat.client.dto.board.jira.JiraBoardVerifyDTO; import heartbeat.client.dto.board.jira.StatusSelfDTO; import java.time.Duration; +import java.util.List; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; import heartbeat.client.dto.board.jira.HolidaysResponseDTO; +import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; @@ -39,6 +41,10 @@ public CacheManager ehCacheManager() { cacheManager.createCache("boardProject", getCacheConfiguration(JiraBoardProject.class)); cacheManager.createCache("jiraCards", getCacheConfiguration(String.class)); cacheManager.createCache("holidayResult", getCacheConfiguration(HolidaysResponseDTO.class)); + cacheManager.createCache("tokenInfo", getCacheConfiguration(BuildKiteTokenInfo.class)); + cacheManager.createCache("buildKiteOrganizationInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pipelineInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pipelineStepsInfo", getCacheConfiguration(List.class)); return cacheManager; } From 1e26b6e94451a7a03111a01c3d969e36ef92a922 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Mon, 8 Jan 2024 16:33:50 +0800 Subject: [PATCH 64/90] ADM-675: [backend]feat: [backend]feat:config cache for all method in GitHubFeignClient --- .../src/main/java/heartbeat/client/GitHubFeignClient.java | 7 +++++++ .../java/heartbeat/client/dto/codebase/github/Author.java | 4 +++- .../heartbeat/client/dto/codebase/github/AuthorOuter.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Base.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Comment.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Commit.java | 4 +++- .../heartbeat/client/dto/codebase/github/CommitInfo.java | 3 ++- .../java/heartbeat/client/dto/codebase/github/Commits.java | 4 +++- .../heartbeat/client/dto/codebase/github/Committer.java | 4 +++- .../client/dto/codebase/github/CommitterOuter.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/File.java | 4 +++- .../dto/codebase/github/GitHubOrganizationsInfo.java | 4 +++- .../heartbeat/client/dto/codebase/github/GitHubRepo.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Head.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Html.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Issue.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/License.java | 4 +++- .../client/dto/codebase/github/LinkCollection.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Owner.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Parent.java | 4 +++- .../client/dto/codebase/github/PullRequestInfo.java | 3 ++- .../java/heartbeat/client/dto/codebase/github/Repo.java | 5 ++--- .../client/dto/codebase/github/ReviewComment.java | 4 +++- .../client/dto/codebase/github/ReviewComments.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Self.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Stats.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Status.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/Tree.java | 4 +++- .../java/heartbeat/client/dto/codebase/github/User.java | 4 +++- .../heartbeat/client/dto/codebase/github/Verification.java | 4 +++- backend/src/main/java/heartbeat/config/CacheConfig.java | 7 +++++++ 31 files changed, 98 insertions(+), 31 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/GitHubFeignClient.java b/backend/src/main/java/heartbeat/client/GitHubFeignClient.java index 75ad40fc57..2b6548292b 100644 --- a/backend/src/main/java/heartbeat/client/GitHubFeignClient.java +++ b/backend/src/main/java/heartbeat/client/GitHubFeignClient.java @@ -5,6 +5,7 @@ import heartbeat.client.dto.codebase.github.GitHubRepo; import heartbeat.client.dto.codebase.github.PullRequestInfo; import heartbeat.decoder.GitHubFeignClientDecoder; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; @@ -24,32 +25,38 @@ public interface GitHubFeignClient { void verifyCanReadTargetBranch(@PathVariable String repository, @PathVariable String branchName, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "githubOrganizationInfo", key = "#token") @GetMapping(path = "/user/orgs") @ResponseStatus(HttpStatus.OK) @Deprecated List getGithubOrganizationsInfo(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "githubAllRepos", key = "#token") @GetMapping(path = "/user/repos") @ResponseStatus(HttpStatus.OK) @Deprecated List getAllRepos(@RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "githubRepos", key = "#organizationName") @GetMapping(path = "/orgs/{organizationName}/repos") @ResponseStatus(HttpStatus.OK) @Deprecated List getReposByOrganizationName(@PathVariable String organizationName, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "commitInfo", key = "#repository+'-'+#commitId") @GetMapping(path = "/repos/{repository}/commits/{commitId}") @ResponseStatus(HttpStatus.OK) CommitInfo getCommitInfo(@PathVariable String repository, @PathVariable String commitId, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pullRequestCommitInfo", key = "#repository+'-'+#mergedPullNumber") @GetMapping(path = "/repos/{repository}/pulls/{mergedPullNumber}/commits") @ResponseStatus(HttpStatus.OK) List getPullRequestCommitInfo(@PathVariable String repository, @PathVariable String mergedPullNumber, @RequestHeader("Authorization") String token); + @Cacheable(cacheNames = "pullRequestListInfo", key = "#repository+'-'+#deployId") @GetMapping(path = "/repos/{repository}/commits/{deployId}/pulls") @ResponseStatus(HttpStatus.OK) List getPullRequestListInfo(@PathVariable String repository, @PathVariable String deployId, diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java index 415198e2ac..da481a3f19 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Author.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Author { +public class Author implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java index 8be0cc0528..566a81519c 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/AuthorOuter.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class AuthorOuter { +public class AuthorOuter implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java index ac96c750e0..e2bc62608b 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Base.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Base { +public class Base implements Serializable { private String label; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java index ff97ba624b..84d4dfbfcb 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Comment.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Comment { +public class Comment implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java index 6309b1b558..2af13c7134 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commit.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Commit { +public class Commit implements Serializable { private Author author; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java index 1f6a55e9a1..6fdf70289a 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitInfo.java @@ -6,13 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class CommitInfo { +public class CommitInfo implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java index 1db391606a..6a6087d8fc 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Commits.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Commits { +public class Commits implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java index 50fd43c57c..136c19dad6 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Committer.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Committer { +public class Committer implements Serializable { private String name; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java index becaf04070..23eb7d5cef 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/CommitterOuter.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class CommitterOuter { +public class CommitterOuter implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java index 8f7b7c3330..5279069781 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/File.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class File { +public class File implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java index d7833728ea..a16ecdf8ce 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubOrganizationsInfo.java @@ -6,12 +6,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; + @AllArgsConstructor @NoArgsConstructor @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class GitHubOrganizationsInfo { +public class GitHubOrganizationsInfo implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java index 97d4f42186..e806cf8b88 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/GitHubRepo.java @@ -7,12 +7,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; + @AllArgsConstructor @NoArgsConstructor @Getter @Builder @JsonIgnoreProperties(ignoreUnknown = true) -public class GitHubRepo { +public class GitHubRepo implements Serializable { @JsonProperty("html_url") private String htmlUrl; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java index c8666a1b87..dab44a8da7 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Head.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Head { +public class Head implements Serializable { private String label; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java index 4323338dbd..c9f7b0d42c 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Html.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Html { +public class Html implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java index fe24e2e905..b935061bb3 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Issue.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Issue { +public class Issue implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java index 74350cf99d..46ce69b932 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/License.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class License { +public class License implements Serializable { private String key; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java index e6d588c70c..5caf031822 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/LinkCollection.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class LinkCollection { +public class LinkCollection implements Serializable { private Self self; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java index a33378eec5..24fd8a1a92 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Owner.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Owner { +public class Owner implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java index 67523769c8..291a39b40f 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Parent.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Parent { +public class Parent implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java index b5bf8e20de..f0672a2b36 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/PullRequestInfo.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @@ -14,7 +15,7 @@ @NoArgsConstructor @AllArgsConstructor @Getter -public class PullRequestInfo { +public class PullRequestInfo implements Serializable { private String url; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java index 94991be6a9..ee55d42092 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Repo.java @@ -1,20 +1,19 @@ package heartbeat.client.dto.codebase.github; import com.fasterxml.jackson.annotation.JsonProperty; -import heartbeat.client.dto.codebase.github.License; -import heartbeat.client.dto.codebase.github.Owner; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.List; @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Repo { +public class Repo implements Serializable { private Integer id; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java index 6828b6b3db..52d50eff3e 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComment.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class ReviewComment { +public class ReviewComment implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java index f71d3eef76..b040cf3f29 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/ReviewComments.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class ReviewComments { +public class ReviewComments implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java index b6615ae9ad..20aa3b6afb 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Self.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Self { +public class Self implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java index 8a35608185..ce1dd29918 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Stats.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Stats { +public class Stats implements Serializable { private Integer total; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java index 4f8e827c97..59d115d7f9 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Status.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Status { +public class Status implements Serializable { private String href; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java index db4d8b9936..2bb563eed6 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Tree.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Tree { +public class Tree implements Serializable { private String sha; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java index 181c9e4c3e..4d82094d20 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/User.java @@ -6,11 +6,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class User { +public class User implements Serializable { private String login; diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java index abfcb34f66..5991419b87 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/Verification.java @@ -5,11 +5,13 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class Verification { +public class Verification implements Serializable { private Boolean verified; diff --git a/backend/src/main/java/heartbeat/config/CacheConfig.java b/backend/src/main/java/heartbeat/config/CacheConfig.java index 0b5c196b44..7c777f87eb 100644 --- a/backend/src/main/java/heartbeat/config/CacheConfig.java +++ b/backend/src/main/java/heartbeat/config/CacheConfig.java @@ -13,6 +13,7 @@ import javax.cache.spi.CachingProvider; import heartbeat.client.dto.board.jira.HolidaysResponseDTO; +import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteTokenInfo; import lombok.val; import org.ehcache.config.builders.CacheConfigurationBuilder; @@ -45,6 +46,12 @@ public CacheManager ehCacheManager() { cacheManager.createCache("buildKiteOrganizationInfo", getCacheConfiguration(List.class)); cacheManager.createCache("pipelineInfo", getCacheConfiguration(List.class)); cacheManager.createCache("pipelineStepsInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("githubOrganizationInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("githubAllRepos", getCacheConfiguration(List.class)); + cacheManager.createCache("githubRepos", getCacheConfiguration(List.class)); + cacheManager.createCache("commitInfo", getCacheConfiguration(CommitInfo.class)); + cacheManager.createCache("pullRequestCommitInfo", getCacheConfiguration(List.class)); + cacheManager.createCache("pullRequestListInfo", getCacheConfiguration(List.class)); return cacheManager; } From 487857248b9a568d602b7a351a4f33790c43a7b0 Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Tue, 9 Jan 2024 17:57:19 +0800 Subject: [PATCH 65/90] ADM-708:[backend]test: remove when annotation --- .../src/test/java/heartbeat/service/jira/JiraServiceTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index 2dd57aeae0..a0b74d09e9 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -1008,12 +1008,9 @@ void shouldReturnBadRequestExceptionWhenBoardTypeIsNotCorrect() { @Test void shouldReturnBadRequestExceptionWhenBoardStyleIsNotCorrect() { - // given BoardRequestParam boardRequestParam = BOARD_REQUEST_BUILDER().build(); - // when when(jiraFeignClient.getProject(any(), any(), any())) .thenReturn(JiraBoardProject.builder().style("unknown").build()); - // then assertThatThrownBy(() -> jiraService.getInfo(boardTypeJira, boardRequestParam)) .isInstanceOf(InternalServerErrorException.class) .hasMessageContaining("Board type does not find!"); From b0a4a724cbbce79a9a2178a32807475d61f706a7 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 10 Jan 2024 11:32:22 +0800 Subject: [PATCH 66/90] ADM-652 [frontend] test: fix code quality --- .../ReportStep/ReportDetail/withBack.test.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx index eaa8af291f..4f9ae25899 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx @@ -1,5 +1,5 @@ import { withGoBack } from '@src/components/Metrics/ReportStep/ReportDetail/withBack' -import { render, fireEvent } from '@testing-library/react' +import { render, fireEvent, screen } from '@testing-library/react' describe('withGoBack', () => { const onBack = jest.fn() @@ -8,20 +8,20 @@ describe('withGoBack', () => { it('should render a link with back', () => { const Component = withGoBack(() =>
test1
) - const { getByText } = render() - expect(getByText('Back')).toBeInTheDocument() + render() + expect(screen.getByText('Back')).toBeInTheDocument() }) it('should render the icon', () => { const Component = withGoBack(() =>
test2
) - const { getByTestId } = render() - expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() + render() + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() }) it('should call onBack when the back is clicked', () => { const Component = withGoBack(() =>
test3
) - const { getByText } = render() - fireEvent.click(getByText('Back')) + render() + fireEvent.click(screen.getByText('Back')) expect(onBack).toBeCalled() }) }) From f94887ff1bd3b1b24e4cb3561b78c9fc9be90655 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 10 Jan 2024 11:43:48 +0800 Subject: [PATCH 67/90] ADM-652 [frontend] test: fix code quality for import react --- .../components/Metrics/ReportStep/ReportDetail/board.test.tsx | 1 + .../src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx index 6c3941a966..44dd11b018 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -2,6 +2,7 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' import { render } from '@testing-library/react' import { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail' import { reportMapper } from '@src/hooks/reportMapper/report' +import React from 'react' jest.mock('@src/hooks/reportMapper/report') diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx index 8b6e04e890..ef465352d0 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx @@ -2,6 +2,7 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' import { render } from '@testing-library/react' import { reportMapper } from '@src/hooks/reportMapper/report' import { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail' +import React from 'react' jest.mock('@src/hooks/reportMapper/report') describe('DoraDetail', () => { From 5e1ab3f21e557ad299d3424244086a52bd15d107 Mon Sep 17 00:00:00 2001 From: LEI WANG Date: Wed, 10 Jan 2024 13:56:25 +0800 Subject: [PATCH 68/90] ADM-741:[backend]feat: refactor commented codes --- .../report/GenerateReportController.java | 66 -------- .../request/GenerateBoardReportRequest.java | 45 ------ .../request/GenerateDoraReportRequest.java | 48 ------ .../report/GenerateReporterService.java | 28 ++-- .../src/main/java/heartbeat/util/IdUtil.java | 6 +- .../GenerateReporterControllerTest.java | 146 ------------------ .../report/GenerateReporterServiceTest.java | 34 ++-- 7 files changed, 39 insertions(+), 334 deletions(-) delete mode 100644 backend/src/main/java/heartbeat/controller/report/dto/request/GenerateBoardReportRequest.java delete mode 100644 backend/src/main/java/heartbeat/controller/report/dto/request/GenerateDoraReportRequest.java diff --git a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java index a2cb4deecd..763217e270 100644 --- a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java +++ b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java @@ -2,17 +2,12 @@ import heartbeat.controller.report.dto.request.DataType; import heartbeat.controller.report.dto.request.ReportType; -import heartbeat.controller.report.dto.request.GenerateDoraReportRequest; -import heartbeat.controller.report.dto.request.GenerateBoardReportRequest; import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.ExportCSVRequest; import heartbeat.controller.report.dto.response.CallbackResponse; import heartbeat.controller.report.dto.response.ReportResponse; -import heartbeat.exception.BadRequestException; -import heartbeat.exception.BaseException; import heartbeat.handler.AsyncExceptionHandler; import heartbeat.service.report.GenerateReporterService; -import heartbeat.util.IdUtil; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Value; @@ -65,67 +60,6 @@ public ResponseEntity generateReport(@PathVariable String report return ResponseEntity.status(HttpStatus.OK).body(reportResponse); } - @PostMapping("/v1/board") - public ResponseEntity generateBoardReport(@RequestBody GenerateBoardReportRequest request) { - log.info( - "Start to generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", - request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), - IdUtil.getBoardReportId(request.getCsvTimeStamp())); - generateReporterService.initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); - CompletableFuture.runAsync(() -> { - try { - ReportResponse reportResponse = generateReporterService - .generateReporter(request.convertToReportRequest()); - generateReporterService.saveReporterInHandler(reportResponse, - IdUtil.getBoardReportId(request.getCsvTimeStamp())); - generateReporterService.updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - log.info( - "Successfully generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", - request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), - request.getEndTime(), IdUtil.getBoardReportId(request.getCsvTimeStamp())); - - } - catch (BaseException e) { - asyncExceptionHandler.put(IdUtil.getBoardReportId(request.getCsvTimeStamp()), e); - } - }); - - String callbackUrl = "/reports/" + request.getCsvTimeStamp(); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); - } - - @PostMapping("/v1/dora") - public ResponseEntity generateDoraReport(@RequestBody GenerateDoraReportRequest request) { - log.info( - "Start to generate dora report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", - request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), - IdUtil.getDoraReportId(request.getCsvTimeStamp())); - generateReporterService.initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); - CompletableFuture.runAsync(() -> { - try { - ReportResponse reportResponse = generateReporterService - .generateReporter(request.convertToReportRequest()); - generateReporterService.saveReporterInHandler(reportResponse, - IdUtil.getDoraReportId(request.getCsvTimeStamp())); - generateReporterService.updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - log.info( - "Successfully generate dora report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", - request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), - request.getEndTime(), IdUtil.getDoraReportId(request.getCsvTimeStamp())); - } - catch (BaseException e) { - asyncExceptionHandler.put(IdUtil.getDoraReportId(request.getCsvTimeStamp()), e); - } - }); - - String callbackUrl = "/reports/" + request.getCsvTimeStamp(); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(CallbackResponse.builder().callbackUrl(callbackUrl).interval(interval).build()); - } - @PostMapping("{reportType}") public ResponseEntity generateReport(@PathVariable ReportType reportType, @RequestBody GenerateReportRequest request) { diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateBoardReportRequest.java b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateBoardReportRequest.java deleted file mode 100644 index 6c26273c52..0000000000 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateBoardReportRequest.java +++ /dev/null @@ -1,45 +0,0 @@ -package heartbeat.controller.report.dto.request; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import jakarta.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class GenerateBoardReportRequest { - - private Boolean considerHoliday; - - @NotBlank(message = "StartTime is required") - private String startTime; - - @NotBlank(message = "EndTime is required") - private String endTime; - - private List metrics; - - private JiraBoardSetting jiraBoardSetting; - - @NotBlank - private String csvTimeStamp; - - public GenerateReportRequest convertToReportRequest() { - return GenerateReportRequest.builder() - .considerHoliday(this.considerHoliday) - .startTime(this.startTime) - .endTime(this.endTime) - .metrics(this.metrics) - .jiraBoardSetting(this.jiraBoardSetting) - .csvTimeStamp(this.csvTimeStamp) - .build(); - } - -} diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateDoraReportRequest.java b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateDoraReportRequest.java deleted file mode 100644 index 3bb4539f8d..0000000000 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateDoraReportRequest.java +++ /dev/null @@ -1,48 +0,0 @@ -package heartbeat.controller.report.dto.request; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import jakarta.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class GenerateDoraReportRequest { - - private Boolean considerHoliday; - - @NotBlank(message = "StartTime is required") - private String startTime; - - @NotBlank(message = "EndTime is required") - private String endTime; - - private List metrics; - - private BuildKiteSetting buildKiteSetting; - - private CodebaseSetting codebaseSetting; - - @NotBlank - private String csvTimeStamp; - - public GenerateReportRequest convertToReportRequest() { - GenerateReportRequest reportRequest = new GenerateReportRequest(); - reportRequest.setConsiderHoliday(this.considerHoliday); - reportRequest.setStartTime(this.startTime); - reportRequest.setEndTime(this.endTime); - reportRequest.setMetrics(this.metrics); - reportRequest.setBuildKiteSetting(this.buildKiteSetting); - reportRequest.setCodebaseSetting(this.codebaseSetting); - reportRequest.setCsvTimeStamp(this.csvTimeStamp); - return reportRequest; - } - -} diff --git a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java index 0d46a26a4f..10fde01a21 100644 --- a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java +++ b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java @@ -28,7 +28,6 @@ import heartbeat.controller.report.dto.request.ExportCSVRequest; import heartbeat.controller.report.dto.request.GenerateReportRequest; import heartbeat.controller.report.dto.request.JiraBoardSetting; -import heartbeat.controller.report.dto.request.RequireDataEnum; import heartbeat.controller.report.dto.response.BoardCSVConfig; import heartbeat.controller.report.dto.response.BoardCSVConfigEnum; import heartbeat.controller.report.dto.response.LeadTimeInfo; @@ -203,7 +202,10 @@ else if (jsonObject.has("key")) { public void generateBoardReport(GenerateReportRequest request) { initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); - + log.info( + "Start to generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), + IdUtil.getPipelineReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { saveReporterInHandler(generateReporter(request), IdUtil.getBoardReportId(request.getCsvTimeStamp())); @@ -233,28 +235,36 @@ public void generateDoraReport(GenerateReportRequest request) { } private void generatePipelineReport(GenerateReportRequest request) { + GenerateReportRequest pipelineRequest = request.convertToPipelineRequest(request); + log.info( + "Start to generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + pipelineRequest.getMetrics(), pipelineRequest.getConsiderHoliday(), pipelineRequest.getStartTime(), + pipelineRequest.getEndTime(), IdUtil.getPipelineReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { - GenerateReportRequest pipelineRequest = request.convertToPipelineRequest(request); saveReporterInHandler(generateReporter(pipelineRequest), - IdUtil.getDoraReportId(pipelineRequest.getCsvTimeStamp())); + IdUtil.getPipelineReportId(pipelineRequest.getCsvTimeStamp())); updateMetricsDataReadyInHandler(pipelineRequest.getCsvTimeStamp(), pipelineRequest.getMetrics()); log.info( "Successfully generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _pipelineReportId: {}", pipelineRequest.getMetrics(), pipelineRequest.getConsiderHoliday(), pipelineRequest.getStartTime(), pipelineRequest.getEndTime(), - IdUtil.getDoraReportId(request.getCsvTimeStamp())); + IdUtil.getPipelineReportId(request.getCsvTimeStamp())); } catch (BaseException e) { - asyncExceptionHandler.put(IdUtil.getDoraReportId(request.getCsvTimeStamp()), e); + asyncExceptionHandler.put(IdUtil.getPipelineReportId(request.getCsvTimeStamp()), e); } }); } private void generateCodeBaseReport(GenerateReportRequest request) { + GenerateReportRequest codebaseRequest = request.convertToCodeBaseRequest(request); + log.info( + "Start to generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), codebaseRequest.getStartTime(), + codebaseRequest.getEndTime(), IdUtil.getPipelineReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { - GenerateReportRequest codebaseRequest = request.convertToCodeBaseRequest(request); saveReporterInHandler(generateReporter(codebaseRequest), IdUtil.getCodeBaseReportId(codebaseRequest.getCsvTimeStamp())); updateMetricsDataReadyInHandler(codebaseRequest.getCsvTimeStamp(), codebaseRequest.getMetrics()); @@ -795,7 +805,7 @@ public boolean checkGenerateReportIsDone(String reportTimeStamp) { throw new GenerateReportException("Failed to get report due to report time expires"); } BaseException boardException = asyncExceptionHandler.get(IdUtil.getBoardReportId(reportTimeStamp)); - BaseException doraException = asyncExceptionHandler.get(IdUtil.getDoraReportId(reportTimeStamp)); + BaseException doraException = asyncExceptionHandler.get(IdUtil.getPipelineReportId(reportTimeStamp)); handleAsyncException(boardException); handleAsyncException(doraException); return asyncReportRequestHandler.isReportReady(reportTimeStamp); @@ -858,7 +868,7 @@ public ReportResponse getReportFromHandler(String reportId) { public ReportResponse getComposedReportResponse(String reportId, boolean isReportReady) { ReportResponse boardReportResponse = getReportFromHandler(IdUtil.getBoardReportId(reportId)); - ReportResponse doraReportResponse = getReportFromHandler(IdUtil.getDoraReportId(reportId)); + ReportResponse doraReportResponse = getReportFromHandler(IdUtil.getPipelineReportId(reportId)); ReportResponse codebaseReportResponse = getReportFromHandler(IdUtil.getCodeBaseReportId(reportId)); MetricsDataReady metricsDataReady = asyncReportRequestHandler.getMetricsDataReady(reportId); ReportResponse response = Optional.ofNullable(boardReportResponse).orElse(doraReportResponse); diff --git a/backend/src/main/java/heartbeat/util/IdUtil.java b/backend/src/main/java/heartbeat/util/IdUtil.java index 176a1b773d..72196a00c4 100644 --- a/backend/src/main/java/heartbeat/util/IdUtil.java +++ b/backend/src/main/java/heartbeat/util/IdUtil.java @@ -4,7 +4,7 @@ public interface IdUtil { String BOARD_REPORT_PREFIX = "board-"; - String DORA_REPORT_PREFIX = "dora-"; + String PIPELINE_REPORT_PREFIX = "pipeline-"; String CODE_BASE_PREFIX = "github-"; @@ -12,8 +12,8 @@ static String getBoardReportId(String timeStamp) { return BOARD_REPORT_PREFIX + timeStamp; } - static String getDoraReportId(String timeStamp) { - return DORA_REPORT_PREFIX + timeStamp; + static String getPipelineReportId(String timeStamp) { + return PIPELINE_REPORT_PREFIX + timeStamp; } static String getCodeBaseReportId(String timeStamp) { diff --git a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java index b831665eb4..6ea0bf6a68 100644 --- a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java @@ -3,18 +3,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.JsonPath; import heartbeat.controller.report.dto.request.ExportCSVRequest; -import heartbeat.controller.report.dto.request.GenerateBoardReportRequest; -import heartbeat.controller.report.dto.request.GenerateDoraReportRequest; import heartbeat.controller.report.dto.request.GenerateReportRequest; -import heartbeat.controller.report.dto.response.AvgDeploymentFrequency; -import heartbeat.controller.report.dto.response.DeploymentFrequency; import heartbeat.controller.report.dto.response.ReportResponse; -import heartbeat.controller.report.dto.response.Velocity; import heartbeat.exception.GenerateReportException; -import heartbeat.exception.RequestFailedException; import heartbeat.service.report.GenerateReporterService; import heartbeat.handler.AsyncExceptionHandler; -import heartbeat.util.IdUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -32,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -140,144 +132,6 @@ void shouldReturnWhenExportCsv() throws Exception { } - @Test - void shouldReturnAcceptedStatusAndCallbackUrlAndIntervalWhenCallBoardReports() throws Exception { - ReportResponse expectedResponse = ReportResponse.builder() - .velocity(Velocity.builder().velocityForSP(10).build()) - .deploymentFrequency(DeploymentFrequency.builder() - .avgDeploymentFrequency(new AvgDeploymentFrequency("Average", 0.10F)) - .build()) - .build(); - - ObjectMapper mapper = new ObjectMapper(); - GenerateBoardReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), - GenerateBoardReportRequest.class); - String currentTimeStamp = "1685010080107"; - request.setCsvTimeStamp(currentTimeStamp); - - when(generateReporterService.generateReporter(request.convertToReportRequest())).thenReturn(expectedResponse); - doNothing().when(generateReporterService).initializeMetricsDataReadyInHandler(any(), any()); - doNothing().when(generateReporterService).saveReporterInHandler(any(), any()); - doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); - - MockHttpServletResponse response = mockMvc - .perform(post("/reports/v1/board").contentType(MediaType.APPLICATION_JSON) - .content(mapper.writeValueAsString(request))) - .andExpect(status().isAccepted()) - .andReturn() - .getResponse(); - - final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); - final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); - assertEquals("/reports/" + currentTimeStamp, callbackUrl); - assertEquals("10", interval); - } - - @Test - void shouldGetExceptionAndPutInExceptionMapWhenCallBoardReport() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - GenerateBoardReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), - GenerateBoardReportRequest.class); - String currentTimeStamp = "1685010080107"; - request.setCsvTimeStamp(currentTimeStamp); - - RequestFailedException requestFailedException = new RequestFailedException(402, "Client Error"); - when(generateReporterService.generateReporter(request.convertToReportRequest())) - .thenThrow(requestFailedException); - doNothing().when(generateReporterService).initializeMetricsDataReadyInHandler(any(), any()); - doNothing().when(generateReporterService).saveReporterInHandler(any(), any()); - doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); - - MockHttpServletResponse response = mockMvc - .perform(post("/reports/v1/board").contentType(MediaType.APPLICATION_JSON) - .content(mapper.writeValueAsString(request))) - .andExpect(status().isAccepted()) - .andReturn() - .getResponse(); - - final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); - final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); - assertEquals("/reports/" + currentTimeStamp, callbackUrl); - assertEquals("10", interval); - - Thread.sleep(2000L); - verify(generateReporterService, times(1)).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - verify(generateReporterService, times(0)).saveReporterInHandler(any(), any()); - verify(generateReporterService, times(0)).updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - verify(asyncExceptionHandler, times(1)).put(IdUtil.getBoardReportId(currentTimeStamp), requestFailedException); - } - - @Test - void shouldGetExceptionAndPutInExceptionMapWhenCallDoraReport() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - GenerateDoraReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), - GenerateDoraReportRequest.class); - String currentTimeStamp = "1685010080107"; - request.setCsvTimeStamp(currentTimeStamp); - - RequestFailedException requestFailedException = new RequestFailedException(402, "Client Error"); - when(generateReporterService.generateReporter(request.convertToReportRequest())) - .thenThrow(requestFailedException); - doNothing().when(generateReporterService).initializeMetricsDataReadyInHandler(any(), any()); - doNothing().when(generateReporterService).saveReporterInHandler(any(), any()); - doNothing().when(generateReporterService).updateMetricsDataReadyInHandler(any(), any()); - - MockHttpServletResponse response = mockMvc - .perform(post("/reports/v1/dora").contentType(MediaType.APPLICATION_JSON) - .content(mapper.writeValueAsString(request))) - .andExpect(status().isAccepted()) - .andReturn() - .getResponse(); - - final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); - final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); - assertEquals("/reports/" + currentTimeStamp, callbackUrl); - assertEquals("10", interval); - - Thread.sleep(2000L); - verify(generateReporterService, times(1)).initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - verify(generateReporterService, times(0)).saveReporterInHandler(any(), any()); - verify(generateReporterService, times(0)).updateMetricsDataReadyInHandler(request.getCsvTimeStamp(), - request.getMetrics()); - verify(asyncExceptionHandler, times(1)).put(IdUtil.getDoraReportId(currentTimeStamp), requestFailedException); - } - - @Test - void shouldReturnAcceptedStatusAndCallbackUrlAndIntervalWhenCallDoraReports() throws Exception { - ReportResponse expectedResponse = ReportResponse.builder() - .deploymentFrequency(DeploymentFrequency.builder() - .avgDeploymentFrequency(new AvgDeploymentFrequency("Average", 0.10F)) - .build()) - .velocity(Velocity.builder().velocityForSP(10).build()) - .deploymentFrequency(DeploymentFrequency.builder() - .avgDeploymentFrequency(new AvgDeploymentFrequency("Average", 0.10F)) - .build()) - .build(); - - ObjectMapper mapper = new ObjectMapper(); - GenerateBoardReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), - GenerateBoardReportRequest.class); - String currentTimeStamp = "1685010080107"; - request.setCsvTimeStamp(currentTimeStamp); - - when(generateReporterService.generateReporter(request.convertToReportRequest())).thenReturn(expectedResponse); - - MockHttpServletResponse response = mockMvc - .perform(post("/reports/v1/dora").contentType(MediaType.APPLICATION_JSON) - .content(mapper.writeValueAsString(request))) - .andExpect(status().isAccepted()) - .andReturn() - .getResponse(); - - final var callbackUrl = JsonPath.parse(response.getContentAsString()).read("$.callbackUrl").toString(); - final var interval = JsonPath.parse(response.getContentAsString()).read("$.interval").toString(); - assertEquals("/reports/" + currentTimeStamp, callbackUrl); - assertEquals("10", interval); - } - @Test void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateBoardReportWhenReportTypeIsBoard() throws Exception { ObjectMapper mapper = new ObjectMapper(); diff --git a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java index f1657c1315..0a7e24dd12 100644 --- a/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/GenerateReporterServiceTest.java @@ -968,7 +968,7 @@ void shouldReturnReportResponse() { @Test void shouldThrowUnauthorizedExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); when(asyncExceptionHandler.get(reportId)) .thenReturn(new UnauthorizedException("Failed to get GitHub info_status: 401, reason: PermissionDeny")); @@ -983,7 +983,7 @@ void shouldThrowUnauthorizedExceptionWhenCheckGenerateReportIsDone() { @Test void shouldThrowPermissionDenyExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); asyncExceptionHandler.put(reportId, new PermissionDenyException("Failed to get GitHub info_status: 403, reason: PermissionDeny")); @@ -999,7 +999,7 @@ void shouldThrowPermissionDenyExceptionWhenCheckGenerateReportIsDone() { @Test void shouldThrowNotFoundExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); when(asyncExceptionHandler.get(reportId)) .thenReturn(new NotFoundException("Failed to get GitHub info_status: 404, reason: NotFound")); @@ -1013,7 +1013,7 @@ void shouldThrowNotFoundExceptionWhenCheckGenerateReportIsDone() { @Test void shouldThrowGenerateReportExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); when(asyncExceptionHandler.get(reportId)) .thenReturn(new GenerateReportException("Failed to get GitHub info_status: 500, reason: GenerateReport")); @@ -1027,7 +1027,7 @@ void shouldThrowGenerateReportExceptionWhenCheckGenerateReportIsDone() { @Test void shouldThrowServiceUnavailableExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); when(asyncExceptionHandler.get(reportId)).thenReturn( new ServiceUnavailableException("Failed to get GitHub info_status: 503, reason: ServiceUnavailable")); @@ -1041,7 +1041,7 @@ void shouldThrowServiceUnavailableExceptionWhenCheckGenerateReportIsDone() { @Test void shouldThrowRequestFailedExceptionWhenCheckGenerateReportIsDone() { String timeStamp = Long.toString(System.currentTimeMillis()); - String reportId = IdUtil.getDoraReportId(timeStamp); + String reportId = IdUtil.getPipelineReportId(timeStamp); when(asyncExceptionHandler.get(reportId)).thenReturn(new RequestFailedException(405, "RequestFailedException")); BaseException exception = assertThrows(RequestFailedException.class, @@ -1207,10 +1207,10 @@ void shouldReturnComposedReportResponseWhenBothBoardResponseAndDoraResponseReady String timeStamp = "1683734399999"; String boardTimeStamp = "board-1683734399999"; - String doraTimestamp = "dora-1683734399999"; + String pipelineTimestamp = "pipeline-1683734399999"; when(generateReporterService.getReportFromHandler(boardTimeStamp)).thenReturn(boardResponse); - when(generateReporterService.getReportFromHandler(doraTimestamp)).thenReturn(pipelineResponse); + when(generateReporterService.getReportFromHandler(pipelineTimestamp)).thenReturn(pipelineResponse); when(asyncReportRequestHandler.getMetricsDataReady(timeStamp)) .thenReturn(new MetricsDataReady(Boolean.TRUE, Boolean.TRUE, null)); @@ -1264,8 +1264,8 @@ void shouldDoConvertMetricDataToCSVWhenCallGenerateCSVForMetrics() throws IOExce @Test void shouldPutReportInHandlerWhenCallSaveReporterInHandler() throws IOException { - String timeStamp = "1683734399999"; - String reportId = IdUtil.getDoraReportId(timeStamp); + String timeStamp = "20240109232359"; + String reportId = IdUtil.getPipelineReportId(timeStamp); ObjectMapper mapper = new ObjectMapper(); ReportResponse reportResponse = mapper .readValue(new File("src/test/java/heartbeat/controller/report/reportResponse.json"), ReportResponse.class); @@ -1282,7 +1282,7 @@ void shouldPutBoardReportIntoHandlerWhenCallGenerateBoardReport() throws IOExcep GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); - reportRequest.setCsvTimeStamp("1683734399999"); + reportRequest.setCsvTimeStamp("20240109232359"); MetricsDataReady previousMetricsReady = MetricsDataReady.builder() .isBoardMetricsReady(true) .isPipelineMetricsReady(false) @@ -1313,8 +1313,8 @@ void shouldPutExceptionInHandlerWhenCallGenerateBoardReportThrowException() GenerateReportRequest reportRequest = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); - reportRequest.setCsvTimeStamp("1683734399999"); - String boardTimeStamp = "board-1683734399999"; + reportRequest.setCsvTimeStamp("20240109232359"); + String boardTimeStamp = "board-20240109232359"; GenerateReporterService spyGenerateReporterService = spy(generateReporterService); GenerateReportException e = new GenerateReportException( "Failed to update metrics data ready through this timestamp."); @@ -1342,8 +1342,8 @@ void shouldGeneratePipelineReportAndUpdatePipelineMetricsReadyWhenCallGeneratePi reportRequest.setMetrics(List.of("Deployment frequency")); ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); GenerateReporterService spyGenerateReporterService = spy(generateReporterService); - reportRequest.setCsvTimeStamp("1683734399999"); - String doraTimeStamp = "dora-1683734399999"; + reportRequest.setCsvTimeStamp("20240109232359"); + String doraTimeStamp = "dora-20240109232359"; MetricsDataReady previousMetricsReady = MetricsDataReady.builder() .isBoardMetricsReady(null) .isPipelineMetricsReady(false) @@ -1373,8 +1373,8 @@ void shouldGenerateCodebaseReportAndUpdateCodebaseMetricsReadyWhenCallGeneratePi reportRequest.setMetrics(List.of("Lead time for changes")); ReportResponse reportResponse = mapper.readValue(new File(RESPONSE_FILE_PATH), ReportResponse.class); GenerateReporterService spyGenerateReporterService = spy(generateReporterService); - reportRequest.setCsvTimeStamp("1683734399999"); - String codebaseTimeStamp = "github-1683734399999"; + reportRequest.setCsvTimeStamp("20240109232359"); + String codebaseTimeStamp = "github-20240109232359"; MetricsDataReady previousMetricsReady = MetricsDataReady.builder() .isBoardMetricsReady(null) .isPipelineMetricsReady(false) From c1e191a83bf35e864020320797c09cd9e40acf22 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 10 Jan 2024 14:06:46 +0800 Subject: [PATCH 69/90] ADM-652 [frontend] test: fix code quality for report test --- .../ReportStep/ReportDetail/board.test.tsx | 71 ++++++++++--------- .../ReportStep/ReportDetail/dora.test.tsx | 60 ++++++++-------- .../Common/ReportForThreeColumns/index.tsx | 4 +- .../Common/ReportForTwoColumns/index.tsx | 4 +- 4 files changed, 75 insertions(+), 64 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx index 44dd11b018..9004317d9b 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -1,5 +1,5 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { render } from '@testing-library/react' +import { render, screen, within } from '@testing-library/react' import { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail' import { reportMapper } from '@src/hooks/reportMapper/report' import React from 'react' @@ -13,9 +13,9 @@ describe('board', () => { it('should render a back link', () => { ;(reportMapper as jest.Mock).mockReturnValue({}) - const { getByText, getByTestId } = render() - expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() - expect(getByText('Back')).toBeInTheDocument() + render() + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() + expect(screen.getByText('Back')).toBeInTheDocument() }) describe('Velocity', () => { @@ -26,18 +26,19 @@ describe('board', () => { { id: 1, name: 'name2', valueList: [{ value: 2 }] }, ], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Velocity')).toBeInTheDocument() - expect(getByTestId('Velocity')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(2) + render() + const velocityTable = screen.getByTestId('Velocity') + expect(screen.getByText('Velocity')).toBeInTheDocument() + expect(velocityTable).toBeInTheDocument() + expect(within(velocityTable).queryAllByTestId('tr').length).toBe(2) }) it('should not show velocity when velocity data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ velocityList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Velocity').length).toEqual(0) + render() + expect(screen.queryAllByText('Velocity').length).toEqual(0) }) }) @@ -50,18 +51,19 @@ describe('board', () => { { id: 2, name: 'name3', valueList: [{ value: 3 }] }, ], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Cycle Time')).toBeInTheDocument() - expect(getByTestId('Cycle Time')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(3) + render() + const cycleTimeTable = screen.getByTestId('Cycle Time') + expect(screen.getByText('Cycle Time')).toBeInTheDocument() + expect(cycleTimeTable).toBeInTheDocument() + expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(3) }) it('should not show cycle time when cycle time data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ cycleTimeList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Cycle Time').length).toEqual(0) + render() + expect(screen.queryAllByText('Cycle Time').length).toEqual(0) }) }) @@ -75,18 +77,20 @@ describe('board', () => { { id: 3, name: 'name4', valuesList: [{ name: 'test4', value: 4 }] }, ], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Classification')).toBeInTheDocument() - expect(getByTestId('Classification')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(8) + + render() + const classificationTable = screen.getByTestId('Classification') + expect(screen.getByText('Classification')).toBeInTheDocument() + expect(classificationTable).toBeInTheDocument() + expect(within(classificationTable).queryAllByTestId('tr').length).toBe(8) }) it('should not show classifications when classifications data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ classification: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Classification').length).toEqual(0) + render() + expect(screen.queryAllByText('Classification').length).toEqual(0) }) }) @@ -103,16 +107,19 @@ describe('board', () => { { id: 2, name: 'name3', valuesList: [{ name: 'test3', value: 3 }] }, ], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Velocity')).toBeInTheDocument() - expect(getByTestId('Velocity')).toBeInTheDocument() - expect(getByText('Cycle Time')).toBeInTheDocument() - expect(getByTestId('Cycle Time')).toBeInTheDocument() - expect(getByText('Classification')).toBeInTheDocument() - expect(getByTestId('Classification')).toBeInTheDocument() + render() + const velocityTable = screen.getByTestId('Velocity') + const cycleTimeTable = screen.getByTestId('Cycle Time') + const classificationTable = screen.getByTestId('Classification') + expect(screen.getByText('Velocity')).toBeInTheDocument() + expect(velocityTable).toBeInTheDocument() + expect(screen.getByText('Cycle Time')).toBeInTheDocument() + expect(cycleTimeTable).toBeInTheDocument() + expect(screen.getByText('Classification')).toBeInTheDocument() + expect(classificationTable).toBeInTheDocument() - expect(container.querySelectorAll('table[data-test-id="Velocity"] > tbody > tr').length).toBe(1) - expect(container.querySelectorAll('table[data-test-id="Cycle Time"] > tbody > tr').length).toBe(2) - expect(container.querySelectorAll('table[data-test-id="Classification"] > tbody > tr').length).toBe(6) + expect(within(velocityTable).queryAllByTestId('tr').length).toBe(1) + expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(2) + expect(within(classificationTable).queryAllByTestId('tr').length).toBe(6) }) }) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx index ef465352d0..ab48c21e93 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx @@ -1,5 +1,5 @@ import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { render } from '@testing-library/react' +import { render, screen, within } from '@testing-library/react' import { reportMapper } from '@src/hooks/reportMapper/report' import { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail' import React from 'react' @@ -12,9 +12,9 @@ describe('DoraDetail', () => { it('should render a back link', () => { ;(reportMapper as jest.Mock).mockReturnValue({}) - const { getByText, getByTestId } = render() - expect(getByTestId('ArrowBackIcon')).toBeInTheDocument() - expect(getByText('Back')).toBeInTheDocument() + render() + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() + expect(screen.getByText('Back')).toBeInTheDocument() }) describe('Deployment Frequency', () => { @@ -22,18 +22,19 @@ describe('DoraDetail', () => { ;(reportMapper as jest.Mock).mockReturnValue({ deploymentFrequencyList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Deployment Frequency')).toBeInTheDocument() - expect(getByTestId('Deployment Frequency')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(2) + render() + const deploymentFrequencyTable = screen.getByTestId('Deployment Frequency') + expect(screen.getByText('Deployment Frequency')).toBeInTheDocument() + expect(deploymentFrequencyTable).toBeInTheDocument() + expect(within(deploymentFrequencyTable).queryAllByTestId('tr').length).toBe(2) }) it('should not show deploymentFrequencyList when deploymentFrequencyList data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ deploymentFrequencyList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Deployment Frequency').length).toEqual(0) + render() + expect(screen.queryAllByText('Deployment Frequency').length).toEqual(0) }) }) @@ -42,18 +43,19 @@ describe('DoraDetail', () => { ;(reportMapper as jest.Mock).mockReturnValue({ leadTimeForChangesList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Lead Time For Changes')).toBeInTheDocument() - expect(getByTestId('Lead Time For Changes')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(2) + render() + const leadTimeForChangesTable = screen.getByTestId('Lead Time For Changes') + expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument() + expect(leadTimeForChangesTable).toBeInTheDocument() + expect(within(leadTimeForChangesTable).queryAllByTestId('tr').length).toBe(2) }) it('should not show leadTimeForChangesList when leadTimeForChangesList data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ leadTimeForChangesList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Lead Time For Changes').length).toEqual(0) + render() + expect(screen.queryAllByText('Lead Time For Changes').length).toEqual(0) }) }) @@ -62,18 +64,19 @@ describe('DoraDetail', () => { ;(reportMapper as jest.Mock).mockReturnValue({ changeFailureRateList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Change Failure Rate')).toBeInTheDocument() - expect(getByTestId('Change Failure Rate')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(2) + render() + const changeFailureRateTable = screen.getByTestId('Change Failure Rate') + expect(screen.getByText('Change Failure Rate')).toBeInTheDocument() + expect(changeFailureRateTable).toBeInTheDocument() + expect(within(changeFailureRateTable).queryAllByTestId('tr').length).toBe(2) }) it('should not show changeFailureRateList when changeFailureRateList data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ changeFailureRateList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Change Failure Rate').length).toEqual(0) + render() + expect(screen.queryAllByText('Change Failure Rate').length).toEqual(0) }) }) @@ -82,18 +85,19 @@ describe('DoraDetail', () => { ;(reportMapper as jest.Mock).mockReturnValue({ meanTimeToRecoveryList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], }) - const { getByText, getByTestId, container } = render() - expect(getByText('Mean Time To Recovery')).toBeInTheDocument() - expect(getByTestId('Mean Time To Recovery')).toBeInTheDocument() - expect(container.querySelectorAll('tbody > tr').length).toBe(2) + render() + const meanTimeToRecoveryTable = screen.getByTestId('Mean Time To Recovery') + expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument() + expect(meanTimeToRecoveryTable).toBeInTheDocument() + expect(within(meanTimeToRecoveryTable).queryAllByTestId('tr').length).toBe(2) }) it('should not show meanTimeToRecoveryList when meanTimeToRecoveryList data is not existing', () => { ;(reportMapper as jest.Mock).mockReturnValue({ meanTimeToRecoveryList: null, }) - const { queryAllByText } = render() - expect(queryAllByText('Mean Time To Recovery').length).toEqual(0) + render() + expect(screen.queryAllByText('Mean Time To Recovery').length).toEqual(0) }) }) }) diff --git a/frontend/src/components/Common/ReportForThreeColumns/index.tsx b/frontend/src/components/Common/ReportForThreeColumns/index.tsx index b42c88a9db..17152a1378 100644 --- a/frontend/src/components/Common/ReportForThreeColumns/index.tsx +++ b/frontend/src/components/Common/ReportForThreeColumns/index.tsx @@ -42,11 +42,11 @@ export const ReportForThreeColumns = ({ title, fieldName, listName, data }: Repo const renderRows = () => data.slice(0, data.length === 2 && data[1].name === AVERAGE_FIELD ? 1 : data.length).map((row) => ( - + {emojiRow(row)} {row.valuesList.map((valuesList) => ( - + {valuesList.name} {valuesList.value} diff --git a/frontend/src/components/Common/ReportForTwoColumns/index.tsx b/frontend/src/components/Common/ReportForTwoColumns/index.tsx index 1bd4893750..89d6ce83f3 100644 --- a/frontend/src/components/Common/ReportForTwoColumns/index.tsx +++ b/frontend/src/components/Common/ReportForTwoColumns/index.tsx @@ -19,14 +19,14 @@ export const ReportForTwoColumns = ({ title, data }: ReportForTwoColumnsProps) = const renderRows = () => { return data.map((row) => ( - + {row.name} {row.valueList[0]?.unit ? `${row.valueList[0].value}${row.valueList[0].unit}` : row.valueList[0].value} {row.valueList.slice(1).map((data) => ( - + {`${data.value}${data.unit}`} ))} From dff3aad55957bc3bbb29e1ac948a07d0a073e88d Mon Sep 17 00:00:00 2001 From: "junbo.dai" Date: Wed, 10 Jan 2024 10:51:13 +0800 Subject: [PATCH 70/90] ADM-717[backend]refactor: refactor SourceType and test --- .../controller/source/GithubController.java | 34 ++++-- .../controller/source/SourceType.java | 25 ++++ .../controller/source/SourceTypeEnum.java | 29 ----- .../source/dto/SourceControlDTO.java | 4 +- .../source/dto/VerifyBranchRequest.java | 4 +- .../service/source/github/GitHubService.java | 25 ++-- .../source/GithubControllerTest.java | 8 +- .../source/github/GithubServiceTest.java | 109 ++++++++---------- 8 files changed, 124 insertions(+), 114 deletions(-) create mode 100644 backend/src/main/java/heartbeat/controller/source/SourceType.java delete mode 100644 backend/src/main/java/heartbeat/controller/source/SourceTypeEnum.java diff --git a/backend/src/main/java/heartbeat/controller/source/GithubController.java b/backend/src/main/java/heartbeat/controller/source/GithubController.java index 31a2d8b869..3f9b16676d 100644 --- a/backend/src/main/java/heartbeat/controller/source/GithubController.java +++ b/backend/src/main/java/heartbeat/controller/source/GithubController.java @@ -28,11 +28,13 @@ @Log4j2 public class GithubController { + public static final String TOKEN_PATTER = "^(ghp|gho|ghu|ghs|ghr)_([a-zA-Z0-9]{36})$"; + private final GitHubService gitHubService; @PostMapping @CrossOrigin - @Deprecated + @Deprecated(since = "frontend completed") @ResponseStatus(HttpStatus.OK) public GitHubResponse getRepos(@RequestBody @Valid SourceControlDTO sourceControlDTO) { log.info("Start to get repos by token"); @@ -45,24 +47,34 @@ public GitHubResponse getRepos(@RequestBody @Valid SourceControlDTO sourceContro @PostMapping("/{sourceType}/verify") public ResponseEntity verifyToken(@PathVariable @NotBlank String sourceType, @RequestBody @Valid SourceControlDTO sourceControlDTO) { - if (!SourceTypeEnum.isValidType(sourceType)) { - throw new BadRequestException("Source type is incorrect."); + log.info("Start to verify source type: {} token.", sourceType); + switch (SourceType.matchSourceType(sourceType)) { + case GITHUB -> { + gitHubService.verifyTokenV2(sourceControlDTO.getToken()); + log.info("Successfully verify source type: {} token.", sourceType); + } + default -> { + log.error("Failed to verify source type: {} token.", sourceType); + throw new BadRequestException("Source type is incorrect."); + } } - log.info("Start to verify token"); - gitHubService.verifyTokenV2(sourceControlDTO.getToken()); - log.info("Successfully to verify token"); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } @PostMapping("/{sourceType}/repos/branches/{branch}/verify") public ResponseEntity verifyBranch(@PathVariable @NotBlank String sourceType, @PathVariable @NotBlank String branch, @RequestBody @Valid VerifyBranchRequest request) { - if (!SourceTypeEnum.isValidType(sourceType)) { - throw new BadRequestException("Source type is incorrect."); + log.info("Start to verify source type: {} branch: {}.", sourceType, branch); + switch (SourceType.matchSourceType(sourceType)) { + case GITHUB -> { + gitHubService.verifyCanReadTargetBranch(request.getRepository(), branch, request.getToken()); + log.info("Successfully verify source type: {} branch: {}.", sourceType, branch); + } + default -> { + log.error("Failed to verify source type: {} branch: {}.", sourceType, branch); + throw new BadRequestException("Source type is incorrect."); + } } - log.info("Start to verify target branch."); - gitHubService.verifyCanReadTargetBranch(request.getRepository(), branch, request.getToken()); - log.info("Successfully to verify target branch."); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } diff --git a/backend/src/main/java/heartbeat/controller/source/SourceType.java b/backend/src/main/java/heartbeat/controller/source/SourceType.java new file mode 100644 index 0000000000..daa583dcef --- /dev/null +++ b/backend/src/main/java/heartbeat/controller/source/SourceType.java @@ -0,0 +1,25 @@ +package heartbeat.controller.source; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public enum SourceType { + + GITHUB("github"), + + ANOTHERSOURCETYPE("anotherSourceType"); + + private String value; + + public static SourceType matchSourceType(String value) { + return switch (value) { + case "github" -> GITHUB; + default -> ANOTHERSOURCETYPE; + }; + } + +} diff --git a/backend/src/main/java/heartbeat/controller/source/SourceTypeEnum.java b/backend/src/main/java/heartbeat/controller/source/SourceTypeEnum.java deleted file mode 100644 index cd110e9dd1..0000000000 --- a/backend/src/main/java/heartbeat/controller/source/SourceTypeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package heartbeat.controller.source; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.util.EnumSet; -import java.util.Set; -import java.util.stream.Collectors; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public enum SourceTypeEnum { - - GITHUB("GitHub"); - - private String value; - - private static final Set SourceTypes = EnumSet.allOf(SourceTypeEnum.class) - .stream() - .map(SourceTypeEnum::getValue) - .collect(Collectors.toSet()); - - public static boolean isValidType(String value) { - return SourceTypes.contains(value); - } - -} diff --git a/backend/src/main/java/heartbeat/controller/source/dto/SourceControlDTO.java b/backend/src/main/java/heartbeat/controller/source/dto/SourceControlDTO.java index 0544070452..93c0672e17 100644 --- a/backend/src/main/java/heartbeat/controller/source/dto/SourceControlDTO.java +++ b/backend/src/main/java/heartbeat/controller/source/dto/SourceControlDTO.java @@ -8,6 +8,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import static heartbeat.controller.source.GithubController.TOKEN_PATTER; + @Builder @NoArgsConstructor @AllArgsConstructor @@ -16,7 +18,7 @@ public class SourceControlDTO { @NotNull(message = "Token cannot be empty.") - @Pattern(regexp = "^(ghp|gho|ghu|ghs|ghr)_([a-zA-Z0-9]{36})$", message = "token's pattern is incorrect") + @Pattern(regexp = TOKEN_PATTER, message = "token's pattern is incorrect") private String token; } diff --git a/backend/src/main/java/heartbeat/controller/source/dto/VerifyBranchRequest.java b/backend/src/main/java/heartbeat/controller/source/dto/VerifyBranchRequest.java index 1fb7675b55..dc25ef6f80 100644 --- a/backend/src/main/java/heartbeat/controller/source/dto/VerifyBranchRequest.java +++ b/backend/src/main/java/heartbeat/controller/source/dto/VerifyBranchRequest.java @@ -9,6 +9,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import static heartbeat.controller.source.GithubController.TOKEN_PATTER; + @Builder @NoArgsConstructor @AllArgsConstructor @@ -17,7 +19,7 @@ public class VerifyBranchRequest { @NotNull(message = "Token cannot be empty.") - @Pattern(regexp = "^(ghp|gho|ghu|ghs|ghr)_([a-zA-Z0-9]{36})$", message = "token's pattern is incorrect") + @Pattern(regexp = TOKEN_PATTER, message = "token's pattern is incorrect") private String token; @NotBlank(message = "Repository is required.") 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 fa035be68d..5838fa3a12 100644 --- a/backend/src/main/java/heartbeat/service/source/github/GitHubService.java +++ b/backend/src/main/java/heartbeat/service/source/github/GitHubService.java @@ -41,6 +41,10 @@ @Log4j2 public class GitHubService { + public static final String TOKEN_TITLE = "token "; + + public static final String BEARER_TITLE = "Bearer "; + private final ThreadPoolTaskExecutor customTaskExecutor; private final GitHubFeignClient gitHubFeignClient; @@ -53,7 +57,7 @@ public void shutdownExecutor() { @Deprecated public GitHubResponse verifyToken(String githubToken) { try { - String token = "token " + githubToken; + String token = TOKEN_TITLE + githubToken; log.info("Start to query repository url by token"); CompletableFuture> githubReposByUserFuture = CompletableFuture .supplyAsync(() -> gitHubFeignClient.getAllRepos(token), customTaskExecutor); @@ -91,7 +95,7 @@ public GitHubResponse verifyToken(String githubToken) { public void verifyTokenV2(String githubToken) { try { - String token = "token " + githubToken; + String token = TOKEN_TITLE + githubToken; log.info("Start to request github with token"); gitHubFeignClient.verifyToken(token); log.info("Successfully verify token from github"); @@ -109,22 +113,23 @@ public void verifyTokenV2(String githubToken) { public void verifyCanReadTargetBranch(String repository, String branch, String githubToken) { try { - String token = "token " + githubToken; - log.info("Start to request github with token"); + String token = TOKEN_TITLE + githubToken; + log.info("Start to request github branch: {}", branch); gitHubFeignClient.verifyCanReadTargetBranch(GithubUtil.getGithubUrlFullName(repository), branch, token); - log.info("Successfully verify token from github"); + log.info("Successfully verify target branch for github, branch: {}", branch); } catch (NotFoundException e) { - throw new PermissionDenyException("Unable to read target branch"); + log.error("Failed to call GitHub with branch: {}, error: {} ", branch, e.getMessage()); + throw new PermissionDenyException(String.format("Unable to read target branch: %s", branch)); } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); - log.error("Failed to call GitHub with token_error: {} ", cause.getMessage()); + log.error("Failed to call GitHub branch:{} with error: {} ", branch, cause.getMessage()); if (cause instanceof BaseException baseException) { throw baseException; } throw new InternalServerErrorException( - String.format("Failed to call GitHub with token_error: %s", cause.getMessage())); + String.format("Failed to call GitHub branch: %s with error: %s", branch, cause.getMessage())); } } @@ -159,7 +164,7 @@ private CompletableFuture> getAllGitHubReposAsync(String token, public List fetchPipelinesLeadTime(List deployTimes, Map repositories, String token) { try { - String realToken = "Bearer " + token; + String realToken = BEARER_TITLE + token; List pipelineInfoOfRepositories = getInfoOfRepositories(deployTimes, repositories); @@ -329,7 +334,7 @@ public LeadTime mapLeadTimeWithInfo(PullRequestInfo pullRequestInfo, DeployInfo public CommitInfo fetchCommitInfo(String commitId, String repositoryId, String token) { try { - String realToken = "Bearer " + token; + String realToken = BEARER_TITLE + token; log.info("Start to get commit info, repoId: {},commitId: {}", repositoryId, commitId); CommitInfo commitInfo = gitHubFeignClient.getCommitInfo(repositoryId, commitId, realToken); log.info("Successfully get commit info, repoId: {},commitId: {}, author: {}", repositoryId, commitId, diff --git a/backend/src/test/java/heartbeat/controller/source/GithubControllerTest.java b/backend/src/test/java/heartbeat/controller/source/GithubControllerTest.java index e6f36ca419..3a99f3fa1c 100644 --- a/backend/src/test/java/heartbeat/controller/source/GithubControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/source/GithubControllerTest.java @@ -67,7 +67,7 @@ void shouldReturnNoContentStatusWhenVerifyToken() throws Exception { SourceControlDTO sourceControlDTO = SourceControlDTO.builder().token(GITHUB_TOKEN).build(); mockMvc - .perform(post("/source-control/GitHub/verify") + .perform(post("/source-control/github/verify") .content(new ObjectMapper().writeValueAsString(sourceControlDTO)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNoContent()); @@ -82,7 +82,7 @@ void shouldReturnNoContentStatusWhenVerifyTargetBranch() throws Exception { doNothing().when(gitHubVerifyService).verifyCanReadTargetBranch("fake/repo", "main", GITHUB_TOKEN); mockMvc - .perform(post("/source-control/GitHub/repos/branches/main/verify") + .perform(post("/source-control/github/repos/branches/main/verify") .content(new ObjectMapper().writeValueAsString(verifyBranchRequest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNoContent()); @@ -161,7 +161,7 @@ void shouldReturnBadRequestGivenSourceTypeIsWrongWhenVerifyToken() throws Except SourceControlDTO sourceControlDTO = SourceControlDTO.builder().token(GITHUB_TOKEN).build(); final var response = mockMvc - .perform(post("/source-control/github/verify") + .perform(post("/source-control/GitHub/verify") .content(new ObjectMapper().writeValueAsString(sourceControlDTO)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) @@ -181,7 +181,7 @@ void shouldReturnBadRequestGivenSourceTypeIsWrongWhenVerifyBranch() throws Excep .build(); final var response = mockMvc - .perform(post("/source-control/github/repos/branches/main/verify") + .perform(post("/source-control/GitHub/repos/branches/main/verify") .content(new ObjectMapper().writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) 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 099cda6ef4..da07d3658a 100644 --- a/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java +++ b/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java @@ -30,6 +30,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import static heartbeat.TestFixtures.GITHUB_REPOSITORY; +import static heartbeat.TestFixtures.GITHUB_TOKEN; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -38,7 +39,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; @@ -168,21 +168,20 @@ public ThreadPoolTaskExecutor getTaskExecutor() { @Test @Deprecated void shouldReturnNonRedundantGithubReposWhenCallGithubFeignClientApi() { - String githubToken = "123456"; + String githubToken = GITHUB_TOKEN; String token = "token " + githubToken; when(gitHubFeignClient.getAllRepos(token)).thenReturn(List.of(GitHubRepo.builder().htmlUrl("11111").build(), GitHubRepo.builder().htmlUrl("22222").build(), GitHubRepo.builder().htmlUrl("33333").build())); - when(gitHubFeignClient.getGithubOrganizationsInfo(token)) .thenReturn(List.of(GitHubOrganizationsInfo.builder().login("org1").build(), GitHubOrganizationsInfo.builder().login("org2").build())); - when(gitHubFeignClient.getReposByOrganizationName("org1", token)) .thenReturn(List.of(GitHubRepo.builder().htmlUrl("22222").build(), GitHubRepo.builder().htmlUrl("33333").build(), GitHubRepo.builder().htmlUrl("44444").build())); final var response = githubService.verifyToken(githubToken); githubService.shutdownExecutor(); + assertThat(response.getGithubRepos()).hasSize(4); assertThat(response.getGithubRepos()) .isEqualTo(new LinkedHashSet<>(List.of("11111", "22222", "33333", "44444"))); @@ -190,7 +189,7 @@ void shouldReturnNonRedundantGithubReposWhenCallGithubFeignClientApi() { @Test void shouldReturnGithubTokenIsVerifyWhenVerifyToken() { - String githubToken = "123456"; + String githubToken = GITHUB_TOKEN; String token = "token " + githubToken; doNothing().when(gitHubFeignClient).verifyToken(token); @@ -200,19 +199,19 @@ void shouldReturnGithubTokenIsVerifyWhenVerifyToken() { @Test void shouldReturnGithubBranchIsVerifyWhenVerifyBranch() { - String githubToken = "123456"; + String githubToken = GITHUB_TOKEN; String token = "token " + githubToken; - doNothing().when(gitHubFeignClient).verifyCanReadTargetBranch(any(), any(), any()); - assertDoesNotThrow(() -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubToken)); + githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubToken); + verify(gitHubFeignClient, times(1)).verifyCanReadTargetBranch("fake/repo", "main", token); } @Test @Deprecated void shouldReturnUnauthorizedStatusWhenCallGithubFeignClientApiWithWrongToken() { - String wrongGithubToken = "123456"; + String wrongGithubToken = GITHUB_TOKEN; String token = "token " + wrongGithubToken; when(gitHubFeignClient.getAllRepos(token)) @@ -239,7 +238,7 @@ void shouldThrowExceptionWhenVerifyGitHubThrowUnExpectedException() { @Test @Deprecated void shouldGithubReturnEmptyWhenVerifyGithubThrowGithubRepoEmptyException() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; when(gitHubFeignClient.getReposByOrganizationName("org1", githubEmptyToken)).thenReturn(new ArrayList<>()); assertThatThrownBy(() -> githubService.verifyToken(githubEmptyToken)) @@ -249,7 +248,7 @@ void shouldGithubReturnEmptyWhenVerifyGithubThrowGithubRepoEmptyException() { @Test void shouldThrowExceptionWhenGithubReturnUnExpectedException() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; doThrow(new UnauthorizedException("Failed to get GitHub info_status: 401 UNAUTHORIZED, reason: ...")) .when(gitHubFeignClient) .verifyToken("token " + githubEmptyToken); @@ -260,7 +259,7 @@ void shouldThrowExceptionWhenGithubReturnUnExpectedException() { @Test void shouldThrowExceptionGivenGithubReturnUnExpectedExceptionWhenVerifyBranch() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; doThrow(new UnauthorizedException("Failed to get GitHub info_status: 401 UNAUTHORIZED, reason: ...")) .when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); @@ -272,32 +271,35 @@ void shouldThrowExceptionGivenGithubReturnUnExpectedExceptionWhenVerifyBranch() @Test void shouldThrowExceptionGivenGithubReturnPermissionDenyExceptionWhenVerifyBranch() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; doThrow(new NotFoundException("Failed to get GitHub info_status: 404, reason: ...")).when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); var exception = assertThrows(PermissionDenyException.class, () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); - assertEquals("Unable to read target branch", exception.getMessage()); + assertEquals("Unable to read target branch: main", exception.getMessage()); } @Test void shouldThrowExceptionGivenGithubReturnCompletionExceptionExceptionWhenVerifyToken() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; doThrow(new CompletionException(new Exception("UnExpected Exception"))).when(gitHubFeignClient) .verifyToken("token " + githubEmptyToken); - assertThrows(InternalServerErrorException.class, () -> githubService.verifyTokenV2(githubEmptyToken)); + var exception = assertThrows(InternalServerErrorException.class, + () -> githubService.verifyTokenV2(githubEmptyToken)); + assertEquals("Failed to call GitHub with token_error: UnExpected Exception", exception.getMessage()); } @Test void shouldThrowExceptionGivenGithubReturnUnauthorizedExceptionWhenVerifyBranch() { - String githubEmptyToken = "123456"; + String githubEmptyToken = GITHUB_TOKEN; doThrow(new CompletionException(new Exception("UnExpected Exception"))).when(gitHubFeignClient) .verifyCanReadTargetBranch("fake/repo", "main", "token " + githubEmptyToken); - assertThrows(InternalServerErrorException.class, + var exception = assertThrows(InternalServerErrorException.class, () -> githubService.verifyCanReadTargetBranch(GITHUB_REPOSITORY, "main", githubEmptyToken)); + assertEquals("Failed to call GitHub branch: main with error: UnExpected Exception", exception.getMessage()); } @Test @@ -305,6 +307,7 @@ void shouldReturnNullWhenMergeTimeIsNull() { PullRequestInfo pullRequestInfo = PullRequestInfo.builder().build(); DeployInfo deployInfo = DeployInfo.builder().build(); CommitInfo commitInfo = CommitInfo.builder().build(); + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); assertNull(result); @@ -312,7 +315,6 @@ void shouldReturnNullWhenMergeTimeIsNull() { @Test void shouldReturnLeadTimeWhenMergedTimeIsNotNull() { - LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); LeadTime expect = LeadTime.builder() .commitId("111") .prCreatedTime(1658548980000L) @@ -326,13 +328,14 @@ void shouldReturnLeadTimeWhenMergedTimeIsNotNull() { .totalTime(180000) .build(); + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); + assertEquals(expect, result); } @Test void CommitTimeInPrShouldBeZeroWhenCommitInfoIsNull() { commitInfo = CommitInfo.builder().build(); - LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); LeadTime expect = LeadTime.builder() .commitId("111") .prCreatedTime(1658548980000L) @@ -346,13 +349,14 @@ void CommitTimeInPrShouldBeZeroWhenCommitInfoIsNull() { .totalTime(180000) .build(); + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); + assertEquals(expect, result); } @Test void shouldReturnFirstCommitTimeInPrZeroWhenCommitInfoIsNull() { commitInfo = CommitInfo.builder().build(); - LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); LeadTime expect = LeadTime.builder() .commitId("111") .prCreatedTime(1658548980000L) @@ -366,17 +370,18 @@ void shouldReturnFirstCommitTimeInPrZeroWhenCommitInfoIsNull() { .totalTime(180000L) .build(); + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); + assertEquals(expect, result); } @Test void shouldReturnPipeLineLeadTimeWhenDeployITimesIsNotEmpty() { String mockToken = "mockToken"; - when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of(commitInfo)); when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(commitInfo); + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(pipelineLeadTimes, result); @@ -385,14 +390,13 @@ void shouldReturnPipeLineLeadTimeWhenDeployITimesIsNotEmpty() { @Test void shouldReturnEmptyLeadTimeWhenDeployTimesIsEmpty() { String mockToken = "mockToken"; - + List expect = List.of(PipelineLeadTime.builder().build()); when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of(commitInfo)); List emptyDeployTimes = List.of(DeployTimes.builder().build()); + List result = githubService.fetchPipelinesLeadTime(emptyDeployTimes, repositoryMap, mockToken); - List expect = List.of(PipelineLeadTime.builder().build()); assertEquals(expect, result); } @@ -400,14 +404,6 @@ void shouldReturnEmptyLeadTimeWhenDeployTimesIsEmpty() { @Test void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoIsEmpty() { String mockToken = "mockToken"; - - when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of()); - - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of()); - when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(new CommitInfo()); - - List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); - List expect = List.of(PipelineLeadTime.builder() .pipelineStep("Step") .pipelineName("Name") @@ -420,6 +416,11 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoIsEmpty() { .totalTime(120000) .build())) .build()); + when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of()); + when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of()); + when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(new CommitInfo()); + + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(expect, result); } @@ -427,14 +428,6 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoIsEmpty() { @Test void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoGot404Error() { String mockToken = "mockToken"; - - 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()); - - List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); - List expect = List.of(PipelineLeadTime.builder() .pipelineStep("Step") .pipelineName("Name") @@ -447,6 +440,11 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoGot404Error() { .totalTime(120000) .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()); + + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(expect, result); } @@ -455,12 +453,6 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoGot404Error() { void shouldReturnEmptyMergeLeadTimeWhenMergeTimeIsEmpty() { String mockToken = "mockToken"; pullRequestInfo.setMergedAt(null); - when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); - - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of()); - when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(new CommitInfo()); - List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); - List expect = List.of(PipelineLeadTime.builder() .pipelineStep("Step") .pipelineName("Name") @@ -473,6 +465,11 @@ void shouldReturnEmptyMergeLeadTimeWhenMergeTimeIsEmpty() { .totalTime(120000) .build())) .build()); + when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); + when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of()); + when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(new CommitInfo()); + + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(expect, result); } @@ -504,7 +501,7 @@ void shouldThrowCompletableExceptionIfGetPullRequestListInfoHasExceptionWhenFetc } @Test - public void shouldFetchCommitInfo() { + void shouldFetchCommitInfo() { CommitInfo commitInfo = CommitInfo.builder() .commit(Commit.builder() .author(Author.builder().name("XXXX").email("XXX@test.com").date("2023-05-10T06:43:02.653Z").build()) @@ -520,7 +517,7 @@ public void shouldFetchCommitInfo() { } @Test - public void shouldThrowPermissionDenyExceptionWhenFetchCommitInfo403Forbidden() { + void shouldThrowPermissionDenyExceptionWhenFetchCommitInfo403Forbidden() { when(gitHubFeignClient.getCommitInfo(anyString(), anyString(), anyString())) .thenThrow(new PermissionDenyException("request forbidden")); @@ -530,7 +527,7 @@ public void shouldThrowPermissionDenyExceptionWhenFetchCommitInfo403Forbidden() } @Test - public void shouldThrowInternalServerErrorExceptionWhenFetchCommitInfo500Exception() { + void shouldThrowInternalServerErrorExceptionWhenFetchCommitInfo500Exception() { when(gitHubFeignClient.getCommitInfo(anyString(), anyString(), anyString())).thenReturn(null); assertThatThrownBy(() -> githubService.fetchCommitInfo("12344", "", "")) @@ -539,7 +536,7 @@ public void shouldThrowInternalServerErrorExceptionWhenFetchCommitInfo500Excepti } @Test - public void shouldReturnNullWhenFetchCommitInfo404Exception() { + void shouldReturnNullWhenFetchCommitInfo404Exception() { when(gitHubFeignClient.getCommitInfo(anyString(), anyString(), anyString())).thenThrow(new NotFoundException("")); assertNull(githubService.fetchCommitInfo("12344", "", "")); @@ -548,12 +545,11 @@ public void shouldReturnNullWhenFetchCommitInfo404Exception() { @Test void shouldReturnPipeLineLeadTimeWhenDeployITimesIsNotEmptyAndCommitInfoError() { String mockToken = "mockToken"; - when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of(commitInfo)); when(gitHubFeignClient.getCommitInfo(any(), any(), any())) .thenThrow(new NotFoundException("Failed to get commit")); + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(pipelineLeadTimes, result); @@ -562,14 +558,12 @@ void shouldReturnPipeLineLeadTimeWhenDeployITimesIsNotEmptyAndCommitInfoError() @Test void shouldReturnPipeLineLeadTimeWhenDeployCommitShaIsDifferent() { String mockToken = "mockToken"; - pullRequestInfo = PullRequestInfo.builder() .mergedAt("2022-07-23T04:04:00.000+00:00") .createdAt("2022-07-23T04:03:00.000+00:00") .mergeCommitSha("222") .number(1) .build(); - pipelineLeadTimes = List.of(PipelineLeadTime.builder() .pipelineName("Name") .pipelineStep("Step") @@ -583,12 +577,11 @@ void shouldReturnPipeLineLeadTimeWhenDeployCommitShaIsDifferent() { .totalTime(120000) .build())) .build()); - when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); - when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of(commitInfo)); when(gitHubFeignClient.getCommitInfo(any(), any(), any())) .thenThrow(new NotFoundException("Failed to get commit")); + List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); assertEquals(pipelineLeadTimes, result); From a93e79bb439c3b51805b2c11e3f316d98312b988 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 10 Jan 2024 14:15:11 +0800 Subject: [PATCH 71/90] ADM-652 [frontend] test: add import --- .../Metrics/ReportStep/ReportDetail/withBack.test.tsx | 1 + frontend/src/components/Common/ReportForTwoColumns/index.tsx | 1 + .../src/components/Metrics/ReportStep/BoradMetrics/index.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx index 4f9ae25899..3d927495d2 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx @@ -1,5 +1,6 @@ import { withGoBack } from '@src/components/Metrics/ReportStep/ReportDetail/withBack' import { render, fireEvent, screen } from '@testing-library/react' +import React from 'react' describe('withGoBack', () => { const onBack = jest.fn() diff --git a/frontend/src/components/Common/ReportForTwoColumns/index.tsx b/frontend/src/components/Common/ReportForTwoColumns/index.tsx index 89d6ce83f3..c711776cd3 100644 --- a/frontend/src/components/Common/ReportForTwoColumns/index.tsx +++ b/frontend/src/components/Common/ReportForTwoColumns/index.tsx @@ -9,6 +9,7 @@ import { import { Fragment } from 'react' import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' import { ReportSelectionTitle } from '@src/components/Metrics/MetricsStep/style' +import React from 'react' interface ReportForTwoColumnsProps { title: string diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index b77de0fc70..a383e9fceb 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -131,7 +131,7 @@ const BoardMetrics = ({ - {boardReport?.isBoardMetricsReady && {'show more >'}} + {boardReport?.isBoardMetricsReady && {SHOW_MORE}} From a3e9869613de0659e355ae74b2b6ab577da54d86 Mon Sep 17 00:00:00 2001 From: Yunsong Date: Wed, 10 Jan 2024 14:42:27 +0800 Subject: [PATCH 72/90] ADM-727:[backend]refactor: refactor code to keep consistence --- .../report/GenerateReporterService.java | 34 +++++++++---------- .../src/main/java/heartbeat/util/IdUtil.java | 6 ++-- .../designs/refinement-on-generate-report.mdx | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java index 10fde01a21..b336c190b1 100644 --- a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java +++ b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java @@ -203,9 +203,9 @@ else if (jsonObject.has("key")) { public void generateBoardReport(GenerateReportRequest request) { initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); log.info( - "Start to generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + "Start to generate board report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _boardReportId: {}", request.getMetrics(), request.getConsiderHoliday(), request.getStartTime(), request.getEndTime(), - IdUtil.getPipelineReportId(request.getCsvTimeStamp())); + IdUtil.getBoardReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { saveReporterInHandler(generateReporter(request), IdUtil.getBoardReportId(request.getCsvTimeStamp())); @@ -226,7 +226,7 @@ public void generateDoraReport(GenerateReportRequest request) { initializeMetricsDataReadyInHandler(request.getCsvTimeStamp(), request.getMetrics()); if (Objects.nonNull(metricsDataStatus.isSourceControlMetricsReady()) && metricsDataStatus.isSourceControlMetricsReady()) { - generateCodeBaseReport(request); + generateSourceControlReport(request); } if (Objects.nonNull(metricsDataStatus.isPipelineMetricsReady()) && metricsDataStatus.isPipelineMetricsReady()) { generatePipelineReport(request); @@ -237,7 +237,7 @@ public void generateDoraReport(GenerateReportRequest request) { private void generatePipelineReport(GenerateReportRequest request) { GenerateReportRequest pipelineRequest = request.convertToPipelineRequest(request); log.info( - "Start to generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + "Start to generate pipeline report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _pipelineReportId: {}", pipelineRequest.getMetrics(), pipelineRequest.getConsiderHoliday(), pipelineRequest.getStartTime(), pipelineRequest.getEndTime(), IdUtil.getPipelineReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { @@ -257,25 +257,25 @@ private void generatePipelineReport(GenerateReportRequest request) { }); } - private void generateCodeBaseReport(GenerateReportRequest request) { + private void generateSourceControlReport(GenerateReportRequest request) { GenerateReportRequest codebaseRequest = request.convertToCodeBaseRequest(request); log.info( - "Start to generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _doraReportId: {}", + "Start to generate source control report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _sourceControlReportId: {}", codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), codebaseRequest.getStartTime(), - codebaseRequest.getEndTime(), IdUtil.getPipelineReportId(request.getCsvTimeStamp())); + codebaseRequest.getEndTime(), IdUtil.getSourceControlReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { saveReporterInHandler(generateReporter(codebaseRequest), - IdUtil.getCodeBaseReportId(codebaseRequest.getCsvTimeStamp())); + IdUtil.getSourceControlReportId(codebaseRequest.getCsvTimeStamp())); updateMetricsDataReadyInHandler(codebaseRequest.getCsvTimeStamp(), codebaseRequest.getMetrics()); log.info( - "Successfully generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _codeBaseReportId: {}", + "Successfully generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _sourceControlReportId: {}", codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), codebaseRequest.getStartTime(), codebaseRequest.getEndTime(), - IdUtil.getCodeBaseReportId(request.getCsvTimeStamp())); + IdUtil.getSourceControlReportId(request.getCsvTimeStamp())); } catch (BaseException e) { - asyncExceptionHandler.put(IdUtil.getCodeBaseReportId(request.getCsvTimeStamp()), e); + asyncExceptionHandler.put(IdUtil.getSourceControlReportId(request.getCsvTimeStamp()), e); } }); } @@ -708,17 +708,17 @@ private Boolean checkCurrentMetricsReadyState(Boolean exist, Boolean previousVal } private MetricsDataReady getMetricsStatus(List metrics, Boolean flag) { - List lowerMetrics = metrics.stream().map(String::toLowerCase).collect(Collectors.toList()); + List lowerMetrics = metrics.stream().map(String::toLowerCase).toList(); boolean boardMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getBoardMetrics(lowerMetrics)); boolean codebaseMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getCodeBaseMetrics(lowerMetrics)); boolean buildKiteMetricsExist = CollectionUtils.isNotEmpty(MetricsUtil.getPipelineMetrics(lowerMetrics)); Boolean isBoardMetricsReady = boardMetricsExist ? flag : null; - Boolean isCodebaseMetricsReady = codebaseMetricsExist ? flag : null; - Boolean isBuildKiteMetricsReady = buildKiteMetricsExist ? flag : null; + Boolean isPipelineMetricsReady = buildKiteMetricsExist ? flag : null; + Boolean isSourceControlMetricsReady = codebaseMetricsExist ? flag : null; return MetricsDataReady.builder() .isBoardMetricsReady(isBoardMetricsReady) - .isPipelineMetricsReady(isBuildKiteMetricsReady) - .isSourceControlMetricsReady(isCodebaseMetricsReady) + .isPipelineMetricsReady(isPipelineMetricsReady) + .isSourceControlMetricsReady(isSourceControlMetricsReady) .build(); } @@ -869,7 +869,7 @@ public ReportResponse getReportFromHandler(String reportId) { public ReportResponse getComposedReportResponse(String reportId, boolean isReportReady) { ReportResponse boardReportResponse = getReportFromHandler(IdUtil.getBoardReportId(reportId)); ReportResponse doraReportResponse = getReportFromHandler(IdUtil.getPipelineReportId(reportId)); - ReportResponse codebaseReportResponse = getReportFromHandler(IdUtil.getCodeBaseReportId(reportId)); + ReportResponse codebaseReportResponse = getReportFromHandler(IdUtil.getSourceControlReportId(reportId)); MetricsDataReady metricsDataReady = asyncReportRequestHandler.getMetricsDataReady(reportId); ReportResponse response = Optional.ofNullable(boardReportResponse).orElse(doraReportResponse); diff --git a/backend/src/main/java/heartbeat/util/IdUtil.java b/backend/src/main/java/heartbeat/util/IdUtil.java index 72196a00c4..1fdfc1952b 100644 --- a/backend/src/main/java/heartbeat/util/IdUtil.java +++ b/backend/src/main/java/heartbeat/util/IdUtil.java @@ -6,7 +6,7 @@ public interface IdUtil { String PIPELINE_REPORT_PREFIX = "pipeline-"; - String CODE_BASE_PREFIX = "github-"; + String SOURCE_CONTROL_PREFIX = "sourceControl-"; static String getBoardReportId(String timeStamp) { return BOARD_REPORT_PREFIX + timeStamp; @@ -16,8 +16,8 @@ static String getPipelineReportId(String timeStamp) { return PIPELINE_REPORT_PREFIX + timeStamp; } - static String getCodeBaseReportId(String timeStamp) { - return CODE_BASE_PREFIX + timeStamp; + static String getSourceControlReportId(String timeStamp) { + return SOURCE_CONTROL_PREFIX + timeStamp; } static String getTimeStampFromReportId(String reportId) { diff --git a/docs/src/content/docs/en/designs/refinement-on-generate-report.mdx b/docs/src/content/docs/en/designs/refinement-on-generate-report.mdx index 90b90d5747..88f027ed60 100644 --- a/docs/src/content/docs/en/designs/refinement-on-generate-report.mdx +++ b/docs/src/content/docs/en/designs/refinement-on-generate-report.mdx @@ -269,7 +269,7 @@ end + Async generate board report ``` -POST /reports/board +POST /reports/{board} Request payload: { @@ -319,7 +319,7 @@ Response: ``` + Async generate dora report ``` -POST /reports/dora +POST /reports/{dora} Request payload: { From e5f99a7967bc4022cfafceaaf85af40285d40423 Mon Sep 17 00:00:00 2001 From: JiangRu1 <3246736839@qq.com> Date: Wed, 10 Jan 2024 14:48:02 +0800 Subject: [PATCH 73/90] ADM-652: [frontend] test: fix report tests --- .../Metrics/ReportStep/ReportStep.test.tsx | 145 +++++++++--------- 1 file changed, 70 insertions(+), 75 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index d829dee728..d961d331cf 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -1,4 +1,4 @@ -import { act, render, renderHook, waitFor, waitForElementToBeRemoved, screen } from '@testing-library/react' +import { render, renderHook, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react' import ReportStep from '@src/components/Metrics/ReportStep' import { BACK, @@ -130,90 +130,90 @@ describe('Report Step', () => { describe('render correctly', () => { it('should render report page', () => { - const { getByText } = setup(REQUIRED_DATA_LIST) - - expect(getByText('Board Metrics')).toBeInTheDocument() - expect(getByText('Velocity')).toBeInTheDocument() - expect(getByText('Cycle Time')).toBeInTheDocument() - expect(getByText('DORA Metrics')).toBeInTheDocument() - expect(getByText('Lead Time For Changes')).toBeInTheDocument() - expect(getByText('Deployment Frequency')).toBeInTheDocument() - expect(getByText('Change Failure Rate')).toBeInTheDocument() - expect(getByText('Mean Time To Recovery')).toBeInTheDocument() + setup(REQUIRED_DATA_LIST) + + expect(screen.getByText('Board Metrics')).toBeInTheDocument() + expect(screen.getByText('Velocity')).toBeInTheDocument() + expect(screen.getByText('Cycle Time')).toBeInTheDocument() + expect(screen.getByText('DORA Metrics')).toBeInTheDocument() + expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument() + expect(screen.getByText('Deployment Frequency')).toBeInTheDocument() + expect(screen.getByText('Change Failure Rate')).toBeInTheDocument() + expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument() }) it('should render loading page when report data is empty', () => { reportHook.current.reportData = EMPTY_REPORT_VALUES - const { getAllByTestId } = setup(REQUIRED_DATA_LIST) + setup(REQUIRED_DATA_LIST) - expect(getAllByTestId('loading-page')).toHaveLength(6) + expect(screen.getAllByTestId('loading-page')).toHaveLength(6) }) it('should render the velocity component with correct props', async () => { - const { getByText } = setup([REQUIRED_DATA_LIST[1]]) + setup([REQUIRED_DATA_LIST[1]]) - expect(getByText('20')).toBeInTheDocument() - expect(getByText('14')).toBeInTheDocument() + expect(screen.getByText('20')).toBeInTheDocument() + expect(screen.getByText('14')).toBeInTheDocument() }) it('should render the CycleTime component with correct props', () => { - const { getByText } = setup([REQUIRED_DATA_LIST[2]]) + setup([REQUIRED_DATA_LIST[2]]) - expect(getByText('30.26')).toBeInTheDocument() - expect(getByText('21.18')).toBeInTheDocument() + expect(screen.getByText('30.26')).toBeInTheDocument() + expect(screen.getByText('21.18')).toBeInTheDocument() }) it('should render the Lead Time For Change component with correct props', () => { - const { getByText } = setup([REQUIRED_DATA_LIST[4]]) + setup([REQUIRED_DATA_LIST[4]]) - expect(getByText('60.79')).toBeInTheDocument() - expect(getByText('39.03')).toBeInTheDocument() - expect(getByText('99.82')).toBeInTheDocument() + expect(screen.getByText('60.79')).toBeInTheDocument() + expect(screen.getByText('39.03')).toBeInTheDocument() + expect(screen.getByText('99.82')).toBeInTheDocument() }) it('should render the Deployment frequency component with correct props', () => { - const { getByText } = setup([REQUIRED_DATA_LIST[5]]) + setup([REQUIRED_DATA_LIST[5]]) - expect(getByText('0.40')).toBeInTheDocument() + expect(screen.getByText('0.40')).toBeInTheDocument() }) it('should render the Change failure rate component with correct props', () => { - const { getByText } = setup([REQUIRED_DATA_LIST[6]]) + setup([REQUIRED_DATA_LIST[6]]) - expect(getByText('0.00')).toBeInTheDocument() - expect(getByText('% (0/6)')).toBeInTheDocument() + expect(screen.getByText('0.00')).toBeInTheDocument() + expect(screen.getByText('% (0/6)')).toBeInTheDocument() }) it('should render the Mean time to recovery component with correct props', () => { - const { getByText } = setup([REQUIRED_DATA_LIST[7]]) + setup([REQUIRED_DATA_LIST[7]]) - expect(getByText('4.00')).toBeInTheDocument() + expect(screen.getByText('4.00')).toBeInTheDocument() }) it('should show errorMessage when generating report has error message', () => { reportHook.current.errorMessage = 'error message' - const { getByText } = setup(['']) + setup(['']) - expect(getByText('error message')).toBeInTheDocument() + expect(screen.getByText('error message')).toBeInTheDocument() }) }) describe('behavior', () => { it('should call handleBack method when clicking back button given back button enabled', async () => { - const { getByText } = setup(['']) + setup(['']) - const back = getByText(PREVIOUS) + const back = screen.getByText(PREVIOUS) await userEvent.click(back) expect(backStep).toHaveBeenCalledTimes(1) }) it('should call handleSaveMock method when clicking save button', async () => { - const { getByText } = setup(['']) + setup(['']) - const save = getByText(SAVE) + const save = screen.getByText(SAVE) await userEvent.click(save) expect(handleSaveMock).toHaveBeenCalledTimes(1) @@ -266,62 +266,57 @@ describe('Report Step', () => { it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should render detail page when clicking show more button given metric %s', async (requiredData) => { - const { getByText } = setup([requiredData]) + setup([requiredData]) - const showMore = getByText(SHOW_MORE) + const showMore = screen.getByText(SHOW_MORE) - act(() => { - userEvent.click(showMore) - }) + await userEvent.click(showMore) - await waitForElementToBeRemoved(showMore) - expect(getByText(BACK)).toBeInTheDocument() + await waitFor(() => { + waitForElementToBeRemoved(showMore) + }) + expect(screen.getByText(BACK)).toBeInTheDocument() } ) it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should return report page when clicking back button in Breadcrumb in detail page given metric %s', async (requiredData) => { - const { getByText } = setup([requiredData]) + setup([requiredData]) - const showMore = getByText(SHOW_MORE) + await userEvent.click(screen.getByText(SHOW_MORE)) - act(() => { - userEvent.click(showMore) + await waitFor(() => { + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() }) - await waitForElementToBeRemoved(showMore) - const back = getByText(BACK) + await userEvent.click(screen.getByText(BACK)) - act(() => { - userEvent.click(back) + await waitFor(() => { + expect(screen.queryByText(BACK)).not.toBeInTheDocument() }) - - await waitForElementToBeRemoved(back) - expect(getByText(SHOW_MORE)).toBeInTheDocument() + expect(screen.getByText(SHOW_MORE)).toBeInTheDocument() } ) it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should return report page when clicking previous button in detail page given metric %s', async (requiredData) => { - const { getByText } = setup([requiredData]) + setup([requiredData]) - const showMore = getByText(SHOW_MORE) - - act(() => { - userEvent.click(showMore) - }) + const showMore = screen.getByText(SHOW_MORE) - await waitForElementToBeRemoved(showMore) - const previous = getByText(PREVIOUS) + await userEvent.click(showMore) - act(() => { - userEvent.click(previous) + await waitFor(() => { + waitForElementToBeRemoved(showMore) }) + const previous = screen.getByText(PREVIOUS) + + await userEvent.click(previous) await waitFor(() => { - expect(getByText(SHOW_MORE)).toBeInTheDocument() + expect(screen.getByText(SHOW_MORE)).toBeInTheDocument() }) } ) @@ -339,9 +334,9 @@ describe('Report Step', () => { it.each([[REQUIRED_DATA_LIST[4]], [REQUIRED_DATA_LIST[5]], [REQUIRED_DATA_LIST[6]], [REQUIRED_DATA_LIST[7]]])( 'should show export pipeline button when selecting %s', (requiredData) => { - const { getByText } = setup([requiredData]) + setup([requiredData]) - const exportPipelineButton = getByText(EXPORT_PIPELINE_DATA) + const exportPipelineButton = screen.getByText(EXPORT_PIPELINE_DATA) expect(exportPipelineButton).toBeInTheDocument() } @@ -349,9 +344,9 @@ describe('Report Step', () => { it('should call fetchExportData when clicking "Export pipeline data"', async () => { const { result } = renderHook(() => useExportCsvEffect()) - const { getByText } = setup([REQUIRED_DATA_LIST[6]]) + setup([REQUIRED_DATA_LIST[6]]) - const exportButton = getByText(EXPORT_PIPELINE_DATA) + const exportButton = screen.getByText(EXPORT_PIPELINE_DATA) expect(exportButton).toBeInTheDocument() await userEvent.click(exportButton) @@ -376,9 +371,9 @@ describe('Report Step', () => { it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[2]]])( 'should show export board button when selecting %s', (requiredData) => { - const { getByText } = setup([requiredData]) + setup([requiredData]) - const exportPipelineButton = getByText(EXPORT_BOARD_DATA) + const exportPipelineButton = screen.getByText(EXPORT_BOARD_DATA) expect(exportPipelineButton).toBeInTheDocument() } @@ -386,9 +381,9 @@ describe('Report Step', () => { it('should call fetchExportData when clicking "Export board data"', async () => { const { result } = renderHook(() => useExportCsvEffect()) - const { getByText } = setup([REQUIRED_DATA_LIST[2]]) + setup([REQUIRED_DATA_LIST[2]]) - const exportButton = getByText(EXPORT_BOARD_DATA) + const exportButton = screen.getByText(EXPORT_BOARD_DATA) expect(exportButton).toBeInTheDocument() await userEvent.click(exportButton) @@ -403,9 +398,9 @@ describe('Report Step', () => { describe('export metric data', () => { it('should show export metric button when visiting this page', () => { - const { getByText } = setup(['']) + setup(['']) - const exportMetricButton = getByText(EXPORT_METRIC_DATA) + const exportMetricButton = screen.getByText(EXPORT_METRIC_DATA) expect(exportMetricButton).toBeInTheDocument() }) From 3e428819902b721641ae094472dc2b9ca9ac9df7 Mon Sep 17 00:00:00 2001 From: Yunsong Date: Wed, 10 Jan 2024 14:52:22 +0800 Subject: [PATCH 74/90] ADM-727:[backend]refactor: rename method --- .../dto/request/GenerateReportRequest.java | 2 +- .../report/GenerateReporterService.java | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java index 4b8784ce29..4c5977f565 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/request/GenerateReportRequest.java @@ -47,7 +47,7 @@ public GenerateReportRequest convertToPipelineRequest(GenerateReportRequest requ return result; } - public GenerateReportRequest convertToCodeBaseRequest(GenerateReportRequest request) { + public GenerateReportRequest convertToSourceControlRequest(GenerateReportRequest request) { List codebaseMetrics = MetricsUtil .getCodeBaseMetrics(request.getMetrics().stream().map(String::toLowerCase).toList()); Gson gson = new Gson(); diff --git a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java index b336c190b1..ec1ca6c9a8 100644 --- a/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java +++ b/backend/src/main/java/heartbeat/service/report/GenerateReporterService.java @@ -72,7 +72,6 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import heartbeat.util.IdUtil; @@ -258,20 +257,22 @@ private void generatePipelineReport(GenerateReportRequest request) { } private void generateSourceControlReport(GenerateReportRequest request) { - GenerateReportRequest codebaseRequest = request.convertToCodeBaseRequest(request); + GenerateReportRequest sourceControlRequest = request.convertToSourceControlRequest(request); log.info( "Start to generate source control report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _sourceControlReportId: {}", - codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), codebaseRequest.getStartTime(), - codebaseRequest.getEndTime(), IdUtil.getSourceControlReportId(request.getCsvTimeStamp())); + sourceControlRequest.getMetrics(), sourceControlRequest.getConsiderHoliday(), + sourceControlRequest.getStartTime(), sourceControlRequest.getEndTime(), + IdUtil.getSourceControlReportId(request.getCsvTimeStamp())); CompletableFuture.runAsync(() -> { try { - saveReporterInHandler(generateReporter(codebaseRequest), - IdUtil.getSourceControlReportId(codebaseRequest.getCsvTimeStamp())); - updateMetricsDataReadyInHandler(codebaseRequest.getCsvTimeStamp(), codebaseRequest.getMetrics()); + saveReporterInHandler(generateReporter(sourceControlRequest), + IdUtil.getSourceControlReportId(sourceControlRequest.getCsvTimeStamp())); + updateMetricsDataReadyInHandler(sourceControlRequest.getCsvTimeStamp(), + sourceControlRequest.getMetrics()); log.info( "Successfully generate codebase report, _metrics: {}, _considerHoliday: {}, _startTime: {}, _endTime: {}, _sourceControlReportId: {}", - codebaseRequest.getMetrics(), codebaseRequest.getConsiderHoliday(), - codebaseRequest.getStartTime(), codebaseRequest.getEndTime(), + sourceControlRequest.getMetrics(), sourceControlRequest.getConsiderHoliday(), + sourceControlRequest.getStartTime(), sourceControlRequest.getEndTime(), IdUtil.getSourceControlReportId(request.getCsvTimeStamp())); } catch (BaseException e) { From ba396f4a0be23afdb67d7a8f9da3457a030fb26a Mon Sep 17 00:00:00 2001 From: JiangRu1 <3246736839@qq.com> Date: Wed, 10 Jan 2024 14:56:36 +0800 Subject: [PATCH 75/90] ADM-652: [frontend] test: fix tests --- .../MetricsStepper/MetricsStepper.test.tsx | 106 +++++++++--------- .../Metrics/ReportStep/ReportStep.test.tsx | 6 +- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx index f218864ce3..119c43661a 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx @@ -162,124 +162,124 @@ describe('MetricsStepper', () => { ) it('should show metrics stepper', () => { - const { getByText } = setup() + setup() STEPPER.map((label) => { - expect(getByText(label)).toBeInTheDocument() + expect(screen.getByText(label)).toBeInTheDocument() }) - expect(getByText(NEXT)).toBeInTheDocument() - expect(getByText(PREVIOUS)).toBeInTheDocument() + expect(screen.getByText(NEXT)).toBeInTheDocument() + expect(screen.getByText(PREVIOUS)).toBeInTheDocument() }) it('should show metrics config step when click back button given config step ', async () => { - const { getByText } = setup() + setup() - await userEvent.click(getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)) - expect(getByText(PROJECT_NAME_LABEL)).toBeInTheDocument() + expect(screen.getByText(PROJECT_NAME_LABEL)).toBeInTheDocument() }) it('should show confirm dialog when click back button in config page', async () => { - const { getByText } = setup() + setup() - await userEvent.click(getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)) - expect(getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument() + expect(screen.getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument() }) it('should close confirm dialog when click cancel button', async () => { - const { getByText, queryByText } = setup() + setup() - await userEvent.click(getByText(PREVIOUS)) - await userEvent.click(getByText(CANCEL)) + await userEvent.click(screen.getByText(PREVIOUS)) + await userEvent.click(screen.getByText(CANCEL)) - expect(queryByText(CONFIRM_DIALOG_DESCRIPTION)).not.toBeInTheDocument() + expect(screen.queryByText(CONFIRM_DIALOG_DESCRIPTION)).not.toBeInTheDocument() }) it('should go to home page when click Yes button', async () => { - const { getByText } = setup() + setup() - await userEvent.click(getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)) - expect(getByText(YES)).toBeVisible() + expect(screen.getByText(YES)).toBeVisible() - await userEvent.click(getByText(YES)) + await userEvent.click(screen.getByText(YES)) expect(navigateMock).toHaveBeenCalledTimes(1) expect(navigateMock).toHaveBeenCalledWith(BASE_PAGE_ROUTE) }) it('should disable next when required data is empty ', async () => { - const { getByText } = setup() + setup() act(() => { store.dispatch(updateMetrics([])) }) - expect(getByText(NEXT)).toBeDisabled() + expect(screen.getByText(NEXT)).toBeDisabled() }) it('should disable next when dataRange is empty ', async () => { - const { getByText, getByRole } = setup() + setup() await fillConfigPageData() - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement await userEvent.clear(startDateInput) await userEvent.clear(endDateInput) - expect(getByText(NEXT)).toBeDisabled() + expect(screen.getByText(NEXT)).toBeDisabled() }, 50000) it('should disable next when endDate is empty ', async () => { - const { getByText, getByRole } = setup() + setup() await fillConfigPageData() - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement await userEvent.clear(endDateInput) - expect(getByText(NEXT)).toBeDisabled() + expect(screen.getByText(NEXT)).toBeDisabled() }) it('should enable next when every selected component is show and verified', async () => { - const { getByText } = setup() + setup() await fillConfigPageData() - expect(getByText(NEXT)).toBeEnabled() + expect(screen.getByText(NEXT)).toBeEnabled() }) it('should disable next when board component is exist but not verified successfully', async () => { - const { getByText } = setup() + setup() act(() => { store.dispatch(updateMetrics([VELOCITY])) store.dispatch(updateBoardVerifyState(false)) }) - expect(getByText(NEXT)).toBeDisabled() + expect(screen.getByText(NEXT)).toBeDisabled() }) it('should go metrics page when click next button given next button enabled', async () => { - const { getByText } = setup() + setup() await fillConfigPageData() - fireEvent.click(getByText(NEXT)) + fireEvent.click(screen.getByText(NEXT)) - expect(getByText(METRICS)).toHaveStyle(`color:${stepperColor}`) + expect(screen.getByText(METRICS)).toHaveStyle(`color:${stepperColor}`) }) it('should show metrics export step when click next button given export step', async () => { - const { getByText } = setup() + setup() await fillConfigPageData() - await userEvent.click(getByText(NEXT)) + await userEvent.click(screen.getByText(NEXT)) await fillMetricsPageDate() waitFor(() => { - expect(getByText(NEXT)).toBeInTheDocument() + expect(screen.getByText(NEXT)).toBeInTheDocument() }) - await userEvent.click(getByText(NEXT)) + await userEvent.click(screen.getByText(NEXT)) - expect(getByText(REPORT)).toHaveStyle(`color:${stepperColor}`) + expect(screen.getByText(REPORT)).toHaveStyle(`color:${stepperColor}`) }) it('should export json when click save button', async () => { @@ -296,9 +296,9 @@ describe('MetricsStepper', () => { projectName: '', sourceControl: undefined, } - const { getByText } = setup() + setup() - await userEvent.click(getByText(SAVE)) + await userEvent.click(screen.getByText(SAVE)) expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) }) @@ -318,10 +318,10 @@ describe('MetricsStepper', () => { sourceControl: undefined, } - const { getByText } = setup() + setup() await fillMetricsData() - await userEvent.click(getByText(SAVE)) + await userEvent.click(screen.getByText(SAVE)) expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) }) @@ -347,11 +347,11 @@ describe('MetricsStepper', () => { doneStatus: undefined, leadTime: undefined, } - const { getByText } = setup() + setup() await fillConfigPageData() - await userEvent.click(getByText(NEXT)) - await userEvent.click(getByText(SAVE)) + await userEvent.click(screen.getByText(NEXT)) + await userEvent.click(screen.getByText(SAVE)) expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) }, 50000) @@ -378,24 +378,24 @@ describe('MetricsStepper', () => { leadTime: undefined, } - const { getByText } = setup() + setup() await fillConfigPageData() - await userEvent.click(getByText(NEXT)) + await userEvent.click(screen.getByText(NEXT)) await fillMetricsPageDate() waitFor(() => { - expect(getByText(NEXT)).toBeInTheDocument() + expect(screen.getByText(NEXT)).toBeInTheDocument() }) - await userEvent.click(getByText(NEXT)) - await userEvent.click(getByText(SAVE)) + await userEvent.click(screen.getByText(NEXT)) + await userEvent.click(screen.getByText(SAVE)) expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) }, 50000) it('should clean the config information that is hidden when click next button', async () => { - const { getByText } = setup() + setup() await fillConfigPageData() - await userEvent.click(getByText(NEXT)) + await userEvent.click(screen.getByText(NEXT)) expect(updateBoard).not.toHaveBeenCalledWith({ type: BOARD_TYPES.JIRA, diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index d961d331cf..c17af8f354 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -268,12 +268,10 @@ describe('Report Step', () => { async (requiredData) => { setup([requiredData]) - const showMore = screen.getByText(SHOW_MORE) - - await userEvent.click(showMore) + await userEvent.click(screen.getByText(SHOW_MORE)) await waitFor(() => { - waitForElementToBeRemoved(showMore) + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() }) expect(screen.getByText(BACK)).toBeInTheDocument() } From 6d2341702d5a06a731c4f65116d1851cb203d96b Mon Sep 17 00:00:00 2001 From: JiangRu1 <3246736839@qq.com> Date: Wed, 10 Jan 2024 15:01:46 +0800 Subject: [PATCH 76/90] ADM-652: [frontend] test: fix tests --- .../src/components/Metrics/ReportStep/ReportStep.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index c17af8f354..eb48902314 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -1,4 +1,4 @@ -import { render, renderHook, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react' +import { render, renderHook, screen, waitFor } from '@testing-library/react' import ReportStep from '@src/components/Metrics/ReportStep' import { BACK, @@ -307,7 +307,7 @@ describe('Report Step', () => { await userEvent.click(showMore) await waitFor(() => { - waitForElementToBeRemoved(showMore) + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() }) const previous = screen.getByText(PREVIOUS) From 00b3a7f0d063b56ddb55950899aee18bd1402517 Mon Sep 17 00:00:00 2001 From: Yunsong Date: Wed, 10 Jan 2024 15:11:25 +0800 Subject: [PATCH 77/90] ADM-727:[backend]refactor: delete unused part --- .../heartbeat/controller/report/GenerateReportController.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java index 763217e270..83687ddd01 100644 --- a/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java +++ b/backend/src/main/java/heartbeat/controller/report/GenerateReportController.java @@ -6,7 +6,6 @@ import heartbeat.controller.report.dto.request.ExportCSVRequest; import heartbeat.controller.report.dto.response.CallbackResponse; import heartbeat.controller.report.dto.response.ReportResponse; -import heartbeat.handler.AsyncExceptionHandler; import heartbeat.service.report.GenerateReporterService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -33,8 +32,6 @@ public class GenerateReportController { private final GenerateReporterService generateReporterService; - private final AsyncExceptionHandler asyncExceptionHandler; - @Value("${callback.interval}") private Integer interval; From 1e651450a3004b83383bda063a9d9be59f7d0a96 Mon Sep 17 00:00:00 2001 From: xuebing Date: Wed, 10 Jan 2024 15:15:25 +0800 Subject: [PATCH 78/90] ADM-652 [frontend] test: fix test for withBack --- .../Metrics/ReportStep/ReportDetail/withBack.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx index 3d927495d2..780c349995 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx @@ -8,19 +8,19 @@ describe('withGoBack', () => { afterEach(jest.clearAllMocks) it('should render a link with back', () => { - const Component = withGoBack(() =>
test1
) + const Component = withGoBack(() =>
{'test1'}
) render() expect(screen.getByText('Back')).toBeInTheDocument() }) it('should render the icon', () => { - const Component = withGoBack(() =>
test2
) + const Component = withGoBack(() =>
{'test2'}
) render() expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() }) it('should call onBack when the back is clicked', () => { - const Component = withGoBack(() =>
test3
) + const Component = withGoBack(() =>
{'test3'}
) render() fireEvent.click(screen.getByText('Back')) expect(onBack).toBeCalled() From e5534991b7ab3f359a76a6f81260d836ec63647e Mon Sep 17 00:00:00 2001 From: LEI WANG Date: Wed, 10 Jan 2024 16:57:19 +0800 Subject: [PATCH 79/90] ADM-727:[backend]feat: fix failed tst --- .../controller/report/GenerateReporterControllerTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java index 6ea0bf6a68..8c91e7b35c 100644 --- a/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java +++ b/backend/src/test/java/heartbeat/controller/report/GenerateReporterControllerTest.java @@ -133,7 +133,7 @@ void shouldReturnWhenExportCsv() throws Exception { } @Test - void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateBoardReportWhenReportTypeIsBoard() throws Exception { + void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateDoraReportWhenReportTypeIsDora() throws Exception { ObjectMapper mapper = new ObjectMapper(); GenerateReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); String currentTimeStamp = "1685010080107"; @@ -157,7 +157,7 @@ void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateBoardReportWhenRe } @Test - void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateDoraReportWhenReportTypeIsBoard() throws Exception { + void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateBoardReportWhenReportTypeIsBoard() throws Exception { ObjectMapper mapper = new ObjectMapper(); GenerateReportRequest request = mapper.readValue(new File(REQUEST_FILE_PATH), GenerateReportRequest.class); String currentTimeStamp = "1685010080107"; @@ -170,6 +170,7 @@ void shouldReturnCallBackUrlWithAcceptedStatusAndInvokeGenerateDoraReportWhenRep .andReturn() .getResponse(); + Thread.sleep(2000); verify(generateReporterService, times(0)).generateDoraReport(request); verify(generateReporterService, times(1)).generateBoardReport(request); From 0669c5dec7af4867379e4a8ce179a9a7a3a8e7b7 Mon Sep 17 00:00:00 2001 From: Shiqi Yuan Date: Wed, 10 Jan 2024 17:15:59 +0800 Subject: [PATCH 80/90] ADM-675: [backend]fix:fix the serialize exception in getPipelineStepsInfo method --- .../client/dto/pipeline/buildkite/BuildKiteBuildInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java index 4afadd19f6..1f17bc75eb 100644 --- a/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java +++ b/backend/src/main/java/heartbeat/client/dto/pipeline/buildkite/BuildKiteBuildInfo.java @@ -39,7 +39,7 @@ public class BuildKiteBuildInfo implements Serializable { @AllArgsConstructor @NoArgsConstructor @Builder - public static class Author { + public static class Author implements Serializable { private String userName; From be9c7ec26877209d0ba8e7f978cb4303be58f344 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 10:04:04 +0800 Subject: [PATCH 81/90] [frontend] Update semi role in eslint to update semicolon usage --- frontend/.eslintrc.json | 2 +- frontend/.prettierrc | 1 - .../__tests__/__mocks__/svgTransformer.ts | 6 +- frontend/__tests__/setupTests.ts | 10 +- frontend/__tests__/src/App.test.tsx | 22 +- .../__tests__/src/client/BoardClient.test.ts | 90 ++-- .../__tests__/src/client/CSVClient.test.ts | 54 +-- .../__tests__/src/client/HeaderClient.test.ts | 36 +- .../src/client/MetricsClient.test.ts | 56 +-- .../src/client/PipelineToolClient.test.ts | 56 +-- .../__tests__/src/client/ReportClient.test.ts | 62 +-- .../src/client/SourceControlClient.test.ts | 62 +-- .../DateRangeViewer/DateRangeViewer.test.tsx | 14 +- .../Common/EllipsisText/EllipsisText.test.tsx | 36 +- .../NotificationButton.test.tsx | 126 +++--- .../Common/ReportGrid/ReportCard.test.tsx | 18 +- .../DateDisplay/CollectionDuration.test.tsx | 30 +- .../components/ErrorContent/index.test.tsx | 42 +- .../ErrorNotification.test.tsx | 14 +- .../WarningNotification.test.tsx | 46 +- .../components/HomeGuide/HomeGuide.test.tsx | 132 +++--- .../src/components/Loading/Loading.test.tsx | 20 +- .../Metrics/ConfigStep/Board.test.tsx | 250 +++++------ .../ConfigStep/BranchSelection.test.tsx | 64 +-- .../Metrics/ConfigStep/ConfigStep.test.tsx | 218 +++++----- .../ConfigStep/DateRangePicker.test.tsx | 100 ++--- .../ConfigStep/MetricsTypeCheckbox.test.tsx | 240 +++++------ .../Metrics/ConfigStep/NoCardPop.test.tsx | 30 +- .../Metrics/ConfigStep/PipelineTool.test.tsx | 214 +++++----- .../Metrics/ConfigStep/SourceControl.test.tsx | 164 ++++---- .../MetricsStep/Classification.test.tsx | 142 +++---- .../Metrics/MetricsStep/Crews.test.tsx | 178 ++++---- .../Metrics/MetricsStep/CycleTime.test.tsx | 290 ++++++------- .../DeploymentFrequencySettings.test.tsx | 68 +-- .../PipelineMetricSelection.test.tsx | 254 ++++++------ .../SingleSelection.test.tsx | 120 +++--- .../Metrics/MetricsStep/MetricsStep.test.tsx | 174 ++++---- .../MetricsStep/MultiAutoComplete.test.tsx | 58 +-- .../Metrics/MetricsStep/RealDone.test.tsx | 172 ++++---- .../MetricsStepper/ConfirmDialog.test.tsx | 18 +- .../Metrics/ReportStep/ExpiredDialog.test.tsx | 48 +-- .../components/ProjectDescripution.test.tsx | 14 +- .../__tests__/src/context/boardSlice.test.ts | 50 +-- .../__tests__/src/context/configSlice.test.ts | 82 ++-- .../__tests__/src/context/headerSlice.test.ts | 18 +- .../src/context/metricsSlice.test.ts | 392 +++++++++--------- .../src/context/pipelineToolSlice.test.ts | 136 +++--- .../src/context/sourceControlSlice.test.ts | 40 +- .../src/context/stepperSlice.test.ts | 46 +- frontend/__tests__/src/emojis/emoji.test.tsx | 42 +- .../src/fileConfig/fileConfig.test.ts | 24 +- frontend/__tests__/src/fixtures.ts | 214 +++++----- .../reportMapper/changeFailureRate.test.tsx | 14 +- .../hooks/reportMapper/classfication.test.tsx | 14 +- .../src/hooks/reportMapper/cycleTime.test.tsx | 14 +- .../reportMapper/deploymentFrequency.test.tsx | 14 +- .../reportMapper/exportValidityTime.test.tsx | 16 +- .../reportMapper/leadTimeForChanges.test.tsx | 26 +- .../reportMapper/meanTimeToRecovery.test.tsx | 34 +- .../src/hooks/reportMapper/report.test.tsx | 12 +- .../src/hooks/reportMapper/velocity.test.tsx | 14 +- .../src/hooks/useExportCsvEffect.test.tsx | 66 +-- .../hooks/useGenerateReportEffect.test.tsx | 366 ++++++++-------- .../hooks/useGetMetricsStepsEffect.test.tsx | 56 +-- ...MetricsStepValidationCheckContext.test.tsx | 110 ++--- .../useNotificationLayoutEffect.test.tsx | 108 ++--- .../src/hooks/useVerifyBoardEffect.test.tsx | 54 +-- .../useVerifyPipelineToolEffect.test.tsx | 54 +-- .../useVerifySourceControlEffect.test.tsx | 54 +-- frontend/__tests__/src/initialConfigState.ts | 8 +- .../__tests__/src/layouts/Header.test.tsx | 136 +++--- frontend/__tests__/src/pages/Home.test.tsx | 28 +- frontend/__tests__/src/pages/Metrics.test.tsx | 22 +- frontend/__tests__/src/router.test.tsx | 54 +-- frontend/__tests__/src/updatedConfigState.ts | 6 +- frontend/__tests__/src/utils/Util.test.tsx | 140 +++---- .../__tests__/src/utils/setupStoreUtil.tsx | 14 +- frontend/cypress.config.ts | 10 +- frontend/cypress/e2e/createANewProject.cy.ts | 268 ++++++------ frontend/cypress/e2e/importAProject.cy.ts | 206 ++++----- frontend/cypress/fixtures/fixtures.ts | 10 +- frontend/cypress/pages/home.ts | 36 +- frontend/cypress/pages/metrics/config.ts | 112 ++--- frontend/cypress/pages/metrics/metrics.ts | 136 +++--- frontend/cypress/pages/metrics/report.ts | 30 +- frontend/cypress/plugins/clearDownloadFile.ts | 22 +- frontend/cypress/plugins/readDir.ts | 14 +- frontend/cypress/support/e2e.ts | 6 +- frontend/global-setup.ts | 6 +- frontend/scripts/runE2eWithServer.ts | 124 +++--- frontend/src/App.tsx | 14 +- frontend/src/clients/Httpclient.ts | 46 +- frontend/src/clients/MetricsClient.ts | 44 +- frontend/src/clients/board/BoardClient.ts | 46 +- frontend/src/clients/board/dto/request.ts | 14 +- frontend/src/clients/header/HeaderClient.ts | 20 +- frontend/src/clients/header/dto/request.ts | 2 +- .../clients/pipeline/PipelineToolClient.ts | 28 +- frontend/src/clients/pipeline/dto/request.ts | 8 +- frontend/src/clients/report/CSVClient.ts | 24 +- frontend/src/clients/report/ReportClient.ts | 36 +- frontend/src/clients/report/dto/request.ts | 122 +++--- .../sourceControl/SourceControlClient.ts | 28 +- .../src/clients/sourceControl/dto/request.ts | 8 +- frontend/src/components/Common/Buttons.ts | 12 +- .../Common/CollectionDuration/index.tsx | 44 +- .../Common/CollectionDuration/style.tsx | 20 +- frontend/src/components/Common/ConfigForms.ts | 18 +- .../Common/DateRangeViewer/index.tsx | 16 +- .../Common/DateRangeViewer/style.tsx | 12 +- .../components/Common/EllipsisText/index.tsx | 14 +- .../components/Common/EllipsisText/style.tsx | 6 +- .../Common/MetricsSettingButton/index.tsx | 12 +- .../Common/MetricsSettingButton/style.tsx | 8 +- .../Common/MetricsSettingTitle/index.tsx | 6 +- .../Common/MetricsSettingTitle/style.tsx | 6 +- .../Common/MultiAutoComplete/index.tsx | 42 +- .../Common/MultiAutoComplete/styles.ts | 6 +- .../Common/NotificationButton/index.tsx | 16 +- .../Common/NotificationButton/style.tsx | 12 +- .../Common/ReportForThreeColumns/index.tsx | 44 +- .../Common/ReportForTwoColumns/index.tsx | 25 +- .../Common/ReportForTwoColumns/style.tsx | 20 +- .../Common/ReportGrid/ReportCard/index.tsx | 44 +- .../Common/ReportGrid/ReportCard/style.tsx | 12 +- .../ReportGrid/ReportCardItem/index.tsx | 22 +- .../ReportGrid/ReportCardItem/style.tsx | 22 +- .../Common/ReportGrid/ReportTitle/index.tsx | 10 +- .../components/Common/ReportGrid/index.tsx | 42 +- .../Common/WarningNotification/index.tsx | 24 +- .../Common/WarningNotification/style.tsx | 18 +- .../src/components/ErrorContent/index.tsx | 22 +- .../src/components/ErrorContent/style.tsx | 28 +- .../components/ErrorNotification/index.tsx | 8 +- .../components/ErrorNotification/style.tsx | 18 +- frontend/src/components/HomeGuide/index.tsx | 90 ++-- frontend/src/components/HomeGuide/style.tsx | 16 +- frontend/src/components/Loading/index.tsx | 14 +- frontend/src/components/Loading/style.tsx | 12 +- .../BasicInfo/RequiredMetrics/index.tsx | 66 +-- .../BasicInfo/RequiredMetrics/style.tsx | 6 +- .../Metrics/ConfigStep/BasicInfo/index.tsx | 54 +-- .../Metrics/ConfigStep/BasicInfo/style.tsx | 12 +- .../Metrics/ConfigStep/Board/index.tsx | 152 +++---- .../ConfigStep/BranchSelection/index.tsx | 40 +- .../ConfigStep/DateRangePicker/index.tsx | 48 +-- .../ConfigStep/DateRangePicker/style.tsx | 10 +- .../ConfigStep/MetricsTypeCheckbox/index.tsx | 22 +- .../ConfigStep/NoDoneCardPop/index.tsx | 14 +- .../ConfigStep/NoDoneCardPop/style.tsx | 10 +- .../Metrics/ConfigStep/PipelineTool/index.tsx | 128 +++--- .../ConfigStep/SourceControl/index.tsx | 122 +++--- .../components/Metrics/ConfigStep/index.tsx | 20 +- .../components/Metrics/ConfigStep/style.tsx | 4 +- .../MetricsStep/Classification/index.tsx | 44 +- .../MetricsStep/Crews/AssigneeFilter.tsx | 24 +- .../Metrics/MetricsStep/Crews/index.tsx | 58 +-- .../Metrics/MetricsStep/Crews/style.tsx | 6 +- .../MetricsStep/CycleTime/FlagCard.tsx | 24 +- .../CycleTime/Table/CellAutoComplete.tsx | 40 +- .../MetricsStep/CycleTime/Table/index.tsx | 74 ++-- .../MetricsStep/CycleTime/Table/style.tsx | 14 +- .../Metrics/MetricsStep/CycleTime/index.tsx | 26 +- .../Metrics/MetricsStep/CycleTime/style.tsx | 16 +- .../PipelineMetricSelection/index.tsx | 96 ++--- .../PipelineMetricSelection/style.tsx | 16 +- .../SingleSelection/index.tsx | 60 +-- .../SingleSelection/style.tsx | 6 +- .../DeploymentFrequencySettings/index.tsx | 46 +- .../Metrics/MetricsStep/RealDone/index.tsx | 80 ++-- .../Metrics/MetricsStep/RealDone/style.tsx | 6 +- .../components/Metrics/MetricsStep/index.tsx | 52 +-- .../MetricsStepper/ConfirmDialog/index.tsx | 12 +- .../MetricsStepper/ConfirmDialog/style.tsx | 16 +- .../Metrics/MetricsStepper/index.tsx | 186 ++++----- .../Metrics/MetricsStepper/style.tsx | 24 +- .../Metrics/ReportButtonGroup/index.tsx | 14 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 30 +- .../Metrics/ReportStep/BoradMetrics/style.tsx | 4 +- .../Metrics/ReportStep/DoraMetrics/style.tsx | 4 +- .../ReportStep/ExpiredDialog/index.tsx | 34 +- .../ReportStep/ExpiredDialog/style.tsx | 8 +- .../Metrics/ReportStep/ReportDetail/style.tsx | 0 .../components/Metrics/ReportStep/style.tsx | 12 +- .../src/components/ProjectDescription.tsx | 10 +- frontend/src/config/routes.ts | 4 +- frontend/src/constants/commons.ts | 30 +- frontend/src/constants/regex.ts | 2 +- frontend/src/constants/resources.ts | 46 +- frontend/src/constants/template.ts | 2 +- frontend/src/context/Metrics/metricsSlice.tsx | 369 +++++++++-------- .../src/context/config/board/boardSlice.ts | 20 +- frontend/src/context/config/configSlice.ts | 154 +++---- .../config/pipelineTool/pipelineToolSlice.ts | 14 +- .../pipelineTool/verifyResponseSlice.ts | 20 +- .../sourceControl/sourceControlSlice.ts | 14 +- .../sourceControl/verifyResponseSlice.ts | 4 +- frontend/src/context/header/headerSlice.tsx | 18 +- frontend/src/context/interface/index.tsx | 10 +- frontend/src/context/stepper/StepperSlice.tsx | 32 +- frontend/src/emojis/emoji.ts | 50 +-- frontend/src/emojis/style.tsx | 10 +- .../src/exceptions/BadRequestException.ts | 8 +- frontend/src/exceptions/ExceptionType.ts | 4 +- frontend/src/exceptions/ForbiddenException.ts | 8 +- .../src/exceptions/InternalServerException.ts | 8 +- frontend/src/exceptions/NotFoundException.ts | 8 +- frontend/src/exceptions/TimeoutException.ts | 8 +- .../src/exceptions/UnauthorizedException.ts | 8 +- frontend/src/exceptions/UnkonwException.ts | 6 +- frontend/src/exceptions/index.ts | 16 +- frontend/src/fileConfig/fileConfig.ts | 140 +++---- frontend/src/hooks/index.ts | 10 +- .../hooks/reportMapper/changeFailureRate.ts | 20 +- .../src/hooks/reportMapper/classification.ts | 22 +- frontend/src/hooks/reportMapper/cycleTime.ts | 34 +- .../hooks/reportMapper/deploymentFrequency.ts | 20 +- .../hooks/reportMapper/exportValidityTime.ts | 12 +- .../hooks/reportMapper/leadTimeForChanges.ts | 26 +- .../hooks/reportMapper/meanTimeToRecovery.ts | 30 +- frontend/src/hooks/reportMapper/report.ts | 38 +- .../reportMapper/reportUIDataStructure.ts | 20 +- frontend/src/hooks/reportMapper/velocity.ts | 18 +- frontend/src/hooks/useAppDispatch.ts | 10 +- frontend/src/hooks/useExportCsvEffect.ts | 40 +- frontend/src/hooks/useGenerateReportEffect.ts | 98 ++--- .../src/hooks/useGetMetricsStepsEffect.ts | 46 +- .../useMetricsStepValidationCheckContext.tsx | 56 +-- .../src/hooks/useNotificationLayoutEffect.ts | 44 +- frontend/src/hooks/useVerifyBoardEffect.ts | 46 +- .../src/hooks/useVerifyPipelineToolEffect.ts | 44 +- .../src/hooks/useVeritySourceControlEffect.ts | 44 +- frontend/src/layouts/Header.tsx | 60 +-- frontend/src/layouts/style.tsx | 30 +- frontend/src/main.tsx | 16 +- frontend/src/pages/ErrorPage.tsx | 12 +- frontend/src/pages/Home.tsx | 14 +- frontend/src/pages/Metrics.tsx | 16 +- frontend/src/router.tsx | 18 +- frontend/src/store.ts | 16 +- frontend/src/theme.ts | 84 ++-- frontend/src/utils/util.ts | 84 ++-- frontend/src/vite-env.d.ts | 6 +- frontend/vite.config.ts | 10 +- 244 files changed, 6105 insertions(+), 6092 deletions(-) create mode 100644 frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index c5b7d3c592..6a71b2c0fc 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -22,7 +22,7 @@ }, "plugins": ["react", "react-hooks", "@typescript-eslint", "prettier"], "rules": { - "semi": 0, + "semi": 2, "react/react-in-jsx-scope": "off", "import/prefer-default-export": "off", "react/display-name": "off", diff --git a/frontend/.prettierrc b/frontend/.prettierrc index 7489e24578..eb862bdd00 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -4,6 +4,5 @@ "jsxSingleQuote": true, "useTabs": false, "tabWidth": 2, - "semi": false, "bracketSpacing": true } diff --git a/frontend/__tests__/__mocks__/svgTransformer.ts b/frontend/__tests__/__mocks__/svgTransformer.ts index d67ee4cdfb..14f39e293c 100644 --- a/frontend/__tests__/__mocks__/svgTransformer.ts +++ b/frontend/__tests__/__mocks__/svgTransformer.ts @@ -1,9 +1,9 @@ module.exports = { process() { - return { code: 'module.exports = {};' } + return { code: 'module.exports = {};' }; }, getCacheKey() { // The output is always the same. - return 'svgTransform' + return 'svgTransform'; }, -} +}; diff --git a/frontend/__tests__/setupTests.ts b/frontend/__tests__/setupTests.ts index 118a726320..0ca2ff4014 100644 --- a/frontend/__tests__/setupTests.ts +++ b/frontend/__tests__/setupTests.ts @@ -1,12 +1,12 @@ -import '@testing-library/jest-dom' -import { configure } from '@testing-library/react' +import '@testing-library/jest-dom'; +import { configure } from '@testing-library/react'; -export const navigateMock = jest.fn() +export const navigateMock = jest.fn(); // This configuration will affect the waitFor default timeout which is 1000ms. -configure({ asyncUtilTimeout: 2000 }) +configure({ asyncUtilTimeout: 2000 }); jest.mock('react-router-dom', () => ({ ...(jest.requireActual('react-router-dom') as Record), useNavigate: () => navigateMock, -})) +})); diff --git a/frontend/__tests__/src/App.test.tsx b/frontend/__tests__/src/App.test.tsx index 173afa7cbd..6078568266 100644 --- a/frontend/__tests__/src/App.test.tsx +++ b/frontend/__tests__/src/App.test.tsx @@ -1,20 +1,20 @@ -import { render, RenderResult, waitFor } from '@testing-library/react' -import App from '@src/App' -import { Provider } from 'react-redux' -import { store } from '@src/store' -jest.useFakeTimers() +import { render, RenderResult, waitFor } from '@testing-library/react'; +import App from '@src/App'; +import { Provider } from 'react-redux'; +import { store } from '@src/store'; +jest.useFakeTimers(); describe('render app', () => { const setup = (): RenderResult => render( - ) + ); it('should show hello World when render app', async () => { - const { container } = setup() + const { container } = setup(); await waitFor(() => { - expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar') - }) - }) -}) + expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar'); + }); + }); +}); diff --git a/frontend/__tests__/src/client/BoardClient.test.ts b/frontend/__tests__/src/client/BoardClient.test.ts index 1af259c9f3..abd803a74e 100644 --- a/frontend/__tests__/src/client/BoardClient.test.ts +++ b/frontend/__tests__/src/client/BoardClient.test.ts @@ -1,69 +1,69 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; import { MOCK_BOARD_URL_FOR_CLASSIC_JIRA, MOCK_BOARD_URL_FOR_JIRA, MOCK_BOARD_VERIFY_REQUEST_PARAMS, MOCK_CLASSIC_JIRA_BOARD_VERIFY_REQUEST_PARAMS, VERIFY_ERROR_MESSAGE, -} from '../fixtures' -import { boardClient } from '@src/clients/board/BoardClient' -import { HttpStatusCode } from 'axios' +} from '../fixtures'; +import { boardClient } from '@src/clients/board/BoardClient'; +import { HttpStatusCode } from 'axios'; const server = setupServer( rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok))), rest.post(MOCK_BOARD_URL_FOR_CLASSIC_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok))) -) +); describe('verify board request', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); it('should isBoardVerify is true when board verify response status 200', async () => { - const result = await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) + const result = await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); - expect(result.isBoardVerify).toEqual(true) - }) + expect(result.isBoardVerify).toEqual(true); + }); it('should isBoardVerify is true when select classic jira and board verify response status 200', async () => { - const result = await boardClient.getVerifyBoard(MOCK_CLASSIC_JIRA_BOARD_VERIFY_REQUEST_PARAMS) + const result = await boardClient.getVerifyBoard(MOCK_CLASSIC_JIRA_BOARD_VERIFY_REQUEST_PARAMS); - expect(result.isBoardVerify).toEqual(true) - }) + expect(result.isBoardVerify).toEqual(true); + }); it('should isNoDoneCard is true when board verify response status 204', async () => { - server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))) + server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))); - const result = await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) + const result = await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); - expect(result.haveDoneCard).toEqual(false) - expect(result.isBoardVerify).toEqual(false) - }) + expect(result.haveDoneCard).toEqual(false); + expect(result.isBoardVerify).toEqual(false); + }); it('should throw error when board verify response status 400', async () => { server.use( rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.BadRequest), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.BAD_REQUEST })) ) - ) + ); boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS).catch((e) => { - expect(e).toBeInstanceOf(Error) - expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.BAD_REQUEST) - }) - }) + expect(e).toBeInstanceOf(Error); + expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.BAD_REQUEST); + }); + }); it('should throw error when board verify response status 401', async () => { server.use( rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) - ) + ); await expect(async () => { - await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNAUTHORIZED) - }) + await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNAUTHORIZED); + }); it('should throw error when board verify response status 500', async () => { server.use( @@ -75,38 +75,38 @@ describe('verify board request', () => { }) ) ) - ) + ); await expect(async () => { - await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) + await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); it('should throw error when board verify response status 503', async () => { server.use( rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.ServiceUnavailable), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.REQUEST_TIMEOUT })) ) - ) + ); await expect(async () => { - await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.REQUEST_TIMEOUT) - }) + await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.REQUEST_TIMEOUT); + }); it('should throw error when board verify response status 300', async () => { - server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.MultipleChoices)))) + server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.MultipleChoices)))); await expect(async () => { - await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN) - }) + await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN); + }); it('should throw unknown error when board verify response empty', async () => { - server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res) => res.networkError('Network Error'))) + server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res) => res.networkError('Network Error'))); await expect(async () => { - await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN) - }) -}) + await boardClient.getVerifyBoard(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN); + }); +}); diff --git a/frontend/__tests__/src/client/CSVClient.test.ts b/frontend/__tests__/src/client/CSVClient.test.ts index bdb43c8286..4e03c24f01 100644 --- a/frontend/__tests__/src/client/CSVClient.test.ts +++ b/frontend/__tests__/src/client/CSVClient.test.ts @@ -1,39 +1,39 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { HttpStatusCode } from 'axios' -import { MOCK_EXPORT_CSV_REQUEST_PARAMS, MOCK_EXPORT_CSV_URL, VERIFY_ERROR_MESSAGE } from '../fixtures' -import { csvClient } from '@src/clients/report/CSVClient' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { HttpStatusCode } from 'axios'; +import { MOCK_EXPORT_CSV_REQUEST_PARAMS, MOCK_EXPORT_CSV_URL, VERIFY_ERROR_MESSAGE } from '../fixtures'; +import { csvClient } from '@src/clients/report/CSVClient'; -const server = setupServer(rest.get(MOCK_EXPORT_CSV_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))) +const server = setupServer(rest.get(MOCK_EXPORT_CSV_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))); describe('verify export csv', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); it('should download the pipeline CSV file when export csv request status 200', async () => { - const mockBlob = new Blob(['CSV data'], { type: 'text/csv' }) - const mockResponse = { data: mockBlob } - const mockGet = jest.fn().mockResolvedValue(mockResponse) + const mockBlob = new Blob(['CSV data'], { type: 'text/csv' }); + const mockResponse = { data: mockBlob }; + const mockGet = jest.fn().mockResolvedValue(mockResponse); const mockCreateObjectURL = jest.fn().mockImplementation((blob) => { - return `mock-url:${blob}` - }) - const appendChildSpy = jest.spyOn(document.body, 'appendChild') - const removeChildSpy = jest.spyOn(document.body, 'removeChild') - window.URL.createObjectURL = mockCreateObjectURL + return `mock-url:${blob}`; + }); + const appendChildSpy = jest.spyOn(document.body, 'appendChild'); + const removeChildSpy = jest.spyOn(document.body, 'removeChild'); + window.URL.createObjectURL = mockCreateObjectURL; - const mockAxiosInstance = { get: mockGet } - await csvClient.exportCSVData.call({ axiosInstance: mockAxiosInstance }, MOCK_EXPORT_CSV_REQUEST_PARAMS) + const mockAxiosInstance = { get: mockGet }; + await csvClient.exportCSVData.call({ axiosInstance: mockAxiosInstance }, MOCK_EXPORT_CSV_REQUEST_PARAMS); - expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob) - expect(appendChildSpy).toHaveBeenCalled() - expect(removeChildSpy).toHaveBeenCalled() - }) + expect(mockCreateObjectURL).toHaveBeenCalledWith(mockBlob); + expect(appendChildSpy).toHaveBeenCalled(); + expect(removeChildSpy).toHaveBeenCalled(); + }); it('should throw error when export csv request status 500', async () => { - server.use(rest.get(MOCK_EXPORT_CSV_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.InternalServerError)))) + server.use(rest.get(MOCK_EXPORT_CSV_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.InternalServerError)))); await expect(async () => { - await csvClient.exportCSVData(MOCK_EXPORT_CSV_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) -}) + await csvClient.exportCSVData(MOCK_EXPORT_CSV_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); +}); diff --git a/frontend/__tests__/src/client/HeaderClient.test.ts b/frontend/__tests__/src/client/HeaderClient.test.ts index 6bb6924d4b..b4e8bd5e7a 100644 --- a/frontend/__tests__/src/client/HeaderClient.test.ts +++ b/frontend/__tests__/src/client/HeaderClient.test.ts @@ -1,26 +1,26 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { MOCK_VERSION_URL, VERIFY_ERROR_MESSAGE, VERSION_RESPONSE } from '../fixtures' -import { HttpStatusCode } from 'axios' -import { headerClient } from '@src/clients/header/HeaderClient' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { MOCK_VERSION_URL, VERIFY_ERROR_MESSAGE, VERSION_RESPONSE } from '../fixtures'; +import { HttpStatusCode } from 'axios'; +import { headerClient } from '@src/clients/header/HeaderClient'; -const server = setupServer(rest.get(MOCK_VERSION_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))) +const server = setupServer(rest.get(MOCK_VERSION_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))); describe('header client', () => { - beforeAll(() => server.listen()) - afterEach(() => server.resetHandlers()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); it('should get response when get header status 200', async () => { - const excepted = '1.11' + const excepted = '1.11'; server.use( rest.get(MOCK_VERSION_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Accepted), ctx.json(VERSION_RESPONSE)) ) - ) + ); - await expect(headerClient.getVersion()).resolves.toEqual(excepted) - }) + await expect(headerClient.getVersion()).resolves.toEqual(excepted); + }); it('should throw error when get version response status 500', () => { server.use( @@ -32,10 +32,10 @@ describe('header client', () => { }) ) ) - ) + ); expect(async () => { - await headerClient.getVersion() - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) -}) + await headerClient.getVersion(); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); +}); diff --git a/frontend/__tests__/src/client/MetricsClient.test.ts b/frontend/__tests__/src/client/MetricsClient.test.ts index 513ecaf3e4..cf07ed3331 100644 --- a/frontend/__tests__/src/client/MetricsClient.test.ts +++ b/frontend/__tests__/src/client/MetricsClient.test.ts @@ -1,27 +1,27 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { metricsClient } from '@src/clients/MetricsClient' -import { BASE_URL, MOCK_GET_STEPS_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures' -import { HttpStatusCode } from 'axios' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { metricsClient } from '@src/clients/MetricsClient'; +import { BASE_URL, MOCK_GET_STEPS_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures'; +import { HttpStatusCode } from 'axios'; describe('get steps from metrics response', () => { - const { params, buildId, organizationId, pipelineType, token } = MOCK_GET_STEPS_PARAMS - const getStepsUrl = `${BASE_URL}/pipelines/:type/:orgId/pipelines/:buildId/steps` - const server = setupServer() - beforeAll(() => server.listen()) - afterAll(() => server.close()) + const { params, buildId, organizationId, pipelineType, token } = MOCK_GET_STEPS_PARAMS; + const getStepsUrl = `${BASE_URL}/pipelines/:type/:orgId/pipelines/:buildId/steps`; + const server = setupServer(); + beforeAll(() => server.listen()); + afterAll(() => server.close()); it('should return steps when getSteps response status 200', async () => { server.use( rest.get(getStepsUrl, (req, res, ctx) => { - return res(ctx.status(HttpStatusCode.Ok), ctx.json({ steps: ['step1'] })) + return res(ctx.status(HttpStatusCode.Ok), ctx.json({ steps: ['step1'] })); }) - ) + ); - const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token) + const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token); - expect(result).toEqual({ response: ['step1'], haveStep: true }) - }) + expect(result).toEqual({ response: ['step1'], haveStep: true }); + }); it('should throw error when getSteps response status 500', async () => { server.use( @@ -33,30 +33,30 @@ describe('get steps from metrics response', () => { }) ) ) - ) + ); await expect(async () => { - await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) + await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); it('should throw error when getSteps response status 400', async () => { server.use( rest.get(getStepsUrl, (req, res, ctx) => res(ctx.status(HttpStatusCode.BadRequest), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.BAD_REQUEST })) ) - ) + ); await expect(async () => { - await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.BAD_REQUEST) - }) + await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.BAD_REQUEST); + }); it('should show isNoStep True when getSteps response status 204', async () => { - server.use(rest.get(getStepsUrl, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))) + server.use(rest.get(getStepsUrl, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))); - const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token) + const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token); - expect(result).toEqual({ branches: [], response: [], haveStep: false, pipelineCrews: [] }) - }) -}) + expect(result).toEqual({ branches: [], response: [], haveStep: false, pipelineCrews: [] }); + }); +}); diff --git a/frontend/__tests__/src/client/PipelineToolClient.test.ts b/frontend/__tests__/src/client/PipelineToolClient.test.ts index af1c7e2a04..f9917dc5bf 100644 --- a/frontend/__tests__/src/client/PipelineToolClient.test.ts +++ b/frontend/__tests__/src/client/PipelineToolClient.test.ts @@ -1,57 +1,57 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { MOCK_PIPELINE_URL, MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures' -import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient' -import { HttpStatusCode } from 'axios' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { MOCK_PIPELINE_URL, MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures'; +import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient'; +import { HttpStatusCode } from 'axios'; const server = setupServer( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => { - return res(ctx.status(HttpStatusCode.Ok)) + return res(ctx.status(HttpStatusCode.Ok)); }) -) +); describe('verify pipelineTool request', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); it('should isPipelineVerified is true when pipelineTool verify response status 200', async () => { - const result = await pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS) + const result = await pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); - expect(result.isPipelineToolVerified).toEqual(true) - }) + expect(result.isPipelineToolVerified).toEqual(true); + }); it('should throw error when pipelineTool verify response status 400', async () => { server.use( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.BadRequest), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.BAD_REQUEST })) ) - ) + ); await expect(() => pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS)).rejects.toThrow( VERIFY_ERROR_MESSAGE.BAD_REQUEST - ) - }) + ); + }); it('should throw error when pipelineTool verify response status is 401', async () => { server.use( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) - ) + ); await expect(() => pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS)).rejects.toThrow( VERIFY_ERROR_MESSAGE.UNAUTHORIZED - ) - }) + ); + }); it('should throw error when pipelineTool verify response status is 403', async () => { server.use( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Forbidden), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.PERMISSION_DENIED })) ) - ) + ); await expect(() => pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS)).rejects.toThrow( VERIFY_ERROR_MESSAGE.PERMISSION_DENIED - ) - }) + ); + }); it('should throw error when pipelineTool verify response status 500', async () => { server.use( @@ -63,11 +63,11 @@ describe('verify pipelineTool request', () => { }) ) ) - ) + ); await expect(() => pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS)).rejects.toThrow( VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR - ) - }) + ); + }); it('should throw error when board verify response status 300', async () => { server.use( @@ -79,10 +79,10 @@ describe('verify pipelineTool request', () => { }) ) ) - ) + ); await expect(() => pipelineToolClient.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS)).rejects.toThrow( VERIFY_ERROR_MESSAGE.UNKNOWN - ) - }) -}) + ); + }); +}); diff --git a/frontend/__tests__/src/client/ReportClient.test.ts b/frontend/__tests__/src/client/ReportClient.test.ts index 36a06e11b7..ee6490e411 100644 --- a/frontend/__tests__/src/client/ReportClient.test.ts +++ b/frontend/__tests__/src/client/ReportClient.test.ts @@ -1,39 +1,39 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; import { MOCK_GENERATE_REPORT_REQUEST_PARAMS, MOCK_REPORT_RESPONSE, MOCK_RETRIEVE_REPORT_RESPONSE, VERIFY_ERROR_MESSAGE, -} from '../fixtures' -import { HttpStatusCode } from 'axios' -import { reportClient } from '@src/clients/report/ReportClient' +} from '../fixtures'; +import { HttpStatusCode } from 'axios'; +import { reportClient } from '@src/clients/report/ReportClient'; -const MOCK_REPORT_URL = 'http://localhost/api/v1/reports' +const MOCK_REPORT_URL = 'http://localhost/api/v1/reports'; const server = setupServer( rest.post(MOCK_REPORT_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok))), rest.get(MOCK_REPORT_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Ok))) -) +); describe('report client', () => { - beforeAll(() => server.listen()) - afterEach(() => server.resetHandlers()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); it('should get response when generate report request status 202', async () => { const excepted = { response: MOCK_RETRIEVE_REPORT_RESPONSE, - } + }; server.use( rest.post(MOCK_REPORT_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Accepted), ctx.json(MOCK_RETRIEVE_REPORT_RESPONSE)) ) - ) + ); await expect( reportClient.retrieveReportByUrl(MOCK_GENERATE_REPORT_REQUEST_PARAMS, '/reports') - ).resolves.toStrictEqual(excepted) - }) + ).resolves.toStrictEqual(excepted); + }); it('should throw error when generate report response status 500', async () => { server.use( @@ -45,12 +45,12 @@ describe('report client', () => { }) ) ) - ) + ); await expect(async () => { - await reportClient.retrieveReportByUrl(MOCK_GENERATE_REPORT_REQUEST_PARAMS, '/reports') - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) + await reportClient.retrieveReportByUrl(MOCK_GENERATE_REPORT_REQUEST_PARAMS, '/reports'); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); it('should throw error when generate report response status 400', async () => { server.use( @@ -62,12 +62,12 @@ describe('report client', () => { }) ) ) - ) + ); await expect(async () => { - await reportClient.retrieveReportByUrl(MOCK_GENERATE_REPORT_REQUEST_PARAMS, '/reports') - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.BAD_REQUEST) - }) + await reportClient.retrieveReportByUrl(MOCK_GENERATE_REPORT_REQUEST_PARAMS, '/reports'); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.BAD_REQUEST); + }); it('should throw error when calling pollingReport given response status 500', () => { server.use( @@ -79,24 +79,24 @@ describe('report client', () => { }) ) ) - ) + ); expect(async () => { - await reportClient.pollingReport(MOCK_REPORT_URL) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) + await reportClient.pollingReport(MOCK_REPORT_URL); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); it('should return status and response when calling pollingReport given response status 201', async () => { const excepted = { status: HttpStatusCode.Created, response: MOCK_REPORT_RESPONSE, - } + }; server.use( rest.get(MOCK_REPORT_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Created), ctx.json(MOCK_REPORT_RESPONSE)) ) - ) + ); - await expect(reportClient.pollingReport(MOCK_REPORT_URL)).resolves.toEqual(excepted) - }) -}) + await expect(reportClient.pollingReport(MOCK_REPORT_URL)).resolves.toEqual(excepted); + }); +}); diff --git a/frontend/__tests__/src/client/SourceControlClient.test.ts b/frontend/__tests__/src/client/SourceControlClient.test.ts index 5cb7b26fb5..62c13c6f91 100644 --- a/frontend/__tests__/src/client/SourceControlClient.test.ts +++ b/frontend/__tests__/src/client/SourceControlClient.test.ts @@ -1,46 +1,46 @@ -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { MOCK_SOURCE_CONTROL_URL, MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures' -import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient' -import { HttpStatusCode } from 'axios' +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { MOCK_SOURCE_CONTROL_URL, MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, VERIFY_ERROR_MESSAGE } from '../fixtures'; +import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; +import { HttpStatusCode } from 'axios'; -const server = setupServer(rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => res(ctx.status(200)))) +const server = setupServer(rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => res(ctx.status(200)))); describe('verify sourceControl request', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); it('should return isSourceControlVerify true when sourceControl verify response status is 200', async () => { - const result = await sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS) + const result = await sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS); - expect(result.isSourceControlVerify).toEqual(true) - }) + expect(result.isSourceControlVerify).toEqual(true); + }); it('should throw error when sourceControl verify response status is 400', () => { server.use( rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.BadRequest), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.BAD_REQUEST })) ) - ) + ); sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { - expect(e).toBeInstanceOf(Error) - expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.BAD_REQUEST) - }) - }) + expect(e).toBeInstanceOf(Error); + expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.BAD_REQUEST); + }); + }); it('should throw error when sourceControl verify response status is 404', async () => { server.use( rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.NotFound), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.NOT_FOUND })) ) - ) + ); sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { - expect(e).toBeInstanceOf(Error) - expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.NOT_FOUND) - }) - }) + expect(e).toBeInstanceOf(Error); + expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.NOT_FOUND); + }); + }); it('should throw error when sourceControl verify response status 500', async () => { server.use( @@ -52,13 +52,13 @@ describe('verify sourceControl request', () => { }) ) ) - ) + ); sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { - expect(e).toBeInstanceOf(Error) - expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR) - }) - }) + expect(e).toBeInstanceOf(Error); + expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); + }); + }); it('should throw error when sourceControl verify response status is 300', async () => { server.use( @@ -70,10 +70,10 @@ describe('verify sourceControl request', () => { }) ) ) - ) + ); await expect(async () => { - await sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS) - }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN) - }) -}) + await sourceControlClient.getVerifySourceControl(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS); + }).rejects.toThrow(VERIFY_ERROR_MESSAGE.UNKNOWN); + }); +}); diff --git a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx index 3d24e564e4..37227cb7bb 100644 --- a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx +++ b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx @@ -1,11 +1,11 @@ -import { render } from '@testing-library/react' -import DateRangeViewer from '@src/components/Common/DateRangeViewer' +import { render } from '@testing-library/react'; +import DateRangeViewer from '@src/components/Common/DateRangeViewer'; describe('DateRangeVier', () => { it('should show date when render component given startDate and endDate', () => { const { getByText } = render( - ) - expect(getByText(/2022\/01\/01/g)).toBeInTheDocument() - expect(getByText(/2022\/01\/02/g)).toBeInTheDocument() - }) -}) + ); + expect(getByText(/2022\/01\/01/g)).toBeInTheDocument(); + expect(getByText(/2022\/01\/02/g)).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx b/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx index 9a6c82a2a2..a95157b6c3 100644 --- a/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx +++ b/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx @@ -1,23 +1,23 @@ -import React from 'react' -import { render } from '@testing-library/react' -import EllipsisText from '@src/components/Common/EllipsisText' +import React from 'react'; +import { render } from '@testing-library/react'; +import EllipsisText from '@src/components/Common/EllipsisText'; describe('EllipsisText', () => { - const WIDTH = '500rem' + const WIDTH = '500rem'; it('should forward ref properly', () => { - const ref = React.createRef() + const ref = React.createRef(); const { getByLabelText } = render(
test
- ) + ); - const childDOM = getByLabelText('test-ref') - expect(ref.current).toEqual(childDOM) - }) + const childDOM = getByLabelText('test-ref'); + expect(ref.current).toEqual(childDOM); + }); it('should apply fit-content as its width when `fitContent` specified', async () => { const { getByLabelText } = render( @@ -26,11 +26,11 @@ describe('EllipsisText', () => {
test
- ) + ); - const targetElement = getByLabelText('test-ellipsis-text') - expect(targetElement).toHaveStyle({ width: 'fit-content' }) - }) + const targetElement = getByLabelText('test-ellipsis-text'); + expect(targetElement).toHaveStyle({ width: 'fit-content' }); + }); it('should apply fit-content as its width when `fitContent` explicitly set to false', async () => { const { getByLabelText } = render( @@ -39,9 +39,9 @@ describe('EllipsisText', () => {
test
- ) + ); - const targetElement = getByLabelText('test-ellipsis-text') - expect(targetElement).toHaveStyle({ width: 'auto' }) - }) -}) + const targetElement = getByLabelText('test-ellipsis-text'); + expect(targetElement).toHaveStyle({ width: 'auto' }); + }); +}); diff --git a/frontend/__tests__/src/components/Common/NotificationButton/NotificationButton.test.tsx b/frontend/__tests__/src/components/Common/NotificationButton/NotificationButton.test.tsx index 9a3cae106b..d8ae510a8e 100644 --- a/frontend/__tests__/src/components/Common/NotificationButton/NotificationButton.test.tsx +++ b/frontend/__tests__/src/components/Common/NotificationButton/NotificationButton.test.tsx @@ -1,110 +1,110 @@ -import { render, renderHook, waitFor, screen } from '@testing-library/react' -import { NotificationButton } from '@src/components/Common/NotificationButton' -import React from 'react' -import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect' -import { act } from 'react-dom/test-utils' -import userEvent from '@testing-library/user-event' - -const notificationIcon = 'NotificationIcon' +import { render, renderHook, waitFor, screen } from '@testing-library/react'; +import { NotificationButton } from '@src/components/Common/NotificationButton'; +import React from 'react'; +import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect'; +import { act } from 'react-dom/test-utils'; +import userEvent from '@testing-library/user-event'; + +const notificationIcon = 'NotificationIcon'; describe('NotificationButton', () => { - const closeNotificationProps = { open: false, title: 'NotificationPopper', closeAutomatically: false } - const openNotificationProps = { open: true, title: 'NotificationPopper', closeAutomatically: false } - const { result } = renderHook(() => useNotificationLayoutEffect()) + const closeNotificationProps = { open: false, title: 'NotificationPopper', closeAutomatically: false }; + const openNotificationProps = { open: true, title: 'NotificationPopper', closeAutomatically: false }; + const { result } = renderHook(() => useNotificationLayoutEffect()); it('should show NotificationIcon when render NotificationButton component', () => { - const { getByTestId } = render() + const { getByTestId } = render(); - expect(getByTestId(notificationIcon)).toBeInTheDocument() - }) + expect(getByTestId(notificationIcon)).toBeInTheDocument(); + }); it('should show NotificationPopper when clicking the component given the "open" value is true', async () => { act(() => { - result.current.notificationProps = openNotificationProps - }) - const { getByText } = render() - await userEvent.click(screen.getByTestId(notificationIcon)) - expect(getByText('NotificationPopper')).toBeInTheDocument() - }) + result.current.notificationProps = openNotificationProps; + }); + const { getByText } = render(); + await userEvent.click(screen.getByTestId(notificationIcon)); + expect(getByText('NotificationPopper')).toBeInTheDocument(); + }); it('should hide NotificationPopper when clicking the component given the "open" value is false', async () => { act(() => { - result.current.notificationProps = closeNotificationProps - }) - const { getByTestId, queryByText } = render() - await userEvent.click(getByTestId(notificationIcon)) + result.current.notificationProps = closeNotificationProps; + }); + const { getByTestId, queryByText } = render(); + await userEvent.click(getByTestId(notificationIcon)); - expect(queryByText('NotificationPopper')).not.toBeInTheDocument() - }) + expect(queryByText('NotificationPopper')).not.toBeInTheDocument(); + }); it('should call updateProps when clicking outside the component given the "open" value.', async () => { - let checkProps = openNotificationProps + let checkProps = openNotificationProps; act(() => { - result.current.notificationProps = openNotificationProps - result.current.updateProps = jest.fn().mockImplementation(() => (checkProps = closeNotificationProps)) - }) + result.current.notificationProps = openNotificationProps; + result.current.updateProps = jest.fn().mockImplementation(() => (checkProps = closeNotificationProps)); + }); const { getByRole, getByText } = render(
OutSideSection
- ) + ); - expect(getByRole('tooltip')).toBeInTheDocument() + expect(getByRole('tooltip')).toBeInTheDocument(); - const content = await waitFor(() => getByText('OutSideSection')) - await userEvent.click(content) + const content = await waitFor(() => getByText('OutSideSection')); + await userEvent.click(content); - expect(result.current.updateProps).toBeCalledTimes(1) - expect(checkProps).toEqual(closeNotificationProps) - }) + expect(result.current.updateProps).toBeCalledTimes(1); + expect(checkProps).toEqual(closeNotificationProps); + }); it('should hide the NotificationPopper component when call render given notificationProps is undefined.', () => { act(() => { - result.current.notificationProps = undefined - }) - const { queryByText } = render() + result.current.notificationProps = undefined; + }); + const { queryByText } = render(); - expect(queryByText('NotificationPopper')).not.toBeInTheDocument() - }) + expect(queryByText('NotificationPopper')).not.toBeInTheDocument(); + }); it('should hide the NotificationPopper component when call render given updateProps is undefined.', () => { act(() => { - result.current.updateProps = undefined - }) - const { queryByText } = render() + result.current.updateProps = undefined; + }); + const { queryByText } = render(); - expect(queryByText('NotificationPopper')).not.toBeInTheDocument() - }) + expect(queryByText('NotificationPopper')).not.toBeInTheDocument(); + }); it('should hide the Notification component when call render given updateProps and notificationProps are undefined.', () => { act(() => { - result.current.notificationProps = undefined - result.current.updateProps = undefined - }) - const { queryByText } = render() + result.current.notificationProps = undefined; + result.current.updateProps = undefined; + }); + const { queryByText } = render(); - expect(queryByText('NotificationPopper')).not.toBeInTheDocument() - }) + expect(queryByText('NotificationPopper')).not.toBeInTheDocument(); + }); it('should not call updateProps when clicking outside the component given the notificationProps is undefined.', async () => { act(() => { - result.current.notificationProps = undefined - result.current.updateProps = jest.fn() - }) + result.current.notificationProps = undefined; + result.current.updateProps = jest.fn(); + }); const { getByText, getByTestId } = render(
OutSideSection
- ) + ); - expect(getByTestId(notificationIcon)).toBeInTheDocument() + expect(getByTestId(notificationIcon)).toBeInTheDocument(); - const content = await waitFor(() => getByText('OutSideSection')) - await userEvent.click(content) + const content = await waitFor(() => getByText('OutSideSection')); + await userEvent.click(content); - expect(result.current.updateProps).not.toBeCalled() - }) -}) + expect(result.current.updateProps).not.toBeCalled(); + }); +}); diff --git a/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx b/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx index c6cd7fe25f..ce25b4ed95 100644 --- a/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx +++ b/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx @@ -1,5 +1,5 @@ -import { render } from '@testing-library/react' -import { ReportCard } from '@src/components/Common/ReportGrid/ReportCard' +import { render } from '@testing-library/react'; +import { ReportCard } from '@src/components/Common/ReportGrid/ReportCard'; describe('Report Card', () => { it('should not show exceeding items', () => { @@ -16,12 +16,12 @@ describe('Report Card', () => { value: 3, subtitle: 'Total Lead Time', }, - ] + ]; - const { getByText, queryByText } = render() + const { getByText, queryByText } = render(); - expect(getByText('1.00')).toBeInTheDocument() - expect(getByText('2.00')).toBeInTheDocument() - expect(queryByText('3.00')).not.toBeInTheDocument() - }) -}) + expect(getByText('1.00')).toBeInTheDocument(); + expect(getByText('2.00')).toBeInTheDocument(); + expect(queryByText('3.00')).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx b/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx index c964dc497c..340bad01a3 100644 --- a/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx +++ b/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx @@ -1,7 +1,7 @@ -import { render } from '@testing-library/react' -import CollectionDuration from '@src/components/Common/CollectionDuration' -import React from 'react' -import { IMPORTED_NEW_CONFIG_FIXTURE, TIME_DISPLAY_TITTLE_END, TIME_DISPLAY_TITTLE_START } from '../../fixtures' +import { render } from '@testing-library/react'; +import CollectionDuration from '@src/components/Common/CollectionDuration'; +import React from 'react'; +import { IMPORTED_NEW_CONFIG_FIXTURE, TIME_DISPLAY_TITTLE_END, TIME_DISPLAY_TITTLE_START } from '../../fixtures'; describe('Collection Duration', () => { const setup = () => @@ -10,18 +10,18 @@ describe('Collection Duration', () => { startDate={IMPORTED_NEW_CONFIG_FIXTURE.dateRange.startDate} endDate={IMPORTED_NEW_CONFIG_FIXTURE.dateRange.endDate} /> - ) + ); it('should render the start and end text correctly', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(TIME_DISPLAY_TITTLE_START)).toBeInTheDocument() - expect(getByText(TIME_DISPLAY_TITTLE_END)).toBeInTheDocument() - }) + expect(getByText(TIME_DISPLAY_TITTLE_START)).toBeInTheDocument(); + expect(getByText(TIME_DISPLAY_TITTLE_END)).toBeInTheDocument(); + }); it('should render the start and end time with correct format', () => { - const { getByText, getAllByText } = setup() + const { getByText, getAllByText } = setup(); - expect(getByText('16')).toBeInTheDocument() - expect(getAllByText('Mar 23')).toHaveLength(2) - expect(getByText('30')).toBeInTheDocument() - }) -}) + expect(getByText('16')).toBeInTheDocument(); + expect(getAllByText('Mar 23')).toHaveLength(2); + expect(getByText('30')).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/ErrorContent/index.test.tsx b/frontend/__tests__/src/components/ErrorContent/index.test.tsx index aeeba5fda1..55e43ce6b5 100644 --- a/frontend/__tests__/src/components/ErrorContent/index.test.tsx +++ b/frontend/__tests__/src/components/ErrorContent/index.test.tsx @@ -1,14 +1,14 @@ -import { render } from '@testing-library/react' -import ErrorPage from '@src/pages/ErrorPage' -import { BASE_PAGE_ROUTE, ERROR_PAGE_MESSAGE, RETRY_BUTTON } from '../../fixtures' -import React from 'react' -import { BrowserRouter } from 'react-router-dom' -import userEvent from '@testing-library/user-event' -import { navigateMock } from '../../../setupTests' -import { ErrorContent } from '@src/components/ErrorContent' -import { headerClient } from '@src/clients/header/HeaderClient' -import { setupStore } from '../../utils/setupStoreUtil' -import { Provider } from 'react-redux' +import { render } from '@testing-library/react'; +import ErrorPage from '@src/pages/ErrorPage'; +import { BASE_PAGE_ROUTE, ERROR_PAGE_MESSAGE, RETRY_BUTTON } from '../../fixtures'; +import React from 'react'; +import { BrowserRouter } from 'react-router-dom'; +import userEvent from '@testing-library/user-event'; +import { navigateMock } from '../../../setupTests'; +import { ErrorContent } from '@src/components/ErrorContent'; +import { headerClient } from '@src/clients/header/HeaderClient'; +import { setupStore } from '../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; describe('error content', () => { it('should show error message when render error page', () => { @@ -16,23 +16,23 @@ describe('error content', () => { - ) + ); - expect(getByText(ERROR_PAGE_MESSAGE)).toBeInTheDocument() - expect(getByText(RETRY_BUTTON)).toBeInTheDocument() - }) + expect(getByText(ERROR_PAGE_MESSAGE)).toBeInTheDocument(); + expect(getByText(RETRY_BUTTON)).toBeInTheDocument(); + }); it('should go to home page when click button', async () => { - headerClient.getVersion = jest.fn().mockResolvedValue('') + headerClient.getVersion = jest.fn().mockResolvedValue(''); const { getByText } = render( - ) - await userEvent.click(getByText(RETRY_BUTTON)) + ); + await userEvent.click(getByText(RETRY_BUTTON)); - expect(navigateMock).toHaveBeenCalledWith(BASE_PAGE_ROUTE) - }) -}) + expect(navigateMock).toHaveBeenCalledWith(BASE_PAGE_ROUTE); + }); +}); diff --git a/frontend/__tests__/src/components/ErrorNotification/ErrorNotification.test.tsx b/frontend/__tests__/src/components/ErrorNotification/ErrorNotification.test.tsx index 642c7fedaf..cb0b1ae7ab 100644 --- a/frontend/__tests__/src/components/ErrorNotification/ErrorNotification.test.tsx +++ b/frontend/__tests__/src/components/ErrorNotification/ErrorNotification.test.tsx @@ -1,13 +1,13 @@ -import { render } from '@testing-library/react' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { BOARD_TYPES, VERIFY_ERROR_MESSAGE } from '../../fixtures' +import { render } from '@testing-library/react'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { BOARD_TYPES, VERIFY_ERROR_MESSAGE } from '../../fixtures'; describe('error notification', () => { it('should show error message when render error notification', () => { const { getByText } = render( - ) + ); - expect(getByText(`${BOARD_TYPES.JIRA} ${VERIFY_ERROR_MESSAGE.BAD_REQUEST}`)).toBeInTheDocument() - }) -}) + expect(getByText(`${BOARD_TYPES.JIRA} ${VERIFY_ERROR_MESSAGE.BAD_REQUEST}`)).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/ErrorNotification/WarningNotification.test.tsx b/frontend/__tests__/src/components/ErrorNotification/WarningNotification.test.tsx index e3f961762d..b7f042df46 100644 --- a/frontend/__tests__/src/components/ErrorNotification/WarningNotification.test.tsx +++ b/frontend/__tests__/src/components/ErrorNotification/WarningNotification.test.tsx @@ -1,38 +1,38 @@ -import { act, render, waitFor } from '@testing-library/react' -import { setupStore } from '../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import React from 'react' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import { ERROR_MESSAGE_TIME_DURATION } from '../../fixtures' +import { act, render, waitFor } from '@testing-library/react'; +import { setupStore } from '../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import React from 'react'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import { ERROR_MESSAGE_TIME_DURATION } from '../../fixtures'; -let store = null -jest.useFakeTimers() +let store = null; +jest.useFakeTimers(); describe('ErrorNotificationAutoDismiss', () => { - store = setupStore() - const message = 'Test error message' + store = setupStore(); + const message = 'Test error message'; const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); test('renders error message and dismisses after 2 seconds', async () => { - const { getByText, queryByText } = setup() + const { getByText, queryByText } = setup(); - expect(getByText(message)).toBeInTheDocument() + expect(getByText(message)).toBeInTheDocument(); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText(message)).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText(message)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/HomeGuide/HomeGuide.test.tsx b/frontend/__tests__/src/components/HomeGuide/HomeGuide.test.tsx index f149221aba..a4712189ba 100644 --- a/frontend/__tests__/src/components/HomeGuide/HomeGuide.test.tsx +++ b/frontend/__tests__/src/components/HomeGuide/HomeGuide.test.tsx @@ -1,123 +1,123 @@ -import { HomeGuide } from '@src/components/HomeGuide' -import { fireEvent, render, waitFor, screen } from '@testing-library/react' -import { setupStore } from '../../utils/setupStoreUtil' -import { Provider } from 'react-redux' +import { HomeGuide } from '@src/components/HomeGuide'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; +import { setupStore } from '../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; import { CREATE_NEW_PROJECT, HOME_VERIFY_IMPORT_WARNING_MESSAGE, IMPORT_PROJECT_FROM_FILE, METRICS_PAGE_ROUTE, IMPORTED_NEW_CONFIG_FIXTURE, -} from '../../fixtures' -import userEvent from '@testing-library/user-event' -import { navigateMock } from '../../../setupTests' +} from '../../fixtures'; +import userEvent from '@testing-library/user-event'; +import { navigateMock } from '../../../setupTests'; -const mockedUseAppDispatch = jest.fn() +const mockedUseAppDispatch = jest.fn(); jest.mock('@src/hooks/useAppDispatch', () => ({ ...jest.requireActual('react-router-dom'), useAppDispatch: () => mockedUseAppDispatch, -})) +})); -let store = setupStore() +let store = setupStore(); const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) -} + ); +}; const setupInputFile = async (configJson: object) => { - const { queryByText, getByTestId } = setup() + const { queryByText, getByTestId } = setup(); const file = new File([`${JSON.stringify(configJson)}`], 'test.json', { type: 'file', - }) + }); - const input = getByTestId('testInput') + const input = getByTestId('testInput'); Object.defineProperty(input, 'files', { value: [file], - }) + }); - await fireEvent.change(input) - return queryByText -} + await fireEvent.change(input); + return queryByText; +}; describe('HomeGuide', () => { beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should show 2 buttons', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(IMPORT_PROJECT_FROM_FILE)).toBeInTheDocument() - expect(getByText(CREATE_NEW_PROJECT)).toBeInTheDocument() - }) + expect(getByText(IMPORT_PROJECT_FROM_FILE)).toBeInTheDocument(); + expect(getByText(CREATE_NEW_PROJECT)).toBeInTheDocument(); + }); it('should render input when click guide button', async () => { - const { getByTestId } = setup() - const fileInput = getByTestId('testInput') + const { getByTestId } = setup(); + const fileInput = getByTestId('testInput'); - const clickSpy = jest.spyOn(fileInput, 'click') - await userEvent.click(screen.getByText(IMPORT_PROJECT_FROM_FILE)) - expect(clickSpy).toHaveBeenCalled() - }) + const clickSpy = jest.spyOn(fileInput, 'click'); + await userEvent.click(screen.getByText(IMPORT_PROJECT_FROM_FILE)); + expect(clickSpy).toHaveBeenCalled(); + }); it('should go to Metrics page and read file when click import file button', async () => { - const { getByTestId } = setup() + const { getByTestId } = setup(); const file = new File([`${JSON.stringify(IMPORTED_NEW_CONFIG_FIXTURE)}`], 'test.json', { type: 'file', - }) + }); - const input = getByTestId('testInput') + const input = getByTestId('testInput'); Object.defineProperty(input, 'files', { value: [file], - }) + }); - fireEvent.change(input) + fireEvent.change(input); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(3) - expect(navigateMock).toHaveBeenCalledWith(METRICS_PAGE_ROUTE) - }) - }) + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(3); + expect(navigateMock).toHaveBeenCalledWith(METRICS_PAGE_ROUTE); + }); + }); it('should go to Metrics page when click create a new project button', async () => { - setup() - await userEvent.click(screen.getByText(CREATE_NEW_PROJECT)) - expect(navigateMock).toHaveBeenCalledTimes(1) - expect(navigateMock).toHaveBeenCalledWith(METRICS_PAGE_ROUTE) - }) + setup(); + await userEvent.click(screen.getByText(CREATE_NEW_PROJECT)); + expect(navigateMock).toHaveBeenCalledTimes(1); + expect(navigateMock).toHaveBeenCalledWith(METRICS_PAGE_ROUTE); + }); describe('isValidImportedConfig', () => { it('should show warning message when no projectName dateRange metrics all exist', async () => { - const emptyConfig = {} - const queryByText = await setupInputFile(emptyConfig) + const emptyConfig = {}; + const queryByText = await setupInputFile(emptyConfig); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0) - expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).toBeInTheDocument() - }) - }) + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0); + expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).toBeInTheDocument(); + }); + }); it('should no display warning message when projectName dateRange metrics all exist', async () => { - const queryByText = await setupInputFile(IMPORTED_NEW_CONFIG_FIXTURE) + const queryByText = await setupInputFile(IMPORTED_NEW_CONFIG_FIXTURE); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0) - expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).not.toBeInTheDocument() - }) - }) + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0); + expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).not.toBeInTheDocument(); + }); + }); it.each([ ['projectName', { projectName: '', metrics: [], dateRange: {} }], @@ -125,12 +125,12 @@ describe('HomeGuide', () => { ['endDate', { projectName: '', metrics: [], dateRange: { startDate: '', endDate: '2023-02-01' } }], ['metrics', { projectName: '', metrics: ['Metric 1', 'Metric 2'], dateRange: {} }], ])('should not display warning message when only %s exists', async (_, validConfig) => { - const queryByText = await setupInputFile(validConfig) + const queryByText = await setupInputFile(validConfig); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0) - expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).not.toBeInTheDocument() - }) - }) - }) -}) + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(0); + expect(queryByText(HOME_VERIFY_IMPORT_WARNING_MESSAGE)).not.toBeInTheDocument(); + }); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Loading/Loading.test.tsx b/frontend/__tests__/src/components/Loading/Loading.test.tsx index f88d0eed99..cfb4af602f 100644 --- a/frontend/__tests__/src/components/Loading/Loading.test.tsx +++ b/frontend/__tests__/src/components/Loading/Loading.test.tsx @@ -1,17 +1,17 @@ -import { render } from '@testing-library/react' -import { Loading } from '@src/components/Loading' +import { render } from '@testing-library/react'; +import { Loading } from '@src/components/Loading'; describe('Loading', () => { it('should show Loading', () => { - const { container } = render() + const { container } = render(); - expect(container.getElementsByTagName('svg')).toHaveLength(1) - expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar') - }) + expect(container.getElementsByTagName('svg')).toHaveLength(1); + expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar'); + }); it('should show Loading message when has message', () => { - const { getByText } = render() + const { getByText } = render(); - expect(getByText('loading...')).toBeInTheDocument() - }) -}) + expect(getByText('loading...')).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx index 596ebd7179..10d4bb1231 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx @@ -1,5 +1,5 @@ -import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' -import { Board } from '@src/components/Metrics/ConfigStep/Board' +import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import { Board } from '@src/components/Metrics/ConfigStep/Board'; import { BOARD_FIELDS, BOARD_TYPES, @@ -12,221 +12,221 @@ import { VERIFY, VERIFY_ERROR_MESSAGE, VERIFY_FAILED, -} from '../../../fixtures' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { HttpStatusCode } from 'axios' +} from '../../../fixtures'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { HttpStatusCode } from 'axios'; export const fillBoardFieldsInformation = () => { - const fields = ['Board Id', 'Email', 'Project Key', 'Site', 'Token'] - const mockInfo = ['2', 'mockEmail@qq.com', 'mockKey', '1', 'mockToken'] - const fieldInputs = fields.map((label) => screen.getByTestId(label).querySelector('input') as HTMLInputElement) + const fields = ['Board Id', 'Email', 'Project Key', 'Site', 'Token']; + const mockInfo = ['2', 'mockEmail@qq.com', 'mockKey', '1', 'mockToken']; + const fieldInputs = fields.map((label) => screen.getByTestId(label).querySelector('input') as HTMLInputElement); fieldInputs.map((input, index) => { - fireEvent.change(input, { target: { value: mockInfo[index] } }) - }) + fireEvent.change(input, { target: { value: mockInfo[index] } }); + }); fieldInputs.map((input, index) => { - expect(input.value).toEqual(mockInfo[index]) - }) -} + expect(input.value).toEqual(mockInfo[index]); + }); +}; -let store = null +let store = null; -const server = setupServer(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(200)))) +const server = setupServer(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(200)))); describe('Board', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); - store = setupStore() + store = setupStore(); const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; afterEach(() => { - store = null - }) + store = null; + }); it('should show board title and fields when render board component ', () => { - const { getByLabelText, getAllByText } = setup() + const { getByLabelText, getAllByText } = setup(); BOARD_FIELDS.map((field) => { - expect(getByLabelText(`${field} *`)).toBeInTheDocument() - }) - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument() - }) + expect(getByLabelText(`${field} *`)).toBeInTheDocument(); + }); + expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + }); it('should show default value jira when init board component', () => { - const { getByText, queryByText } = setup() - const boardType = getByText(BOARD_TYPES.JIRA) + const { getByText, queryByText } = setup(); + const boardType = getByText(BOARD_TYPES.JIRA); - expect(boardType).toBeInTheDocument() + expect(boardType).toBeInTheDocument(); - const option = queryByText(BOARD_TYPES.CLASSIC_JIRA) - expect(option).not.toBeTruthy() - }) + const option = queryByText(BOARD_TYPES.CLASSIC_JIRA); + expect(option).not.toBeTruthy(); + }); it('should show detail options when click board field', () => { - const { getByRole } = setup() - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionValue = options.map((li) => li.getAttribute('data-value')) + const { getByRole } = setup(); + fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionValue = options.map((li) => li.getAttribute('data-value')); - expect(optionValue).toEqual(Object.values(BOARD_TYPES)) - }) + expect(optionValue).toEqual(Object.values(BOARD_TYPES)); + }); it('should show board type when select board field value ', async () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })) - fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)) + fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); + fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)); await waitFor(() => { - expect(getByText(BOARD_TYPES.CLASSIC_JIRA)).toBeInTheDocument() - }) - }) + expect(getByText(BOARD_TYPES.CLASSIC_JIRA)).toBeInTheDocument(); + }); + }); it('should show error message when input a wrong type or empty email ', async () => { - const { getByTestId, getByText } = setup() - const EMAil_INVALID_ERROR_MESSAGE = 'Email is invalid' - const emailInput = getByTestId('Email').querySelector('input') as HTMLInputElement + const { getByTestId, getByText } = setup(); + const EMAil_INVALID_ERROR_MESSAGE = 'Email is invalid'; + const emailInput = getByTestId('Email').querySelector('input') as HTMLInputElement; - fireEvent.change(emailInput, { target: { value: 'wrong type email' } }) + fireEvent.change(emailInput, { target: { value: 'wrong type email' } }); - expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toBeVisible() - expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toHaveStyle(ERROR_MESSAGE_COLOR) + expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toBeVisible(); + expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toHaveStyle(ERROR_MESSAGE_COLOR); - fireEvent.change(emailInput, { target: { value: '' } }) + fireEvent.change(emailInput, { target: { value: '' } }); - const EMAIL_REQUIRE_ERROR_MESSAGE = 'Email is required' - expect(getByText(EMAIL_REQUIRE_ERROR_MESSAGE)).toBeVisible() - }) + const EMAIL_REQUIRE_ERROR_MESSAGE = 'Email is required'; + expect(getByText(EMAIL_REQUIRE_ERROR_MESSAGE)).toBeVisible(); + }); it('should clear other fields information when change board field selection', () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); const boardIdInput = getByRole('textbox', { name: 'Board Id', - }) as HTMLInputElement + }) as HTMLInputElement; const emailInput = getByRole('textbox', { name: 'Email', - }) as HTMLInputElement + }) as HTMLInputElement; - fireEvent.change(boardIdInput, { target: { value: 2 } }) - fireEvent.change(emailInput, { target: { value: 'mockEmail@qq.com' } }) - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })) - fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)) + fireEvent.change(boardIdInput, { target: { value: 2 } }); + fireEvent.change(emailInput, { target: { value: 'mockEmail@qq.com' } }); + fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); + fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)); - expect(emailInput.value).toEqual('') - expect(boardIdInput.value).toEqual('') - }) + expect(emailInput.value).toEqual(''); + expect(boardIdInput.value).toEqual(''); + }); it('should clear all fields information when click reset button', async () => { - const { getByRole, getByText, queryByRole } = setup() + const { getByRole, getByText, queryByRole } = setup(); const fieldInputs = BOARD_FIELDS.slice(1, 5).map( (label) => screen.getByRole('textbox', { name: label, hidden: true, }) as HTMLInputElement - ) - fillBoardFieldsInformation() + ); + fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)) + fireEvent.click(getByText(VERIFY)); await waitFor(() => { - fireEvent.click(getByRole('button', { name: RESET })) - }) + fireEvent.click(getByRole('button', { name: RESET })); + }); fieldInputs.map((input) => { - expect(input.value).toEqual('') - }) - expect(getByText(BOARD_TYPES.JIRA)).toBeInTheDocument() - expect(queryByRole('button', { name: RESET })).not.toBeTruthy() - expect(queryByRole('button', { name: VERIFY })).toBeDisabled() - }) + expect(input.value).toEqual(''); + }); + expect(getByText(BOARD_TYPES.JIRA)).toBeInTheDocument(); + expect(queryByRole('button', { name: RESET })).not.toBeTruthy(); + expect(queryByRole('button', { name: VERIFY })).toBeDisabled(); + }); it('should enabled verify button when all fields checked correctly given disable verify button', () => { - const { getByRole } = setup() - const verifyButton = getByRole('button', { name: VERIFY }) + const { getByRole } = setup(); + const verifyButton = getByRole('button', { name: VERIFY }); - expect(verifyButton).toBeDisabled() + expect(verifyButton).toBeDisabled(); - fillBoardFieldsInformation() + fillBoardFieldsInformation(); - expect(verifyButton).toBeEnabled() - }) + expect(verifyButton).toBeEnabled(); + }); it('should show reset button and verified button when verify succeed ', async () => { - const { getByText } = setup() - fillBoardFieldsInformation() + const { getByText } = setup(); + fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)) + fireEvent.click(getByText(VERIFY)); await waitFor(() => { - expect(getByText(RESET)).toBeVisible() - }) + expect(getByText(RESET)).toBeVisible(); + }); await waitFor(() => { - expect(getByText(VERIFIED)).toBeTruthy() - }) - }) + expect(getByText(VERIFIED)).toBeTruthy(); + }); + }); it('should called verifyBoard method once when click verify button', async () => { - const { getByRole, getByText } = setup() - fillBoardFieldsInformation() - fireEvent.click(getByRole('button', { name: VERIFY })) + const { getByRole, getByText } = setup(); + fillBoardFieldsInformation(); + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(getByText('Verified')).toBeInTheDocument() - }) - }) + expect(getByText('Verified')).toBeInTheDocument(); + }); + }); it('should check loading animation when click verify button', async () => { - const { getByRole, container } = setup() - fillBoardFieldsInformation() - fireEvent.click(getByRole('button', { name: VERIFY })) + const { getByRole, container } = setup(); + fillBoardFieldsInformation(); + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar') - }) - }) + expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar'); + }); + }); it('should check noCardPop show and disappear when board verify response status is 204', async () => { - server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))) - const { getByText, getByRole } = setup() - fillBoardFieldsInformation() + server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))); + const { getByText, getByRole } = setup(); + fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })) + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument() - }) + expect(getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument(); + }); - fireEvent.click(getByRole('button', { name: 'Ok' })) - expect(getByText(NO_CARD_ERROR_MESSAGE)).not.toBeVisible() - }) + fireEvent.click(getByRole('button', { name: 'Ok' })); + expect(getByText(NO_CARD_ERROR_MESSAGE)).not.toBeVisible(); + }); it('should check error notification show and disappear when board verify response status is 401', async () => { server.use( rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) - ) - const { getByText, getByRole } = setup() - fillBoardFieldsInformation() + ); + const { getByText, getByRole } = setup(); + fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })) + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { expect( getByText(`${BOARD_TYPES.JIRA} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) - ).toBeInTheDocument() - }) - }) -}) + ).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx index 1f9fbf4aba..001cb073ef 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx @@ -1,15 +1,15 @@ -import { act, render } from '@testing-library/react' -import { BranchSelection } from '@src/components/Metrics/ConfigStep/BranchSelection' -import { ALL, BRANCH, MOCK_AUTOCOMPLETE_LIST } from '../../../fixtures' -import { setupStore } from '../../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import userEvent from '@testing-library/user-event' +import { act, render } from '@testing-library/react'; +import { BranchSelection } from '@src/components/Metrics/ConfigStep/BranchSelection'; +import { ALL, BRANCH, MOCK_AUTOCOMPLETE_LIST } from '../../../fixtures'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import userEvent from '@testing-library/user-event'; describe('BranchSelection', () => { - let store = null - const onUpdatePipeline = jest.fn() + let store = null; + const onUpdatePipeline = jest.fn(); const setup = () => { - store = setupStore() + store = setupStore(); const pipelineSetting = { id: 2, @@ -17,48 +17,48 @@ describe('BranchSelection', () => { pipelineName: 'Test', step: 1, branches: MOCK_AUTOCOMPLETE_LIST, - } + }; return render( - ) - } + ); + }; it('should show Branches when render BranchSelection component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Branches')).toBeInTheDocument() - }) + expect(getByText('Branches')).toBeInTheDocument(); + }); it('should has Option 2 when render BranchSelection component', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); - expect(getByRole('button', { name: 'Option 2' })).toBeVisible() - }) + expect(getByRole('button', { name: 'Option 2' })).toBeVisible(); + }); it('should show branches selection when getSteps succeed ', async () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); - expect(getByText(BRANCH)).toBeInTheDocument() + expect(getByText(BRANCH)).toBeInTheDocument(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: 'Branches' })) - }) + await userEvent.click(getByRole('combobox', { name: 'Branches' })); + }); - const allOption = getByRole('option', { name: ALL }) + const allOption = getByRole('option', { name: ALL }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - const optionOne = getByRole('button', { name: 'Option 1' }) + const optionOne = getByRole('button', { name: 'Option 1' }); - expect(optionOne).toBeVisible() + expect(optionOne).toBeVisible(); await act(async () => { - await userEvent.click(optionOne) - }) + await userEvent.click(optionOne); + }); - expect(onUpdatePipeline).toHaveBeenCalledTimes(1) - }) -}) + expect(onUpdatePipeline).toHaveBeenCalledTimes(1); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx index e2c9bde7a7..f0b930008a 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx @@ -1,5 +1,5 @@ -import { act, fireEvent, Matcher, render, waitFor, within } from '@testing-library/react' -import ConfigStep from '@src/components/Metrics/ConfigStep' +import { act, fireEvent, Matcher, render, waitFor, within } from '@testing-library/react'; +import ConfigStep from '@src/components/Metrics/ConfigStep'; import { CHINA_CALENDAR, CONFIG_TITLE, @@ -12,176 +12,176 @@ import { TEST_PROJECT_NAME, VELOCITY, VERIFY, -} from '../../../fixtures' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' -import dayjs from 'dayjs' -import { fillBoardFieldsInformation } from './Board.test' +} from '../../../fixtures'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import dayjs from 'dayjs'; +import { fillBoardFieldsInformation } from './Board.test'; -let store = null +let store = null; jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), selectWarningMessage: jest.fn().mockReturnValue('Test warning Message'), -})) +})); describe('ConfigStep', () => { const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; beforeEach(() => { - jest.useFakeTimers() - }) + jest.useFakeTimers(); + }); afterEach(() => { - store = null - jest.clearAllMocks() - jest.useRealTimers() - }) + store = null; + jest.clearAllMocks(); + jest.useRealTimers(); + }); it('should show project name when render configStep', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(PROJECT_NAME_LABEL)).toBeInTheDocument() - }) + expect(getByText(PROJECT_NAME_LABEL)).toBeInTheDocument(); + }); it('should show project name when input some letters', () => { - const { getByRole, getByDisplayValue } = setup() + const { getByRole, getByDisplayValue } = setup(); const hasInputValue = (e: HTMLElement, inputValue: Matcher) => { - return getByDisplayValue(inputValue) === e - } - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }) + return getByDisplayValue(inputValue) === e; + }; + const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); - expect(input).toBeInTheDocument() + expect(input).toBeInTheDocument(); - fireEvent.change(input, { target: { value: TEST_PROJECT_NAME } }) + fireEvent.change(input, { target: { value: TEST_PROJECT_NAME } }); - expect(hasInputValue(input, TEST_PROJECT_NAME)).toBe(true) - }) + expect(hasInputValue(input, TEST_PROJECT_NAME)).toBe(true); + }); it('should show error message when project name is Empty', () => { - const { getByRole, getByText } = setup() - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }) + const { getByRole, getByText } = setup(); + const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); - fireEvent.change(input, { target: { value: TEST_PROJECT_NAME } }) - fireEvent.change(input, { target: { value: '' } }) + fireEvent.change(input, { target: { value: TEST_PROJECT_NAME } }); + fireEvent.change(input, { target: { value: '' } }); - expect(getByText('Project name is required')).toBeInTheDocument() - }) + expect(getByText('Project name is required')).toBeInTheDocument(); + }); it('should show error message when click project name input with no letter', () => { - const { getByRole, getByText } = setup() - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }) + const { getByRole, getByText } = setup(); + const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); - fireEvent.focus(input) + fireEvent.focus(input); - expect(getByText('Project name is required')).toBeInTheDocument() - }) + expect(getByText('Project name is required')).toBeInTheDocument(); + }); it('should select Regular calendar by default when rendering the radioGroup', () => { - const { getByRole } = setup() - const defaultValue = getByRole('radio', { name: REGULAR_CALENDAR }) - const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }) + const { getByRole } = setup(); + const defaultValue = getByRole('radio', { name: REGULAR_CALENDAR }); + const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }); - expect(defaultValue).toBeChecked() - expect(chinaCalendar).not.toBeChecked() - }) + expect(defaultValue).toBeChecked(); + expect(chinaCalendar).not.toBeChecked(); + }); it('should switch the radio when any radioLabel is selected', () => { - const { getByRole } = setup() - const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }) - const regularCalendar = getByRole('radio', { name: REGULAR_CALENDAR }) - fireEvent.click(chinaCalendar) + const { getByRole } = setup(); + const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }); + const regularCalendar = getByRole('radio', { name: REGULAR_CALENDAR }); + fireEvent.click(chinaCalendar); - expect(chinaCalendar).toBeChecked() - expect(regularCalendar).not.toBeChecked() + expect(chinaCalendar).toBeChecked(); + expect(regularCalendar).not.toBeChecked(); - fireEvent.click(regularCalendar) + fireEvent.click(regularCalendar); - expect(regularCalendar).toBeChecked() - expect(chinaCalendar).not.toBeChecked() - }) + expect(regularCalendar).toBeChecked(); + expect(chinaCalendar).not.toBeChecked(); + }); it('should not show board component when init ConfigStep component ', async () => { - const { queryByText } = setup() + const { queryByText } = setup(); await waitFor(() => { - expect(queryByText(CONFIG_TITLE.BOARD)).toBeNull() - }) - }) + expect(queryByText(CONFIG_TITLE.BOARD)).toBeNull(); + }); + }); it('should show board component when MetricsTypeCheckbox select Velocity,Cycle time', () => { - const { getByRole, getAllByText } = setup() + const { getByRole, getAllByText } = setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })) - const requireDateSelection = within(getByRole('listbox')) - fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })) - fireEvent.click(requireDateSelection.getByRole('option', { name: CYCLE_TIME })) + fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(getByRole('listbox')); + fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); + fireEvent.click(requireDateSelection.getByRole('option', { name: CYCLE_TIME })); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument() - }) + expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + }); it('should show board component when MetricsTypeCheckbox select Classification, ', () => { - const { getByRole, getAllByText } = setup() + const { getByRole, getAllByText } = setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })) - const requireDateSelection = within(getByRole('listbox')) - fireEvent.click(requireDateSelection.getByRole('option', { name: 'Classification' })) + fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(getByRole('listbox')); + fireEvent.click(requireDateSelection.getByRole('option', { name: 'Classification' })); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument() - }) + expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + }); it('should verify again when calendar type is changed given board fields are filled and verified', () => { - const { getByRole, getByText, queryByText } = setup() + const { getByRole, getByText, queryByText } = setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })) - const requireDateSelection = within(getByRole('listbox')) - fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })) - fillBoardFieldsInformation() - fireEvent.click(getByText(VERIFY)) - fireEvent.click(getByText(CHINA_CALENDAR)) + fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(getByRole('listbox')); + fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); + fillBoardFieldsInformation(); + fireEvent.click(getByText(VERIFY)); + fireEvent.click(getByText(CHINA_CALENDAR)); - expect(queryByText(VERIFY)).toBeVisible() - expect(queryByText('Verified')).toBeNull() - expect(queryByText(RESET)).toBeNull() - }) + expect(queryByText(VERIFY)).toBeVisible(); + expect(queryByText('Verified')).toBeNull(); + expect(queryByText(RESET)).toBeNull(); + }); it('should verify again when date picker is changed given board fields are filled and verified', () => { - const { getByRole, getByText, queryByText, getByLabelText } = setup() - const today = dayjs().format('MM/DD/YYYY') - const startDateInput = getByLabelText('From *') - - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })) - const requireDateSelection = within(getByRole('listbox')) - fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })) - fillBoardFieldsInformation() - fireEvent.click(getByText(VERIFY)) - fireEvent.change(startDateInput, { target: { value: today } }) - - expect(queryByText(VERIFY)).toBeVisible() - expect(queryByText('Verified')).toBeNull() - expect(queryByText(RESET)).toBeNull() - }) + const { getByRole, getByText, queryByText, getByLabelText } = setup(); + const today = dayjs().format('MM/DD/YYYY'); + const startDateInput = getByLabelText('From *'); + + fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(getByRole('listbox')); + fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); + fillBoardFieldsInformation(); + fireEvent.click(getByText(VERIFY)); + fireEvent.change(startDateInput, { target: { value: today } }); + + expect(queryByText(VERIFY)).toBeVisible(); + expect(queryByText('Verified')).toBeNull(); + expect(queryByText(RESET)).toBeNull(); + }); it('should show warning message when selectWarningMessage has a value', async () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Test warning Message')).toBeVisible() - }) + expect(getByText('Test warning Message')).toBeVisible(); + }); it('should show disable warning message When selectWarningMessage has a value after two seconds', async () => { - const { queryByText } = setup() + const { queryByText } = setup(); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx index accf085bdb..92c1abe839 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx @@ -1,77 +1,77 @@ -import { fireEvent, render } from '@testing-library/react' -import { DateRangePicker } from '@src/components/Metrics/ConfigStep/DateRangePicker' -import { ERROR_DATE } from '../../../fixtures' -import dayjs from 'dayjs' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' - -const START_DATE_LABEL = 'From *' -const END_DATE_LABEL = 'To *' -const TODAY = dayjs() -const INPUT_DATE_VALUE = TODAY.format('MM/DD/YYYY') -let store = setupStore() +import { fireEvent, render } from '@testing-library/react'; +import { DateRangePicker } from '@src/components/Metrics/ConfigStep/DateRangePicker'; +import { ERROR_DATE } from '../../../fixtures'; +import dayjs from 'dayjs'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; + +const START_DATE_LABEL = 'From *'; +const END_DATE_LABEL = 'To *'; +const TODAY = dayjs(); +const INPUT_DATE_VALUE = TODAY.format('MM/DD/YYYY'); +let store = setupStore(); const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) -} + ); +}; describe('DateRangePicker', () => { const expectDate = (inputDate: HTMLInputElement) => { - expect(inputDate.value).toEqual(expect.stringContaining(TODAY.date().toString())) - expect(inputDate.value).toEqual(expect.stringContaining((TODAY.month() + 1).toString())) - expect(inputDate.value).toEqual(expect.stringContaining(TODAY.year().toString())) - } + expect(inputDate.value).toEqual(expect.stringContaining(TODAY.date().toString())); + expect(inputDate.value).toEqual(expect.stringContaining((TODAY.month() + 1).toString())); + expect(inputDate.value).toEqual(expect.stringContaining(TODAY.year().toString())); + }; it('should render DateRangePicker', () => { - const { queryAllByText } = setup() + const { queryAllByText } = setup(); - expect(queryAllByText(START_DATE_LABEL)).toHaveLength(1) - expect(queryAllByText(END_DATE_LABEL)).toHaveLength(1) - }) + expect(queryAllByText(START_DATE_LABEL)).toHaveLength(1); + expect(queryAllByText(END_DATE_LABEL)).toHaveLength(1); + }); it('should show right start date when input a valid date given init start date is null ', () => { - const { getByRole } = setup() - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement - fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }) + const { getByRole } = setup(); + const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }); - expectDate(startDateInput) - }) + expectDate(startDateInput); + }); it('should show right end date when input a valid date given init end date is null ', () => { - const { getByRole } = setup() - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const { getByRole } = setup(); + const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; - fireEvent.change(endDateInput, { target: { value: INPUT_DATE_VALUE } }) + fireEvent.change(endDateInput, { target: { value: INPUT_DATE_VALUE } }); - expectDate(endDateInput) - }) + expectDate(endDateInput); + }); it('should Auto-fill endDate which is after startDate 13 days when fill right startDate ', () => { - const { getByRole } = setup() - const endDate = TODAY.add(13, 'day') - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const { getByRole } = setup(); + const endDate = TODAY.add(13, 'day'); + const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; - fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }) + fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }); - expect(endDateInput.value).toEqual(expect.stringContaining(endDate.date().toString())) - expect(endDateInput.value).toEqual(expect.stringContaining((endDate.month() + 1).toString())) - expect(endDateInput.value).toEqual(expect.stringContaining(endDate.year().toString())) - }) + expect(endDateInput.value).toEqual(expect.stringContaining(endDate.date().toString())); + expect(endDateInput.value).toEqual(expect.stringContaining((endDate.month() + 1).toString())); + expect(endDateInput.value).toEqual(expect.stringContaining(endDate.year().toString())); + }); it('should not Auto-fill endDate which is after startDate 14 days when fill wrong format startDate ', () => { - const { getByRole } = setup() - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const { getByRole } = setup(); + const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; - fireEvent.change(startDateInput, { target: { value: ERROR_DATE } }) + fireEvent.change(startDateInput, { target: { value: ERROR_DATE } }); - expect(startDateInput.valueAsDate).toEqual(null) - expect(endDateInput.valueAsDate).toEqual(null) - }) -}) + expect(startDateInput.valueAsDate).toEqual(null); + expect(endDateInput.valueAsDate).toEqual(null); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/MetricsTypeCheckbox.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/MetricsTypeCheckbox.test.tsx index c921be903a..eddb7a7239 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/MetricsTypeCheckbox.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/MetricsTypeCheckbox.test.tsx @@ -10,106 +10,106 @@ import { REQUIRED_DATA, REQUIRED_DATA_LIST, VELOCITY, -} from '../../../fixtures' -import { act, fireEvent, render, waitFor, within, screen } from '@testing-library/react' -import { MetricsTypeCheckbox } from '@src/components/Metrics/ConfigStep/MetricsTypeCheckbox' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' -import userEvent from '@testing-library/user-event' -import { SELECTED_VALUE_SEPARATOR } from '@src/constants/commons' -import BasicInfo from '@src/components/Metrics/ConfigStep/BasicInfo' +} from '../../../fixtures'; +import { act, fireEvent, render, waitFor, within, screen } from '@testing-library/react'; +import { MetricsTypeCheckbox } from '@src/components/Metrics/ConfigStep/MetricsTypeCheckbox'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import userEvent from '@testing-library/user-event'; +import { SELECTED_VALUE_SEPARATOR } from '@src/constants/commons'; +import BasicInfo from '@src/components/Metrics/ConfigStep/BasicInfo'; -let store = null +let store = null; describe('MetricsTypeCheckbox', () => { const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; afterEach(() => { - store = null - }) + store = null; + }); it('should show require data and do not display specific options when init', () => { - const { getByText, queryByText } = setup() - const require = getByText(REQUIRED_DATA) + const { getByText, queryByText } = setup(); + const require = getByText(REQUIRED_DATA); - expect(require).toBeInTheDocument() + expect(require).toBeInTheDocument(); - const option = queryByText(VELOCITY) - expect(option).not.toBeInTheDocument() - }) + const option = queryByText(VELOCITY); + expect(option).not.toBeInTheDocument(); + }); it('should show detail options when click require data button', async () => { - const { getByRole } = setup() - await userEvent.click(screen.getByRole('button', { name: REQUIRED_DATA })) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionValue = options.map((li) => li.getAttribute('data-value')) + const { getByRole } = setup(); + await userEvent.click(screen.getByRole('button', { name: REQUIRED_DATA })); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionValue = options.map((li) => li.getAttribute('data-value')); - expect(optionValue).toEqual(REQUIRED_DATA_LIST) - }) + expect(optionValue).toEqual(REQUIRED_DATA_LIST); + }); it('should show multiple selections when multiple options are selected', async () => { - const { getByRole, getByText } = setup() - await userEvent.click(screen.getByRole('button', { name: REQUIRED_DATA })) - const listBox = within(getByRole('listbox')) + const { getByRole, getByText } = setup(); + await userEvent.click(screen.getByRole('button', { name: REQUIRED_DATA })); + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(listBox.getByRole('option', { name: VELOCITY })); + }); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: CYCLE_TIME })) - }) + await userEvent.click(listBox.getByRole('option', { name: CYCLE_TIME })); + }); - expect(getByText([VELOCITY, CYCLE_TIME].join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument() - }) + expect(getByText([VELOCITY, CYCLE_TIME].join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument(); + }); it('should show all selections when all option are select', async () => { - const { getByRole, getByText } = setup() - const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8) + const { getByRole, getByText } = setup(); + const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) - const listBox = within(getByRole('listbox')) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: ALL })) - }) + await userEvent.click(listBox.getByRole('option', { name: ALL })); + }); - expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true') - expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument() - }) + expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true'); + expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument(); + }); it('should show all selections when click velocity selection and then click all selection', async () => { - const { getByRole, getByText } = setup() - const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8) + const { getByRole, getByText } = setup(); + const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(listBox.getByRole('option', { name: VELOCITY })); + }); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: ALL })) - }) + await userEvent.click(listBox.getByRole('option', { name: ALL })); + }); - expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true') - expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument() - }) + expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true'); + expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument(); + }); it('should be checked of All selected option when click any other options', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); const optionsToClick = [ listBox.getByRole('option', { name: VELOCITY }), listBox.getByRole('option', { name: CYCLE_TIME }), @@ -118,94 +118,94 @@ describe('MetricsTypeCheckbox', () => { listBox.getByRole('option', { name: DEPLOYMENT_FREQUENCY }), listBox.getByRole('option', { name: CHANGE_FAILURE_RATE }), listBox.getByRole('option', { name: MEAN_TIME_TO_RECOVERY }), - ] - await Promise.all(optionsToClick.map((opt) => fireEvent.click(opt))) + ]; + await Promise.all(optionsToClick.map((opt) => fireEvent.click(opt))); await waitFor(() => { - expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true') - }) - }) + expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'true'); + }); + }); it('should show some selections when click all option and then click velocity selection', async () => { - const { getByRole, getByText } = setup() - const displayedDataList = REQUIRED_DATA_LIST.slice(1, 7) + const { getByRole, getByText } = setup(); + const displayedDataList = REQUIRED_DATA_LIST.slice(1, 7); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: ALL })) - }) + await userEvent.click(listBox.getByRole('option', { name: ALL })); + }); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: MEAN_TIME_TO_RECOVERY })) - }) + await userEvent.click(listBox.getByRole('option', { name: MEAN_TIME_TO_RECOVERY })); + }); - expect(listBox.getByRole('option', { name: MEAN_TIME_TO_RECOVERY })).toHaveAttribute('aria-selected', 'false') - expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'false') - expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument() - }) + expect(listBox.getByRole('option', { name: MEAN_TIME_TO_RECOVERY })).toHaveAttribute('aria-selected', 'false'); + expect(listBox.getByRole('option', { name: ALL })).toHaveAttribute('aria-selected', 'false'); + expect(getByText(displayedDataList.join(SELECTED_VALUE_SEPARATOR))).toBeInTheDocument(); + }); it('should show none selection when double click all option', async () => { - const { getByRole, getByText } = setup() - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - const listBox = within(getByRole('listbox')) - await userEvent.dblClick(listBox.getByRole('option', { name: ALL })) - await userEvent.click(getByRole('listbox', { name: REQUIRED_DATA })) + const { getByRole, getByText } = setup(); + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + const listBox = within(getByRole('listbox')); + await userEvent.dblClick(listBox.getByRole('option', { name: ALL })); + await userEvent.click(getByRole('listbox', { name: REQUIRED_DATA })); - const errorMessage = getByText('Metrics is required') - await waitFor(() => expect(errorMessage).toBeInTheDocument()) - }) + const errorMessage = getByText('Metrics is required'); + await waitFor(() => expect(errorMessage).toBeInTheDocument()); + }); it('should show error message when require data is null', async () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) - const listBox = within(getByRole('listbox')) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(listBox.getByRole('option', { name: VELOCITY })); + }); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(listBox.getByRole('option', { name: VELOCITY })); + }); await act(async () => { - await userEvent.click(getByRole('listbox', { name: REQUIRED_DATA })) - }) + await userEvent.click(getByRole('listbox', { name: REQUIRED_DATA })); + }); - const errorMessage = getByText('Metrics is required') - expect(errorMessage).toBeInTheDocument() - }) + const errorMessage = getByText('Metrics is required'); + expect(errorMessage).toBeInTheDocument(); + }); it('should show board component when click MetricsTypeCheckbox selection velocity ', async () => { - const { getByRole, getAllByText } = setup() + const { getByRole, getAllByText } = setup(); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) - const listBox = within(getByRole('listbox')) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(listBox.getByRole('option', { name: VELOCITY })); + }); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument() - }) + expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + }); it('should hidden board component when MetricsTypeCheckbox select is null given MetricsTypeCheckbox select is velocity ', async () => { - const { getByRole, queryByText } = setup() + const { getByRole, queryByText } = setup(); await act(async () => { - await userEvent.click(getByRole('button', { name: REQUIRED_DATA })) - }) - const requireDateSelection = within(getByRole('listbox')) + await userEvent.click(getByRole('button', { name: REQUIRED_DATA })); + }); + const requireDateSelection = within(getByRole('listbox')); await act(async () => { - await userEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); + }); await act(async () => { - await userEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })) - }) + await userEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); + }); - expect(queryByText(CONFIG_TITLE.BOARD)).not.toBeInTheDocument() - }) -}) + expect(queryByText(CONFIG_TITLE.BOARD)).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/NoCardPop.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/NoCardPop.test.tsx index d5afd0af1a..3817b59cda 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/NoCardPop.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/NoCardPop.test.tsx @@ -1,23 +1,23 @@ -import { fireEvent, render } from '@testing-library/react' -import { NoCardPop } from '@src/components/Metrics/ConfigStep/NoDoneCardPop' -import { NO_CARD_ERROR_MESSAGE } from '../../../fixtures' +import { fireEvent, render } from '@testing-library/react'; +import { NoCardPop } from '@src/components/Metrics/ConfigStep/NoDoneCardPop'; +import { NO_CARD_ERROR_MESSAGE } from '../../../fixtures'; -const OK = 'Ok' +const OK = 'Ok'; describe('NoCardPop', () => { it('should show NoCardPop component given isOpen param is true', () => { - const { getByText, getByRole } = render() + const { getByText, getByRole } = render(); - expect(getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument() - expect(getByRole('button', { name: OK })).toBeInTheDocument() - }) + expect(getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument(); + expect(getByRole('button', { name: OK })).toBeInTheDocument(); + }); it('should call onClose function when click Ok button given isOpen param is true', () => { - const handleClose = jest.fn() - const { getByRole } = render() - const okButton = getByRole('button', { name: OK }) + const handleClose = jest.fn(); + const { getByRole } = render(); + const okButton = getByRole('button', { name: OK }); - fireEvent.click(okButton) + fireEvent.click(okButton); - expect(handleClose).toBeCalledTimes(1) - }) -}) + expect(handleClose).toBeCalledTimes(1); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/PipelineTool.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/PipelineTool.test.tsx index 70b0344d25..ee82409bba 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/PipelineTool.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/PipelineTool.test.tsx @@ -1,5 +1,5 @@ -import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' -import { PipelineTool } from '@src/components/Metrics/ConfigStep/PipelineTool' +import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import { PipelineTool } from '@src/components/Metrics/ConfigStep/PipelineTool'; import { CONFIG_TITLE, ERROR_MESSAGE_COLOR, @@ -13,23 +13,23 @@ import { VERIFY, VERIFY_ERROR_MESSAGE, VERIFY_FAILED, -} from '../../../fixtures' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import userEvent from '@testing-library/user-event' -import { HttpStatusCode } from 'axios' +} from '../../../fixtures'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import userEvent from '@testing-library/user-event'; +import { HttpStatusCode } from 'axios'; export const fillPipelineToolFieldsInformation = async () => { - const mockInfo = 'bkua_mockTokenMockTokenMockTokenMockToken1234' - const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement - await userEvent.type(tokenInput, mockInfo) + const mockInfo = 'bkua_mockTokenMockTokenMockTokenMockToken1234'; + const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement; + await userEvent.type(tokenInput, mockInfo); - expect(tokenInput.value).toEqual(mockInfo) -} + expect(tokenInput.value).toEqual(mockInfo); +}; -let store = null +let store = null; const server = setupServer( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => @@ -45,157 +45,157 @@ const server = setupServer( ctx.status(200) ) ) -) +); describe('PipelineTool', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) - store = setupStore() + beforeAll(() => server.listen()); + afterAll(() => server.close()); + store = setupStore(); const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; afterEach(() => { - store = null - }) + store = null; + }); it('should show pipelineTool title and fields when render pipelineTool component ', () => { - const { getByLabelText, getAllByText } = setup() + const { getByLabelText, getAllByText } = setup(); PIPELINE_TOOL_FIELDS.map((field) => { - expect(getByLabelText(`${field} *`)).toBeInTheDocument() - }) + expect(getByLabelText(`${field} *`)).toBeInTheDocument(); + }); - expect(getAllByText(CONFIG_TITLE.PIPELINE_TOOL)[0]).toBeInTheDocument() - }) + expect(getAllByText(CONFIG_TITLE.PIPELINE_TOOL)[0]).toBeInTheDocument(); + }); it('should show default value buildKite when init pipelineTool component', () => { - const { getByText, queryByText } = setup() - const pipelineToolType = getByText(PIPELINE_TOOL_TYPES.BUILD_KITE) + const { getByText, queryByText } = setup(); + const pipelineToolType = getByText(PIPELINE_TOOL_TYPES.BUILD_KITE); - expect(pipelineToolType).toBeInTheDocument() + expect(pipelineToolType).toBeInTheDocument(); - const option = queryByText(PIPELINE_TOOL_TYPES.GO_CD) + const option = queryByText(PIPELINE_TOOL_TYPES.GO_CD); - expect(option).not.toBeInTheDocument() - }) + expect(option).not.toBeInTheDocument(); + }); it('should clear other fields information when change pipelineTool Field selection', async () => { - setup() - const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement + setup(); + const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement; - await fillPipelineToolFieldsInformation() - await userEvent.click(screen.getByRole('button', { name: 'Pipeline Tool' })) + await fillPipelineToolFieldsInformation(); + await userEvent.click(screen.getByRole('button', { name: 'Pipeline Tool' })); - await userEvent.click(screen.getByText(PIPELINE_TOOL_TYPES.GO_CD)) - expect(tokenInput.value).toEqual('') - }) + await userEvent.click(screen.getByText(PIPELINE_TOOL_TYPES.GO_CD)); + expect(tokenInput.value).toEqual(''); + }); it('should clear all fields information when click reset button', async () => { - const { getByText, queryByRole } = setup() - const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement - await fillPipelineToolFieldsInformation() + const { getByText, queryByRole } = setup(); + const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement; + await fillPipelineToolFieldsInformation(); - await userEvent.click(screen.getByText(VERIFY)) + await userEvent.click(screen.getByText(VERIFY)); - await userEvent.click(screen.getByRole('button', { name: RESET })) + await userEvent.click(screen.getByRole('button', { name: RESET })); - expect(tokenInput.value).toEqual('') - expect(getByText(PIPELINE_TOOL_TYPES.BUILD_KITE)).toBeInTheDocument() - expect(queryByRole('button', { name: RESET })).not.toBeInTheDocument() - expect(queryByRole('button', { name: VERIFY })).toBeDisabled() - }) + expect(tokenInput.value).toEqual(''); + expect(getByText(PIPELINE_TOOL_TYPES.BUILD_KITE)).toBeInTheDocument(); + expect(queryByRole('button', { name: RESET })).not.toBeInTheDocument(); + expect(queryByRole('button', { name: VERIFY })).toBeDisabled(); + }); it('should show detail options when click pipelineTool fields', async () => { - const { getByRole } = setup() - await userEvent.click(screen.getByRole('button', { name: 'Pipeline Tool' })) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionValue = options.map((li) => li.getAttribute('data-value')) + const { getByRole } = setup(); + await userEvent.click(screen.getByRole('button', { name: 'Pipeline Tool' })); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionValue = options.map((li) => li.getAttribute('data-value')); - expect(optionValue).toEqual(Object.values(PIPELINE_TOOL_TYPES)) - }) + expect(optionValue).toEqual(Object.values(PIPELINE_TOOL_TYPES)); + }); it('should enabled verify button when all fields checked correctly given disable verify button', async () => { - const { getByRole } = setup() - const verifyButton = getByRole('button', { name: VERIFY }) + const { getByRole } = setup(); + const verifyButton = getByRole('button', { name: VERIFY }); - expect(verifyButton).toBeDisabled() + expect(verifyButton).toBeDisabled(); - await fillPipelineToolFieldsInformation() + await fillPipelineToolFieldsInformation(); - expect(verifyButton).toBeEnabled() - }) + expect(verifyButton).toBeEnabled(); + }); it('should show error message and error style when token is empty', async () => { - const { getByText } = setup() - await fillPipelineToolFieldsInformation() - const mockInfo = 'mockToken' - const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement - await userEvent.type(tokenInput, mockInfo) - await userEvent.clear(tokenInput) + const { getByText } = setup(); + await fillPipelineToolFieldsInformation(); + const mockInfo = 'mockToken'; + const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement; + await userEvent.type(tokenInput, mockInfo); + await userEvent.clear(tokenInput); - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toBeVisible() - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR) - }) + expect(getByText(TOKEN_ERROR_MESSAGE[1])).toBeVisible(); + expect(getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR); + }); it('should show error message and error style when token is invalid', async () => { - const { getByText } = setup() - const mockInfo = 'mockToken' - const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement - await userEvent.type(tokenInput, mockInfo) + const { getByText } = setup(); + const mockInfo = 'mockToken'; + const tokenInput = screen.getByTestId('pipelineToolTextField').querySelector('input') as HTMLInputElement; + await userEvent.type(tokenInput, mockInfo); - expect(tokenInput.value).toEqual(mockInfo) + expect(tokenInput.value).toEqual(mockInfo); - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument() - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR) - }) + expect(getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument(); + expect(getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR); + }); it('should show reset button and verified button when verify succeed ', async () => { - const { getByText } = setup() - await fillPipelineToolFieldsInformation() + const { getByText } = setup(); + await fillPipelineToolFieldsInformation(); - await userEvent.click(screen.getByText(VERIFY)) - expect(screen.getByText(RESET)).toBeVisible() + await userEvent.click(screen.getByText(VERIFY)); + expect(screen.getByText(RESET)).toBeVisible(); await waitFor(() => { - expect(getByText(VERIFIED)).toBeTruthy() - }) - }) + expect(getByText(VERIFIED)).toBeTruthy(); + }); + }); it('should called verifyPipelineTool method once when click verify button', async () => { - const { getByText } = setup() - await fillPipelineToolFieldsInformation() - await userEvent.click(screen.getByRole('button', { name: VERIFY })) + const { getByText } = setup(); + await fillPipelineToolFieldsInformation(); + await userEvent.click(screen.getByRole('button', { name: VERIFY })); - expect(getByText('Verified')).toBeInTheDocument() - }) + expect(getByText('Verified')).toBeInTheDocument(); + }); it('should check loading animation when click verify button', async () => { - const { getByRole, container } = setup() - await fillPipelineToolFieldsInformation() - fireEvent.click(getByRole('button', { name: VERIFY })) + const { getByRole, container } = setup(); + await fillPipelineToolFieldsInformation(); + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar') - }) - }) + expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar'); + }); + }); it('should check error notification show when pipelineTool verify response status is 401', async () => { server.use( rest.post(MOCK_PIPELINE_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) - ) - const { getByText } = setup() - await fillPipelineToolFieldsInformation() + ); + const { getByText } = setup(); + await fillPipelineToolFieldsInformation(); - await userEvent.click(screen.getByRole('button', { name: VERIFY })) + await userEvent.click(screen.getByRole('button', { name: VERIFY })); expect( getByText(`${MOCK_PIPELINE_VERIFY_REQUEST_PARAMS.type} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) - ).toBeInTheDocument() - }) -}) + ).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx index 4928ce2109..b89593a9e0 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx @@ -1,7 +1,7 @@ -import { setupStore } from '../../../utils/setupStoreUtil' -import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import { Provider } from 'react-redux' -import { SourceControl } from '@src/components/Metrics/ConfigStep/SourceControl' +import { setupStore } from '../../../utils/setupStoreUtil'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { SourceControl } from '@src/components/Metrics/ConfigStep/SourceControl'; import { CONFIG_TITLE, ERROR_MESSAGE_COLOR, @@ -14,21 +14,23 @@ import { VERIFY, VERIFY_ERROR_MESSAGE, VERIFY_FAILED, -} from '../../../fixtures' -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { HttpStatusCode } from 'axios' +} from '../../../fixtures'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { HttpStatusCode } from 'axios'; export const fillSourceControlFieldsInformation = () => { - const mockInfo = 'ghpghoghughsghr_1A2b1A2b1A2b1A2b1A2b1A2b1A2b1A2b1A2b' - const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement + const mockInfo = 'AAAAA_XXXXXX' + .replace('AAAAA', 'ghpghoghughsghr') + .replace('XXXXXX', '1A2b1A2b1A2b1A2b1A2b1A2b1A2b1A2b1A2b'); + const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; - fireEvent.change(tokenInput, { target: { value: mockInfo } }) + fireEvent.change(tokenInput, { target: { value: mockInfo } }); - expect(tokenInput.value).toEqual(mockInfo) -} + expect(tokenInput.value).toEqual(mockInfo); +}; -let store = null +let store = null; const server = setupServer( rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => @@ -39,126 +41,126 @@ const server = setupServer( ctx.status(200) ) ) -) +); describe('SourceControl', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) - store = setupStore() + beforeAll(() => server.listen()); + afterAll(() => server.close()); + store = setupStore(); const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) - } + ); + }; afterEach(() => { - store = null - }) + store = null; + }); it('should show sourceControl title and fields when render sourceControl component', () => { - const { getByLabelText, getAllByText } = setup() + const { getByLabelText, getAllByText } = setup(); - expect(getAllByText(CONFIG_TITLE.SOURCE_CONTROL)[0]).toBeInTheDocument() + expect(getAllByText(CONFIG_TITLE.SOURCE_CONTROL)[0]).toBeInTheDocument(); SOURCE_CONTROL_FIELDS.map((field) => { - expect(getByLabelText(`${field} *`)).toBeInTheDocument() - }) - }) + expect(getByLabelText(`${field} *`)).toBeInTheDocument(); + }); + }); it('should show default value gitHub when init sourceControl component', () => { - const { getByText } = setup() - const sourceControlType = getByText(SOURCE_CONTROL_TYPES.GITHUB) + const { getByText } = setup(); + const sourceControlType = getByText(SOURCE_CONTROL_TYPES.GITHUB); - expect(sourceControlType).toBeInTheDocument() - }) + expect(sourceControlType).toBeInTheDocument(); + }); it('should clear all fields information when click reset button', async () => { - const { getByRole, getByText, queryByRole } = setup() - const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement + const { getByRole, getByText, queryByRole } = setup(); + const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; - fillSourceControlFieldsInformation() + fillSourceControlFieldsInformation(); - fireEvent.click(getByText(VERIFY)) + fireEvent.click(getByText(VERIFY)); await waitFor(() => { - expect(getByRole('button', { name: RESET })).toBeTruthy() - fireEvent.click(getByRole('button', { name: RESET })) - }) + expect(getByRole('button', { name: RESET })).toBeTruthy(); + fireEvent.click(getByRole('button', { name: RESET })); + }); - expect(tokenInput.value).toEqual('') - expect(getByText(SOURCE_CONTROL_TYPES.GITHUB)).toBeInTheDocument() - expect(queryByRole('button', { name: RESET })).not.toBeTruthy() - expect(getByRole('button', { name: VERIFY })).toBeDisabled() - }) + expect(tokenInput.value).toEqual(''); + expect(getByText(SOURCE_CONTROL_TYPES.GITHUB)).toBeInTheDocument(); + expect(queryByRole('button', { name: RESET })).not.toBeTruthy(); + expect(getByRole('button', { name: VERIFY })).toBeDisabled(); + }); it('should enable verify button when all fields checked correctly given disable verify button', () => { - const { getByRole } = setup() - const verifyButton = getByRole('button', { name: VERIFY }) + const { getByRole } = setup(); + const verifyButton = getByRole('button', { name: VERIFY }); - expect(verifyButton).toBeDisabled() + expect(verifyButton).toBeDisabled(); - fillSourceControlFieldsInformation() + fillSourceControlFieldsInformation(); - expect(verifyButton).toBeEnabled() - }) + expect(verifyButton).toBeEnabled(); + }); it('should show reset button and verified button when verify successfully', async () => { - const { getByText } = setup() - fillSourceControlFieldsInformation() + const { getByText } = setup(); + fillSourceControlFieldsInformation(); - fireEvent.click(getByText(VERIFY)) + fireEvent.click(getByText(VERIFY)); await waitFor(() => { - expect(getByText(RESET)).toBeTruthy() - }) + expect(getByText(RESET)).toBeTruthy(); + }); await waitFor(() => { - expect(getByText(VERIFIED)).toBeTruthy() - }) - }) + expect(getByText(VERIFIED)).toBeTruthy(); + }); + }); it('should show error message and error style when token is empty', () => { - const { getByText } = setup() + const { getByText } = setup(); - fillSourceControlFieldsInformation() + fillSourceControlFieldsInformation(); - const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement + const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; - fireEvent.change(tokenInput, { target: { value: '' } }) + fireEvent.change(tokenInput, { target: { value: '' } }); - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toBeInTheDocument() - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR) - }) + expect(getByText(TOKEN_ERROR_MESSAGE[1])).toBeInTheDocument(); + expect(getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR); + }); it('should show error message and error style when token is invalid', () => { - const { getByText } = setup() - const mockInfo = 'mockToken' - const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement + const { getByText } = setup(); + const mockInfo = 'mockToken'; + const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; - fireEvent.change(tokenInput, { target: { value: mockInfo } }) + fireEvent.change(tokenInput, { target: { value: mockInfo } }); - expect(tokenInput.value).toEqual(mockInfo) - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument() - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR) - }) + expect(tokenInput.value).toEqual(mockInfo); + expect(getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument(); + expect(getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR); + }); it('should show error notification when sourceControl verify response status is 401', async () => { server.use( rest.post(MOCK_SOURCE_CONTROL_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) - ) - const { getByText, getByRole } = setup() + ); + const { getByText, getByRole } = setup(); - fillSourceControlFieldsInformation() + fillSourceControlFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })) + fireEvent.click(getByRole('button', { name: VERIFY })); await waitFor(() => { expect( getByText(`${SOURCE_CONTROL_TYPES.GITHUB} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) - ).toBeInTheDocument() - }) - }) -}) + ).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx index a2a0cfd2d2..0b86eb7ea1 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx @@ -1,130 +1,130 @@ -import { act, render, waitFor, within } from '@testing-library/react' -import { Classification } from '@src/components/Metrics/MetricsStep/Classification' -import userEvent from '@testing-library/user-event' -import { setupStore } from '../../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import { ERROR_MESSAGE_TIME_DURATION } from '../../../fixtures' - -const mockTitle = 'Classification Setting' -const mockLabel = 'Distinguished by' +import { act, render, waitFor, within } from '@testing-library/react'; +import { Classification } from '@src/components/Metrics/MetricsStep/Classification'; +import userEvent from '@testing-library/user-event'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import { ERROR_MESSAGE_TIME_DURATION } from '../../../fixtures'; + +const mockTitle = 'Classification Setting'; +const mockLabel = 'Distinguished by'; const mockTargetFields = [ { flag: true, key: 'issue', name: 'Issue' }, { flag: false, key: 'type', name: 'Type' }, -] +]; jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), selectIsProjectCreated: jest.fn().mockReturnValue(false), -})) +})); jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), selectClassificationWarningMessage: jest.fn().mockReturnValue('Test warning Message'), -})) +})); -let store = setupStore() +let store = setupStore(); const setup = () => { return render( - ) -} + ); +}; describe('Classification', () => { beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should show Classification when render Classification component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(mockTitle)).toBeInTheDocument() - expect(getByText(mockLabel)).toBeInTheDocument() - }) + expect(getByText(mockTitle)).toBeInTheDocument(); + expect(getByText(mockLabel)).toBeInTheDocument(); + }); it('should show default options when initialization', () => { - const { queryByText, getByText } = setup() + const { queryByText, getByText } = setup(); - expect(getByText('Issue')).toBeInTheDocument() - expect(queryByText('Type')).not.toBeInTheDocument() - }) + expect(getByText('Issue')).toBeInTheDocument(); + expect(queryByText('Type')).not.toBeInTheDocument(); + }); it('should show all options when click selectBox', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - expect(getByRole('option', { name: 'Issue' })).toBeInTheDocument() - expect(getByRole('option', { name: 'Type' })).toBeInTheDocument() - }) + expect(getByRole('option', { name: 'Issue' })).toBeInTheDocument(); + expect(getByRole('option', { name: 'Type' })).toBeInTheDocument(); + }); it('should show all targetField when click All and show nothing when cancel click', async () => { - const { getByText, getByRole, queryByRole } = setup() + const { getByText, getByRole, queryByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); await act(async () => { - await userEvent.click(getByText('All')) - }) - const names = mockTargetFields.map((item) => item.name) + await userEvent.click(getByText('All')); + }); + const names = mockTargetFields.map((item) => item.name); - expect(getByRole('button', { name: names[0] })).toBeVisible() - expect(getByRole('button', { name: names[1] })).toBeVisible() + expect(getByRole('button', { name: names[0] })).toBeVisible(); + expect(getByRole('button', { name: names[1] })).toBeVisible(); await act(async () => { - await userEvent.click(getByText('All')) - }) + await userEvent.click(getByText('All')); + }); - expect(queryByRole('button', { name: names[0] })).not.toBeInTheDocument() - expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument() - }) + expect(queryByRole('button', { name: names[0] })).not.toBeInTheDocument(); + expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); + }); it('should show selected targetField when click selected field', async () => { - const { getByRole, getByText, queryByRole } = setup() - const names = mockTargetFields.map((item) => item.name) + const { getByRole, getByText, queryByRole } = setup(); + const names = mockTargetFields.map((item) => item.name); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); await act(async () => { - await userEvent.click(getByText('All')) - }) + await userEvent.click(getByText('All')); + }); await act(async () => { - await userEvent.click(getByText('All')) - }) + await userEvent.click(getByText('All')); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: names[0] })) - }) + await userEvent.click(listBox.getByRole('option', { name: names[0] })); + }); - expect(queryByRole('button', { name: names[0] })).toBeInTheDocument() - expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument() - }) + expect(queryByRole('button', { name: names[0] })).toBeInTheDocument(); + expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); + }); it('should show warning message when classification warning message has a value in cycleTime component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Test warning Message')).toBeVisible() - }) + expect(getByText('Test warning Message')).toBeVisible(); + }); it('should show disable warning message when classification warning message has a value after two seconds in cycleTime component', async () => { - jest.useFakeTimers() - const { queryByText } = setup() + jest.useFakeTimers(); + const { queryByText } = setup(); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx index b7fcf248f4..09da6d2363 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx @@ -1,154 +1,154 @@ -import { act, render, waitFor, within } from '@testing-library/react' -import { Crews } from '@src/components/Metrics/MetricsStep/Crews' -import userEvent from '@testing-library/user-event' -import { setupStore } from '../../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import { updateAssigneeFilter } from '@src/context/Metrics/metricsSlice' - -const mockOptions = ['crew A', 'crew B'] -const mockTitle = 'Crews Setting' -const mockLabel = 'Included Crews' -const assigneeFilterLabels = ['Last assignee', 'Historical assignee'] -const assigneeFilterValues = ['lastAssignee', 'historicalAssignee'] +import { act, render, waitFor, within } from '@testing-library/react'; +import { Crews } from '@src/components/Metrics/MetricsStep/Crews'; +import userEvent from '@testing-library/user-event'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import { updateAssigneeFilter } from '@src/context/Metrics/metricsSlice'; + +const mockOptions = ['crew A', 'crew B']; +const mockTitle = 'Crews Setting'; +const mockLabel = 'Included Crews'; +const assigneeFilterLabels = ['Last assignee', 'Historical assignee']; +const assigneeFilterValues = ['lastAssignee', 'historicalAssignee']; jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), selectMetricsContent: jest.fn().mockReturnValue({ users: ['crew A', 'crew B'] }), -})) +})); -const mockedUseAppDispatch = jest.fn() +const mockedUseAppDispatch = jest.fn(); jest.mock('@src/hooks/useAppDispatch', () => ({ useAppDispatch: () => mockedUseAppDispatch, -})) +})); -let store = setupStore() +let store = setupStore(); const setup = () => { return render( - ) -} + ); +}; describe('Crew', () => { beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should show Crews when render Crews component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(mockTitle)).toBeInTheDocument() - }) + expect(getByText(mockTitle)).toBeInTheDocument(); + }); it('should selected all options by default when initializing', () => { - const { getByRole } = setup() + const { getByRole } = setup(); - expect(getByRole('button', { name: 'crew A' })).toBeInTheDocument() - expect(getByRole('button', { name: 'crew B' })).toBeInTheDocument() - }) + expect(getByRole('button', { name: 'crew A' })).toBeInTheDocument(); + expect(getByRole('button', { name: 'crew B' })).toBeInTheDocument(); + }); it('should show detail options when click Included crews button', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) - const listBox = within(getByRole('listbox')) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); + const listBox = within(getByRole('listbox')); - expect(listBox.getByRole('option', { name: 'All' })).toBeVisible() - expect(listBox.getByRole('option', { name: 'crew A' })).toBeVisible() - expect(listBox.getByRole('option', { name: 'crew B' })).toBeVisible() - }) + expect(listBox.getByRole('option', { name: 'All' })).toBeVisible(); + expect(listBox.getByRole('option', { name: 'crew A' })).toBeVisible(); + expect(listBox.getByRole('option', { name: 'crew B' })).toBeVisible(); + }); it('should show error message when crews is null', async () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); await act(async () => { - await userEvent.click(getByText('All')) - }) + await userEvent.click(getByText('All')); + }); - const requiredText = getByText('required') - expect(requiredText.tagName).toBe('STRONG') - }) + const requiredText = getByText('required'); + expect(requiredText.tagName).toBe('STRONG'); + }); it('should show other selections when cancel one option given default all selections in crews', async () => { - const { getByRole, queryByRole } = setup() + const { getByRole, queryByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: mockOptions[0] })) - }) + await userEvent.click(listBox.getByRole('option', { name: mockOptions[0] })); + }); - expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument() - expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument() - }) + expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); + expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); + }); it('should clear crews data when check all option', async () => { - const { getByRole, queryByRole } = setup() + const { getByRole, queryByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - const listBox = within(getByRole('listbox')) - const allOption = listBox.getByRole('option', { name: 'All' }) + const listBox = within(getByRole('listbox')); + const allOption = listBox.getByRole('option', { name: 'All' }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument() - expect(queryByRole('button', { name: mockOptions[1] })).not.toBeInTheDocument() + expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); + expect(queryByRole('button', { name: mockOptions[1] })).not.toBeInTheDocument(); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(queryByRole('button', { name: mockOptions[0] })).toBeInTheDocument() - expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument() - }, 50000) + expect(queryByRole('button', { name: mockOptions[0] })).toBeInTheDocument(); + expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); + }, 50000); it('should show radio group when render Crews component', async () => { - const { getByRole, getByText } = setup() + const { getByRole, getByText } = setup(); - expect(getByText(assigneeFilterLabels[0])).toBeInTheDocument() - expect(getByText(assigneeFilterLabels[1])).toBeInTheDocument() - expect(getByRole('radiogroup', { name: 'assigneeFilter' })).toBeVisible() - }) + expect(getByText(assigneeFilterLabels[0])).toBeInTheDocument(); + expect(getByText(assigneeFilterLabels[1])).toBeInTheDocument(); + expect(getByRole('radiogroup', { name: 'assigneeFilter' })).toBeVisible(); + }); it('should show radio group with init value when render Crews component', async () => { - const { getAllByRole } = setup() + const { getAllByRole } = setup(); - const radioGroups = getAllByRole('radio') - const optionValues = radioGroups.map((option) => option.getAttribute('value')) - const checkedValues = radioGroups.map((option) => option.getAttribute('checked')) + const radioGroups = getAllByRole('radio'); + const optionValues = radioGroups.map((option) => option.getAttribute('value')); + const checkedValues = radioGroups.map((option) => option.getAttribute('checked')); - const expectedCheckedValues = ['', null] - expect(optionValues).toEqual(assigneeFilterValues) - expect(checkedValues).toEqual(expectedCheckedValues) - }) + const expectedCheckedValues = ['', null]; + expect(optionValues).toEqual(assigneeFilterValues); + expect(checkedValues).toEqual(expectedCheckedValues); + }); it('should call update function when change radio option', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('radio', { name: assigneeFilterLabels[1] })) - }) + await userEvent.click(getByRole('radio', { name: assigneeFilterLabels[1] })); + }); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(2) - expect(mockedUseAppDispatch).toHaveBeenCalledWith(updateAssigneeFilter(assigneeFilterValues[1])) - }) - }) -}) + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(2); + expect(mockedUseAppDispatch).toHaveBeenCalledWith(updateAssigneeFilter(assigneeFilterValues[1])); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx index a336abc3b1..4eee7a15c2 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx @@ -1,14 +1,14 @@ -import { act, render, waitFor, within } from '@testing-library/react' -import { CycleTime } from '@src/components/Metrics/MetricsStep/CycleTime' -import userEvent from '@testing-library/user-event' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' -import { CYCLE_TIME_SETTINGS, ERROR_MESSAGE_TIME_DURATION, LIST_OPEN, NO_RESULT_DASH } from '../../../fixtures' -import { saveDoneColumn, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice' +import { act, render, waitFor, within } from '@testing-library/react'; +import { CycleTime } from '@src/components/Metrics/MetricsStep/CycleTime'; +import userEvent from '@testing-library/user-event'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { CYCLE_TIME_SETTINGS, ERROR_MESSAGE_TIME_DURATION, LIST_OPEN, NO_RESULT_DASH } from '../../../fixtures'; +import { saveDoneColumn, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice'; -const FlagAsBlock = 'Consider the "Flag" as "Block"' +const FlagAsBlock = 'Consider the "Flag" as "Block"'; -let store = setupStore() +let store = setupStore(); jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), selectMetricsContent: jest.fn().mockReturnValue({ @@ -29,7 +29,7 @@ jest.mock('@src/context/Metrics/metricsSlice', () => ({ }), selectTreatFlagCardAsBlock: jest.fn().mockReturnValue(true), selectCycleTimeWarningMessage: jest.fn().mockReturnValue('Test warning Message'), -})) +})); jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), selectJiraColumns: jest.fn().mockReturnValue([ @@ -37,77 +37,77 @@ jest.mock('@src/context/config/configSlice', () => ({ { key: 'Testing', value: { name: 'Testing', statuses: ['Test'] } }, { key: 'TODO', value: { name: 'TODO', statuses: ['To do'] } }, ]), -})) +})); -const mockedUseAppDispatch = jest.fn() +const mockedUseAppDispatch = jest.fn(); jest.mock('@src/hooks/useAppDispatch', () => ({ useAppDispatch: () => mockedUseAppDispatch, -})) +})); const setup = () => render( - ) + ); describe('CycleTime', () => { beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); describe('CycleTime Title', () => { it('should show Cycle Time title when render Crews component', () => { - const { getByText } = setup() - expect(getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument() - }) + const { getByText } = setup(); + expect(getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument(); + }); it('should show Cycle Time tooltip when render Crews component', () => { - const { getByTestId } = setup() - expect(getByTestId('InfoOutlinedIcon')).toBeInTheDocument() - }) - }) + const { getByTestId } = setup(); + expect(getByTestId('InfoOutlinedIcon')).toBeInTheDocument(); + }); + }); describe('CycleTime Selector List', () => { it('should show selectors title when render Crews component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Analysis, In Dev, doing')).toBeInTheDocument() - expect(getByText('Test')).toBeInTheDocument() - expect(getByText('To do')).toBeInTheDocument() - }) + expect(getByText('Analysis, In Dev, doing')).toBeInTheDocument(); + expect(getByText('Test')).toBeInTheDocument(); + expect(getByText('To do')).toBeInTheDocument(); + }); it('should always show board status column tooltip', async () => { - const { getByText, getByRole } = setup() - userEvent.hover(getByText('Analysis, In Dev, doing')) + const { getByText, getByRole } = setup(); + userEvent.hover(getByText('Analysis, In Dev, doing')); await waitFor(() => { - expect(getByRole('tooltip', { name: 'Analysis, In Dev, doing' })).toBeVisible() - }) - }) + expect(getByRole('tooltip', { name: 'Analysis, In Dev, doing' })).toBeVisible(); + }); + }); it('should show right input value when initializing', async () => { - const { getAllByRole } = setup() - const inputElements = getAllByRole('combobox') - const selectedInputValues = inputElements.map((input) => input.getAttribute('value')) + const { getAllByRole } = setup(); + const inputElements = getAllByRole('combobox'); + const selectedInputValues = inputElements.map((input) => input.getAttribute('value')); - const expectedInputValues = ['Analysis', 'Review', NO_RESULT_DASH] + const expectedInputValues = ['Analysis', 'Review', NO_RESULT_DASH]; - expect(selectedInputValues).toEqual(expectedInputValues) - }) + expect(selectedInputValues).toEqual(expectedInputValues); + }); it('should show detail options when click included button', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(columnsArray[0]) - }) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionText = options.map((option) => option.textContent) + await userEvent.click(columnsArray[0]); + }); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionText = options.map((option) => option.textContent); const expectedOptions = [ NO_RESULT_DASH, @@ -119,162 +119,162 @@ describe('CycleTime', () => { 'Testing', 'Review', 'Done', - ] + ]; expectedOptions.forEach((expectedOption) => { - expect(optionText).toContain(expectedOption) - }) - }) + expect(optionText).toContain(expectedOption); + }); + }); it('should show the right options when input the keyword to search', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.type(columnsArray[0], 'Done') - }) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionTexts = options.map((option) => option.textContent) + await userEvent.type(columnsArray[0], 'Done'); + }); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionTexts = options.map((option) => option.textContent); - const expectedOptions = ['Done'] + const expectedOptions = ['Done']; - expect(optionTexts).toEqual(expectedOptions) - }) + expect(optionTexts).toEqual(expectedOptions); + }); it('should show no options when enter the wrong keyword', async () => { - const { getAllByRole, getByText } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByText } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.type(columnsArray[0], 'wrong keyword') - }) + await userEvent.type(columnsArray[0], 'wrong keyword'); + }); - expect(getByText('No options')).toBeInTheDocument() - }) + expect(getByText('No options')).toBeInTheDocument(); + }); it('should show selected option when click the dropDown button ', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(columnsArray[2]) - }) + await userEvent.click(columnsArray[2]); + }); - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const selectedOption = options.find((option) => option.getAttribute('aria-selected') === 'true') + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const selectedOption = options.find((option) => option.getAttribute('aria-selected') === 'true'); - const selectedOptionText = selectedOption?.textContent + const selectedOptionText = selectedOption?.textContent; - expect(selectedOptionText).toBe(NO_RESULT_DASH) - }) + expect(selectedOptionText).toBe(NO_RESULT_DASH); + }); it('should show other selections when change option and will not affect Real done', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(columnsArray[2]) - }) + await userEvent.click(columnsArray[2]); + }); - const listBox = within(getByRole('listbox')) - const mockOptions = listBox.getAllByRole('option') + const listBox = within(getByRole('listbox')); + const mockOptions = listBox.getAllByRole('option'); await act(async () => { - await userEvent.click(mockOptions[1]) - }) + await userEvent.click(mockOptions[1]); + }); - const inputElements = getAllByRole('combobox') - const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[2] + const inputElements = getAllByRole('combobox'); + const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[2]; - expect(selectedInputValue).toBe('To do') - await waitFor(() => expect(mockedUseAppDispatch).not.toHaveBeenCalledWith(saveDoneColumn([]))) - }) + expect(selectedInputValue).toBe('To do'); + await waitFor(() => expect(mockedUseAppDispatch).not.toHaveBeenCalledWith(saveDoneColumn([]))); + }); it('should reset Real done when marked as done from other options', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(columnsArray[0]) - }) + await userEvent.click(columnsArray[0]); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getAllByRole('option')[8]) - }) + await userEvent.click(listBox.getAllByRole('option')[8]); + }); - const inputElements = getAllByRole('combobox') + const inputElements = getAllByRole('combobox'); - const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0] + const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0]; - expect(selectedInputValue).toBe('Done') - await waitFor(() => expect(mockedUseAppDispatch).toHaveBeenCalledWith(saveDoneColumn([]))) - }) + expect(selectedInputValue).toBe('Done'); + await waitFor(() => expect(mockedUseAppDispatch).toHaveBeenCalledWith(saveDoneColumn([]))); + }); it('should show the right selected value when cancel the done', async () => { - const { getAllByRole, getByRole } = setup() - const columnsArray = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const columnsArray = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(columnsArray[0]) - }) + await userEvent.click(columnsArray[0]); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getAllByRole('option')[8]) - }) + await userEvent.click(listBox.getAllByRole('option')[8]); + }); await act(async () => { - await userEvent.click(columnsArray[0]) - }) + await userEvent.click(columnsArray[0]); + }); - const newListBox = within(getByRole('listbox')) + const newListBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(newListBox.getAllByRole('option')[7]) - }) + await userEvent.click(newListBox.getAllByRole('option')[7]); + }); - const inputElements = getAllByRole('combobox') - const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0] + const inputElements = getAllByRole('combobox'); + const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0]; - expect(selectedInputValue).toBe('Review') - await waitFor(() => expect(mockedUseAppDispatch).toHaveBeenCalledWith(saveDoneColumn([]))) - }) - }) + expect(selectedInputValue).toBe('Review'); + await waitFor(() => expect(mockedUseAppDispatch).toHaveBeenCalledWith(saveDoneColumn([]))); + }); + }); describe('CycleTime Flag as Block', () => { it('should show FlagAsBlock when render Crews component', () => { - const { getByText } = setup() - expect(getByText(FlagAsBlock)).toBeInTheDocument() - }) + const { getByText } = setup(); + expect(getByText(FlagAsBlock)).toBeInTheDocument(); + }); it('should be checked by default when initializing', () => { - const { getByRole } = setup() - expect(getByRole('checkbox')).toHaveProperty('checked', true) - }) + const { getByRole } = setup(); + expect(getByRole('checkbox')).toHaveProperty('checked', true); + }); it('should change checked when click', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('checkbox')) - }) + await userEvent.click(getByRole('checkbox')); + }); await waitFor(() => { - expect(mockedUseAppDispatch).toHaveBeenCalledWith(updateTreatFlagCardAsBlock(false)) - }) - }) - }) + expect(mockedUseAppDispatch).toHaveBeenCalledWith(updateTreatFlagCardAsBlock(false)); + }); + }); + }); it('should show warning message when selectWarningMessage has a value in cycleTime component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Test warning Message')).toBeVisible() - }) + expect(getByText('Test warning Message')).toBeVisible(); + }); it('should show disable warning message when selectWarningMessage has a value after two seconds in cycleTime component', async () => { - jest.useFakeTimers() - const { queryByText } = setup() + jest.useFakeTimers(); + const { queryByText } = setup(); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/DeploymentFrequencySettings.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/DeploymentFrequencySettings.test.tsx index 69ef47dad3..98dd1d32a0 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/DeploymentFrequencySettings.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/DeploymentFrequencySettings.test.tsx @@ -1,19 +1,19 @@ -import { render, within, screen } from '@testing-library/react' -import { Provider } from 'react-redux' -import { store } from '@src/store' -import userEvent from '@testing-library/user-event' -import { DeploymentFrequencySettings } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings' +import { render, within, screen } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { store } from '@src/store'; +import userEvent from '@testing-library/user-event'; +import { DeploymentFrequencySettings } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings'; import { addADeploymentFrequencySetting, deleteADeploymentFrequencySetting, updateDeploymentFrequencySettings, -} from '@src/context/Metrics/metricsSlice' -import { DEPLOYMENT_FREQUENCY_SETTINGS, LIST_OPEN, ORGANIZATION, REMOVE_BUTTON } from '../../../../fixtures' +} from '@src/context/Metrics/metricsSlice'; +import { DEPLOYMENT_FREQUENCY_SETTINGS, LIST_OPEN, ORGANIZATION, REMOVE_BUTTON } from '../../../../fixtures'; jest.mock('@src/hooks', () => ({ ...jest.requireActual('@src/hooks'), useAppDispatch: () => jest.fn(), -})) +})); jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), @@ -28,7 +28,7 @@ jest.mock('@src/context/Metrics/metricsSlice', () => ({ selectPipelineNameWarningMessage: jest.fn().mockReturnValue(null), selectStepWarningMessage: jest.fn().mockReturnValue(null), selectMetricsContent: jest.fn().mockReturnValue({ pipelineCrews: [], users: [] }), -})) +})); jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), @@ -37,16 +37,16 @@ jest.mock('@src/context/config/configSlice', () => ({ selectSteps: jest.fn().mockReturnValue(['']), selectBranches: jest.fn().mockReturnValue(['']), selectPipelineCrews: jest.fn().mockReturnValue(['']), -})) +})); const mockValidationCheckContext = { isPipelineValid: jest.fn().mockReturnValue(true), getDuplicatedPipeLineIds: jest.fn().mockReturnValue([]), -} +}; jest.mock('@src/hooks/useMetricsStepValidationCheckContext', () => ({ useMetricsStepValidationCheckContext: () => mockValidationCheckContext, -})) +})); describe('DeploymentFrequencySettings', () => { const setup = () => @@ -54,38 +54,38 @@ describe('DeploymentFrequencySettings', () => { - ) + ); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should render DeploymentFrequencySettings component', () => { - const { getByText, getAllByText } = setup() + const { getByText, getAllByText } = setup(); - expect(getByText(DEPLOYMENT_FREQUENCY_SETTINGS)).toBeInTheDocument() - expect(getAllByText(ORGANIZATION).length).toBe(2) - }) + expect(getByText(DEPLOYMENT_FREQUENCY_SETTINGS)).toBeInTheDocument(); + expect(getAllByText(ORGANIZATION).length).toBe(2); + }); it('should call addADeploymentFrequencySetting function when click add another pipeline button', async () => { - setup() - await userEvent.click(screen.getByTestId('AddIcon')) + setup(); + await userEvent.click(screen.getByTestId('AddIcon')); - expect(addADeploymentFrequencySetting).toHaveBeenCalledTimes(1) - }) + expect(addADeploymentFrequencySetting).toHaveBeenCalledTimes(1); + }); it('should call deleteADeploymentFrequencySetting function when click remove pipeline button', async () => { - setup() - await userEvent.click(screen.getAllByRole('button', { name: REMOVE_BUTTON })[0]) + setup(); + await userEvent.click(screen.getAllByRole('button', { name: REMOVE_BUTTON })[0]); - expect(deleteADeploymentFrequencySetting).toHaveBeenCalledTimes(1) - }) + expect(deleteADeploymentFrequencySetting).toHaveBeenCalledTimes(1); + }); it('should call updateDeploymentFrequencySetting function and clearErrorMessages function when select organization', async () => { - const { getByRole } = setup() - await userEvent.click(screen.getAllByRole('button', { name: LIST_OPEN })[0]) - const listBox = within(getByRole('listbox')) - await userEvent.click(listBox.getByText('mockOrgName')) + const { getByRole } = setup(); + await userEvent.click(screen.getAllByRole('button', { name: LIST_OPEN })[0]); + const listBox = within(getByRole('listbox')); + await userEvent.click(listBox.getByText('mockOrgName')); - expect(updateDeploymentFrequencySettings).toHaveBeenCalledTimes(1) - }) -}) + expect(updateDeploymentFrequencySettings).toHaveBeenCalledTimes(1); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx index ce6851b943..9f07fcc210 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx @@ -1,10 +1,10 @@ -import { act, render, waitFor, within } from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import { Provider } from 'react-redux' -import { setupStore } from '../../../../utils/setupStoreUtil' -import { PipelineMetricSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection' -import { metricsClient } from '@src/clients/MetricsClient' -import { updatePipelineToolVerifyResponseSteps } from '@src/context/config/configSlice' +import { act, render, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../../utils/setupStoreUtil'; +import { PipelineMetricSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection'; +import { metricsClient } from '@src/clients/MetricsClient'; +import { updatePipelineToolVerifyResponseSteps } from '@src/context/config/configSlice'; import { BRANCH, ERROR_MESSAGE_TIME_DURATION, @@ -14,8 +14,8 @@ import { PIPELINE_SETTING_TYPES, REMOVE_BUTTON, STEP, -} from '../../../../fixtures' -import { PipelineSetting } from '@src/context/interface' +} from '../../../../fixtures'; +import { PipelineSetting } from '@src/context/interface'; jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), @@ -23,7 +23,7 @@ jest.mock('@src/context/Metrics/metricsSlice', () => ({ selectOrganizationWarningMessage: jest.fn().mockReturnValue('Test organization warning message'), selectPipelineNameWarningMessage: jest.fn().mockReturnValue('Test pipelineName warning message'), selectStepWarningMessage: jest.fn().mockReturnValue('Test step warning message'), -})) +})); jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), @@ -47,26 +47,26 @@ jest.mock('@src/context/config/configSlice', () => ({ updatePipelineToolVerifyResponseSteps: jest .fn() .mockReturnValue({ type: 'UPDATE_PIPELINE_TOOL_VERIFY_RESPONSE_STEPS' }), -})) +})); describe('PipelineMetricSelection', () => { - const mockId = 0 + const mockId = 0; const deploymentFrequencySetting = { id: 0, organization: '', pipelineName: '', step: '', branches: [], - } - const mockHandleClickRemoveButton = jest.fn() - const mockUpdatePipeline = jest.fn() + }; + const mockHandleClickRemoveButton = jest.fn(); + const mockUpdatePipeline = jest.fn(); const setup = async ( deploymentFrequencySetting: PipelineSetting, isShowRemoveButton: boolean, isDuplicated: boolean ) => { - const store = setupStore() + const store = setupStore(); return render( { isDuplicated={isDuplicated} /> - ) - } + ); + }; beforeEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should render PipelineMetricSelection when isShowRemoveButton is true', async () => { - const { getByText } = await setup(deploymentFrequencySetting, true, false) + const { getByText } = await setup(deploymentFrequencySetting, true, false); - expect(getByText(REMOVE_BUTTON)).toBeInTheDocument() - expect(getByText(ORGANIZATION)).toBeInTheDocument() - }) + expect(getByText(REMOVE_BUTTON)).toBeInTheDocument(); + expect(getByText(ORGANIZATION)).toBeInTheDocument(); + }); it('should render PipelineMetricSelection when isShowRemoveButton is false', async () => { - const { getByText, queryByText } = await setup(deploymentFrequencySetting, false, false) + const { getByText, queryByText } = await setup(deploymentFrequencySetting, false, false); - expect(queryByText(REMOVE_BUTTON)).not.toBeInTheDocument() - expect(getByText(ORGANIZATION)).toBeInTheDocument() - }) + expect(queryByText(REMOVE_BUTTON)).not.toBeInTheDocument(); + expect(getByText(ORGANIZATION)).toBeInTheDocument(); + }); it('should call deleteADeploymentFrequencySetting function when click remove this pipeline button', async () => { - const { getByRole } = await setup(deploymentFrequencySetting, true, false) + const { getByRole } = await setup(deploymentFrequencySetting, true, false); await act(async () => { - await userEvent.click(getByRole('button', { name: REMOVE_BUTTON })) - }) + await userEvent.click(getByRole('button', { name: REMOVE_BUTTON })); + }); - expect(mockHandleClickRemoveButton).toHaveBeenCalledTimes(1) - expect(mockHandleClickRemoveButton).toHaveBeenCalledWith(mockId) - }) + expect(mockHandleClickRemoveButton).toHaveBeenCalledTimes(1); + expect(mockHandleClickRemoveButton).toHaveBeenCalledWith(mockId); + }); it('should show pipelineName selection when select organization', async () => { - const { getByText } = await setup({ ...deploymentFrequencySetting, organization: 'mockOrgName' }, false, false) + const { getByText } = await setup({ ...deploymentFrequencySetting, organization: 'mockOrgName' }, false, false); - expect(getByText(ORGANIZATION)).toBeInTheDocument() - expect(getByText(PIPELINE_NAME)).toBeInTheDocument() - }) + expect(getByText(ORGANIZATION)).toBeInTheDocument(); + expect(getByText(PIPELINE_NAME)).toBeInTheDocument(); + }); it('should show step selection when select organization and pipelineName', async () => { - metricsClient.getSteps = jest.fn().mockImplementation(() => ['steps1', 'steps2']) + metricsClient.getSteps = jest.fn().mockImplementation(() => ['steps1', 'steps2']); const { getByText } = await setup( { ...deploymentFrequencySetting, organization: 'mockOrgName', pipelineName: 'mockName' }, false, false - ) + ); - expect(getByText(ORGANIZATION)).toBeInTheDocument() - expect(getByText(PIPELINE_NAME)).toBeInTheDocument() - expect(getByText(BRANCH)).toBeInTheDocument() - expect(getByText(STEP)).toBeInTheDocument() - }) + expect(getByText(ORGANIZATION)).toBeInTheDocument(); + expect(getByText(PIPELINE_NAME)).toBeInTheDocument(); + expect(getByText(BRANCH)).toBeInTheDocument(); + expect(getByText(STEP)).toBeInTheDocument(); + }); it('should show error message pop when getSteps failed', async () => { metricsClient.getSteps = jest.fn().mockImplementation(() => { - throw new Error('error message') - }) + throw new Error('error message'); + }); const { getByText, getByRole, getAllByRole } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] }, false, false - ) + ); await act(async () => { - await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]) - }) + await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByText('mockName2')) - }) + await userEvent.click(listBox.getByText('mockName2')); + }); await waitFor(() => { - expect(getByText('BuildKite get steps failed: error message')).toBeInTheDocument() - }) - expect(mockUpdatePipeline).toHaveBeenCalledTimes(2) - }) + expect(getByText('BuildKite get steps failed: error message')).toBeInTheDocument(); + }); + expect(mockUpdatePipeline).toHaveBeenCalledTimes(2); + }); it('should show no steps warning message when getSteps succeed but get no steps', async () => { - metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false }) + metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false }); const { getByText, getByRole, getAllByRole } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] }, false, false - ) + ); await act(async () => { - await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]) - }) + await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByText('mockName2')) - }) + await userEvent.click(listBox.getByText('mockName2')); + }); await waitFor(() => { expect( getByText( 'There is no step during this period for this pipeline! Please change the search time in the Config page!' ) - ).toBeInTheDocument() - }) - }) + ).toBeInTheDocument(); + }); + }); it('should show no steps warning message when getSteps succeed but get no steps and isShowRemoveButton is true', async () => { - metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false }) + metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false }); const { getByRole, getAllByRole } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] }, true, false - ) + ); await act(async () => { - await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]) - }) + await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByText('mockName2')) - }) + await userEvent.click(listBox.getByText('mockName2')); + }); await waitFor(() => { - expect(mockHandleClickRemoveButton).toHaveBeenCalledTimes(2) - }) - }) + expect(mockHandleClickRemoveButton).toHaveBeenCalledTimes(2); + }); + }); it('should show steps selection when getSteps succeed ', async () => { - metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true }) + metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true }); const { getByRole, getByText, getAllByRole } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] }, false, false - ) + ); await waitFor(() => { - expect(updatePipelineToolVerifyResponseSteps).toHaveBeenCalledTimes(1) - expect(getByText(STEP)).toBeInTheDocument() - }) + expect(updatePipelineToolVerifyResponseSteps).toHaveBeenCalledTimes(1); + expect(getByText(STEP)).toBeInTheDocument(); + }); await act(async () => { - await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[2]) - }) + await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[2]); + }); - const stepsListBox = within(getByRole('listbox')) + const stepsListBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(stepsListBox.getByText('step2')) - }) + await userEvent.click(stepsListBox.getByText('step2')); + }); - expect(mockUpdatePipeline).toHaveBeenCalledTimes(1) - }) + expect(mockUpdatePipeline).toHaveBeenCalledTimes(1); + }); it('should show branches selection when getSteps succeed ', async () => { metricsClient.getSteps = jest .fn() - .mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'] }) + .mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'] }); const { getByRole, getByText } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: ['branch1', 'branch2'] }, false, false - ) + ); await waitFor(() => { - expect(updatePipelineToolVerifyResponseSteps).toHaveBeenCalledTimes(1) - expect(getByText(BRANCH)).toBeInTheDocument() - }) + expect(updatePipelineToolVerifyResponseSteps).toHaveBeenCalledTimes(1); + expect(getByText(BRANCH)).toBeInTheDocument(); + }); await act(async () => { - await userEvent.click(getByRole('combobox', { name: 'Branches' })) - }) + await userEvent.click(getByRole('combobox', { name: 'Branches' })); + }); - const branchesListBox = within(getByRole('listbox')) - const allOption = branchesListBox.getByRole('option', { name: 'All' }) + const branchesListBox = within(getByRole('listbox')); + const allOption = branchesListBox.getByRole('option', { name: 'All' }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(getByRole('button', { name: 'branch1' })).toBeInTheDocument() - expect(getByRole('button', { name: 'branch2' })).toBeInTheDocument() + expect(getByRole('button', { name: 'branch1' })).toBeInTheDocument(); + expect(getByRole('button', { name: 'branch2' })).toBeInTheDocument(); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(getByRole('button', { name: 'branch1' })).toBeInTheDocument() - expect(getByRole('button', { name: 'branch2' })).toBeInTheDocument() + expect(getByRole('button', { name: 'branch1' })).toBeInTheDocument(); + expect(getByRole('button', { name: 'branch2' })).toBeInTheDocument(); - expect(mockUpdatePipeline).toHaveBeenCalledTimes(2) - }) + expect(mockUpdatePipeline).toHaveBeenCalledTimes(2); + }); it('should show duplicated message given duplicated id', async () => { - metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true }) + metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true }); const { getByText } = await setup( { id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: 'step1', branches: [] }, false, true - ) + ); - expect(getByText('This pipeline is the same as another one!')).toBeInTheDocument() - }) + expect(getByText('This pipeline is the same as another one!')).toBeInTheDocument(); + }); it('should show warning message when organization and pipelineName warning messages have value', async () => { - const { getByText } = await setup(deploymentFrequencySetting, false, false) + const { getByText } = await setup(deploymentFrequencySetting, false, false); - expect(getByText('Test organization warning message')).toBeInTheDocument() - expect(getByText('Test pipelineName warning message')).toBeInTheDocument() - expect(getByText('Test step warning message')).toBeInTheDocument() - }) + expect(getByText('Test organization warning message')).toBeInTheDocument(); + expect(getByText('Test pipelineName warning message')).toBeInTheDocument(); + expect(getByText('Test step warning message')).toBeInTheDocument(); + }); it('should clear warning message when organization and pipelineName warning messages have value after four seconds', async () => { - jest.useFakeTimers() - const { queryByText } = await setup(deploymentFrequencySetting, false, false) + jest.useFakeTimers(); + const { queryByText } = await setup(deploymentFrequencySetting, false, false); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText('Test organization warning message')).not.toBeInTheDocument() - expect(queryByText('Test pipelineName warning message')).not.toBeInTheDocument() - expect(queryByText('Test step warning message')).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText('Test organization warning message')).not.toBeInTheDocument(); + expect(queryByText('Test pipelineName warning message')).not.toBeInTheDocument(); + expect(queryByText('Test step warning message')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection.test.tsx index 41b4a5e736..144c8d4d7b 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection.test.tsx @@ -1,38 +1,38 @@ -import { act, render, within } from '@testing-library/react' -import { SingleSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection' -import userEvent from '@testing-library/user-event' -import { Provider } from 'react-redux' -import { setupStore } from '../../../../utils/setupStoreUtil' -import { LIST_OPEN } from '../../../../fixtures' +import { act, render, within } from '@testing-library/react'; +import { SingleSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection'; +import userEvent from '@testing-library/user-event'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../../utils/setupStoreUtil'; +import { LIST_OPEN } from '../../../../fixtures'; const mockValidationCheckContext = { checkDuplicatedPipeLine: jest.fn(), checkPipelineValidation: jest.fn(), -} +}; jest.mock('@src/hooks/useMetricsStepValidationCheckContext', () => ({ useMetricsStepValidationCheckContext: () => mockValidationCheckContext, -})) +})); jest.mock('react', () => ({ ...jest.requireActual('react'), useEffect: jest.fn(), -})) -let store = setupStore() +})); +let store = setupStore(); describe('SingleSelection', () => { - const mockOptions = ['mockOptions 1', 'mockOptions 2', 'mockOptions 3'] - const mockLabel = 'mockLabel' - const mockValue = 'mockOptions 1' - const mockOnGetSteps = jest.fn() - const mockUpdatePipeline = jest.fn() + const mockOptions = ['mockOptions 1', 'mockOptions 2', 'mockOptions 3']; + const mockLabel = 'mockLabel'; + const mockValue = 'mockOptions 1'; + const mockOnGetSteps = jest.fn(); + const mockUpdatePipeline = jest.fn(); beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); const setup = () => render( @@ -46,71 +46,71 @@ describe('SingleSelection', () => { onUpDatePipeline={mockUpdatePipeline} /> - ) + ); it('should show selected label and value when render a SingleSelection', () => { - const { getByText, getAllByRole } = setup() - const inputElements = getAllByRole('combobox') + const { getByText, getAllByRole } = setup(); + const inputElements = getAllByRole('combobox'); - const selectedInputValues = inputElements.map((input) => input.getAttribute('value')) + const selectedInputValues = inputElements.map((input) => input.getAttribute('value')); - expect(getByText(mockLabel)).toBeInTheDocument() - expect(selectedInputValues).toEqual([mockValue]) - }) + expect(getByText(mockLabel)).toBeInTheDocument(); + expect(selectedInputValues).toEqual([mockValue]); + }); it('should show detail options when click the dropdown button', async () => { - const { getAllByRole, getByRole } = setup() - const buttonElements = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const buttonElements = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.click(buttonElements[0]) - }) - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionText = options.map((option) => option.textContent) + await userEvent.click(buttonElements[0]); + }); + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionText = options.map((option) => option.textContent); - expect(optionText).toEqual(mockOptions) - }) + expect(optionText).toEqual(mockOptions); + }); it('should show the right options when search the keyword', async () => { - const { getAllByRole, getByRole } = setup() - const buttonElements = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByRole } = setup(); + const buttonElements = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.type(buttonElements[0], '1') - }) + await userEvent.type(buttonElements[0], '1'); + }); - const listBox = within(getByRole('listbox')) - const options = listBox.getAllByRole('option') - const optionTexts = options.map((option) => option.textContent) + const listBox = within(getByRole('listbox')); + const options = listBox.getAllByRole('option'); + const optionTexts = options.map((option) => option.textContent); - const expectedOptions = ['mockOptions 1'] + const expectedOptions = ['mockOptions 1']; - expect(optionTexts).toEqual(expectedOptions) - }) + expect(optionTexts).toEqual(expectedOptions); + }); it('should show no options when search the wrong keyword', async () => { - const { getAllByRole, getByText } = setup() - const buttonElements = getAllByRole('button', { name: LIST_OPEN }) + const { getAllByRole, getByText } = setup(); + const buttonElements = getAllByRole('button', { name: LIST_OPEN }); await act(async () => { - await userEvent.type(buttonElements[0], 'wrong keyword') - }) + await userEvent.type(buttonElements[0], 'wrong keyword'); + }); - expect(getByText('No options')).toBeInTheDocument() - }) + expect(getByText('No options')).toBeInTheDocument(); + }); it('should call update option function and OnGetSteps function when change option given mockValue as default', async () => { - const { getByText, getByRole } = setup() + const { getByText, getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('button', { name: LIST_OPEN })) - }) + await userEvent.click(getByRole('button', { name: LIST_OPEN })); + }); await act(async () => { - await userEvent.click(getByText(mockOptions[1])) - }) + await userEvent.click(getByText(mockOptions[1])); + }); - expect(mockOnGetSteps).toHaveBeenCalledTimes(1) - expect(mockUpdatePipeline).toHaveBeenCalledTimes(2) - }) -}) + expect(mockOnGetSteps).toHaveBeenCalledTimes(1); + expect(mockUpdatePipeline).toHaveBeenCalledTimes(2); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/MetricsStep.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/MetricsStep.test.tsx index abb74a2fac..11e5f13ebf 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/MetricsStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/MetricsStep.test.tsx @@ -1,9 +1,9 @@ -import { act, render, renderHook, waitFor, within } from '@testing-library/react' -import MetricsStep from '@src/components/Metrics/MetricsStep' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' +import { act, render, renderHook, waitFor, within } from '@testing-library/react'; +import MetricsStep from '@src/components/Metrics/MetricsStep'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; -import { updateJiraVerifyResponse, updateMetrics } from '@src/context/config/configSlice' +import { updateJiraVerifyResponse, updateMetrics } from '@src/context/config/configSlice'; import { CLASSIFICATION_SETTING, CREWS_SETTING, @@ -16,74 +16,74 @@ import { REAL_DONE_SETTING_SECTION, REQUIRED_DATA_LIST, SELECT_CONSIDER_AS_DONE_MESSAGE, -} from '../../../fixtures' -import { saveCycleTimeSettings, saveDoneColumn } from '@src/context/Metrics/metricsSlice' -import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect' -import userEvent from '@testing-library/user-event' +} from '../../../fixtures'; +import { saveCycleTimeSettings, saveDoneColumn } from '@src/context/Metrics/metricsSlice'; +import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect'; +import userEvent from '@testing-library/user-event'; -let store = setupStore() +let store = setupStore(); const setup = () => render( - ) + ); describe('MetricsStep', () => { beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); - const { result } = renderHook(() => useNotificationLayoutEffect()) + const { result } = renderHook(() => useNotificationLayoutEffect()); it('should render Crews when select velocity, and show Real done when have done column in Cycle time', async () => { - store.dispatch(updateMetrics([REQUIRED_DATA_LIST[1]])) - const { getByText, queryByText } = setup() + store.dispatch(updateMetrics([REQUIRED_DATA_LIST[1]])); + const { getByText, queryByText } = setup(); - expect(getByText(CREWS_SETTING)).toBeInTheDocument() - expect(queryByText(CYCLE_TIME_SETTINGS)).not.toBeInTheDocument() - expect(queryByText(CLASSIFICATION_SETTING)).not.toBeInTheDocument() + expect(getByText(CREWS_SETTING)).toBeInTheDocument(); + expect(queryByText(CYCLE_TIME_SETTINGS)).not.toBeInTheDocument(); + expect(queryByText(CLASSIFICATION_SETTING)).not.toBeInTheDocument(); act(() => { - store.dispatch(saveCycleTimeSettings([{ name: 'Testing', value: 'Done' }])) - }) + store.dispatch(saveCycleTimeSettings([{ name: 'Testing', value: 'Done' }])); + }); - expect(getByText(REAL_DONE)).toBeInTheDocument() - }) + expect(getByText(REAL_DONE)).toBeInTheDocument(); + }); it('should show Cycle Time Settings when select cycle time in config page', async () => { - await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[2]])) - const { getByText } = setup() + await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[2]])); + const { getByText } = setup(); - expect(getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument() - }) + expect(getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument(); + }); it('should hide Real Done when no done column in cycleTime settings', async () => { - await store.dispatch(saveCycleTimeSettings([{ name: 'Testing', value: 'Block' }])) - const { queryByText } = setup() + await store.dispatch(saveCycleTimeSettings([{ name: 'Testing', value: 'Block' }])); + const { queryByText } = setup(); - expect(queryByText(REAL_DONE)).not.toBeInTheDocument() - }) + expect(queryByText(REAL_DONE)).not.toBeInTheDocument(); + }); it('should show Classification Setting when select classification in config page', async () => { - await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[3]])) - const { getByText } = setup() + await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[3]])); + const { getByText } = setup(); - expect(getByText(CLASSIFICATION_SETTING)).toBeInTheDocument() - }) + expect(getByText(CLASSIFICATION_SETTING)).toBeInTheDocument(); + }); it('should show DeploymentFrequencySettings component when select deployment frequency in config page', async () => { - await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[5]])) - const { getByText } = setup() + await store.dispatch(updateMetrics([REQUIRED_DATA_LIST[5]])); + const { getByText } = setup(); - expect(getByText(DEPLOYMENT_FREQUENCY_SETTINGS)).toBeInTheDocument() - }) + expect(getByText(DEPLOYMENT_FREQUENCY_SETTINGS)).toBeInTheDocument(); + }); it('should call resetProps when resetProps is not undefined', async () => { act(() => { - result.current.resetProps = jest.fn() - }) + result.current.resetProps = jest.fn(); + }); await waitFor(() => render( @@ -91,10 +91,10 @@ describe('MetricsStep', () => { ) - ) + ); - expect(result.current.resetProps).toBeCalled() - }) + expect(result.current.resetProps).toBeCalled(); + }); describe('with pre-filled cycle time data', () => { beforeEach(() => { @@ -119,77 +119,77 @@ describe('MetricsStep', () => { name: 'Done', value: 'Done', }, - ] - const doneColumn = ['IN PROGRESS', 'IN DEV', 'PRE-DONE', 'DONE', 'CANCLE'] + ]; + const doneColumn = ['IN PROGRESS', 'IN DEV', 'PRE-DONE', 'DONE', 'CANCLE']; const jiraColumns = [ { key: 'indeterminate', value: { name: 'To Do', statuses: ['BACKLOG', 'TO DO', 'GOING TO DO'] } }, { key: 'indeterminate', value: { name: 'In Progress', statuses: ['IN PROGRESS', 'IN DEV'] } }, { key: 'indeterminate', value: { name: 'Block', statuses: ['BLOCK'] } }, { key: 'indeterminate', value: { name: 'Test', statuses: ['TESTING', 'TO BE TESTED'] } }, { key: 'done', value: { name: 'Done', statuses: ['PRE-DONE,', 'DONE', 'CANCLE'] } }, - ] + ]; - store.dispatch(updateMetrics(REQUIRED_DATA_LIST)) - store.dispatch(saveCycleTimeSettings(cycleTimeSettingsWithTwoDoneValue)) - store.dispatch(saveDoneColumn(doneColumn)) + store.dispatch(updateMetrics(REQUIRED_DATA_LIST)); + store.dispatch(saveCycleTimeSettings(cycleTimeSettingsWithTwoDoneValue)); + store.dispatch(saveDoneColumn(doneColumn)); store.dispatch( updateJiraVerifyResponse({ jiraColumns, users: MOCK_JIRA_VERIFY_RESPONSE.users, }) - ) - }) + ); + }); it('should reset real done when change Cycle time settings DONE to other status', async () => { - const { getByLabelText, getByRole } = setup() - const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION) - const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION) + const { getByLabelText, getByRole } = setup(); + const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION); + const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION); - expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE) - const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }) + expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE); + const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }); - await userEvent.click(columnsArray[1]) + await userEvent.click(columnsArray[1]); - const options = within(getByRole('listbox')).getAllByRole('option') - await userEvent.click(options[1]) + const options = within(getByRole('listbox')).getAllByRole('option'); + await userEvent.click(options[1]); - await waitFor(() => expect(realDoneSettingSection).toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE)) - }) + await waitFor(() => expect(realDoneSettingSection).toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE)); + }); it('should reset real done when change Cycle time settings other status to DONE', async () => { - const { getByLabelText, getByRole } = setup() - const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION) - const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION) + const { getByLabelText, getByRole } = setup(); + const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION); + const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION); - expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE) - const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }) - await userEvent.click(columnsArray[2]) + expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE); + const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }); + await userEvent.click(columnsArray[2]); - const options = within(getByRole('listbox')).getAllByRole('option') - await userEvent.click(options[options.length - 1]) + const options = within(getByRole('listbox')).getAllByRole('option'); + await userEvent.click(options[options.length - 1]); - await waitFor(() => expect(realDoneSettingSection).toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE)) - }) + await waitFor(() => expect(realDoneSettingSection).toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE)); + }); it('should hide real done when change all Cycle time settings to other status', async () => { - const { getByLabelText, getByRole } = setup() - const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION) - const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION) + const { getByLabelText, getByRole } = setup(); + const cycleTimeSettingsSection = getByLabelText(CYCLE_TIME_SETTINGS_SECTION); + const realDoneSettingSection = getByLabelText(REAL_DONE_SETTING_SECTION); - expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE) - const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }) + expect(realDoneSettingSection).not.toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE); + const columnsArray = within(cycleTimeSettingsSection).getAllByRole('button', { name: LIST_OPEN }); - await userEvent.click(columnsArray[1]) + await userEvent.click(columnsArray[1]); - const options1 = within(getByRole('listbox')).getAllByRole('option') - await userEvent.click(options1[1]) + const options1 = within(getByRole('listbox')).getAllByRole('option'); + await userEvent.click(options1[1]); - await userEvent.click(columnsArray[4]) + await userEvent.click(columnsArray[4]); - const options2 = within(getByRole('listbox')).getAllByRole('option') - await userEvent.click(options2[1]) + const options2 = within(getByRole('listbox')).getAllByRole('option'); + await userEvent.click(options2[1]); - await waitFor(() => expect(realDoneSettingSection).not.toBeInTheDocument()) - }) - }) -}) + await waitFor(() => expect(realDoneSettingSection).not.toBeInTheDocument()); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx index 997ff09234..9a16a03aeb 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx @@ -1,18 +1,18 @@ -import React from 'react' -import { render } from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import MultiAutoComplete from '@src/components/Common/MultiAutoComplete' -import { act } from 'react-dom/test-utils' -import { ALL, AUTOCOMPLETE_SELECT_ACTION, MOCK_AUTOCOMPLETE_LIST } from '../../../fixtures' +import React from 'react'; +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; +import { act } from 'react-dom/test-utils'; +import { ALL, AUTOCOMPLETE_SELECT_ACTION, MOCK_AUTOCOMPLETE_LIST } from '../../../fixtures'; describe('MultiAutoComplete', () => { - const optionList = ['Option 1', 'Option 2', 'Option 3'] - const selectedOption = ['Option 1'] - const onChangeHandler = jest.fn() - const isSelectAll = false - const textFieldLabel = 'Select Options' - const isError = false - const testId = 'multi-auto-complete' + const optionList = ['Option 1', 'Option 2', 'Option 3']; + const selectedOption = ['Option 1']; + const onChangeHandler = jest.fn(); + const isSelectAll = false; + const textFieldLabel = 'Select Options'; + const isError = false; + const testId = 'multi-auto-complete'; const setup = () => render( { isError={isError} testId={testId} /> - ) + ); it('renders the component', () => { - const { getByTestId } = setup() + const { getByTestId } = setup(); - expect(getByTestId(testId)).toBeInTheDocument() - }) + expect(getByTestId(testId)).toBeInTheDocument(); + }); it('When passed selectedoption changed, the correct option would be displayed', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); - expect(getByRole('button', { name: 'Option 1' })).toBeVisible() - }) + expect(getByRole('button', { name: 'Option 1' })).toBeVisible(); + }); it('When user select All option, all options in drop box would be selected', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); - const inputField = getByRole('combobox') - await userEvent.click(inputField) - const allOption = getByRole('option', { name: 'All' }) + const inputField = getByRole('combobox'); + await userEvent.click(inputField); + const allOption = getByRole('option', { name: 'All' }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); expect(onChangeHandler).toHaveBeenCalledWith( expect.anything(), @@ -55,6 +55,6 @@ describe('MultiAutoComplete', () => { { option: ALL, } - ) - }) -}) + ); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/RealDone.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/RealDone.test.tsx index 71ebf0002f..9e91715176 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/RealDone.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/RealDone.test.tsx @@ -1,19 +1,19 @@ -import { act, render, waitFor, within } from '@testing-library/react' -import { RealDone } from '@src/components/Metrics/MetricsStep/RealDone' -import userEvent from '@testing-library/user-event' -import { setupStore } from '../../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import { saveCycleTimeSettings } from '@src/context/Metrics/metricsSlice' -import { ERROR_MESSAGE_TIME_DURATION } from '../../../fixtures' +import { act, render, waitFor, within } from '@testing-library/react'; +import { RealDone } from '@src/components/Metrics/MetricsStep/RealDone'; +import userEvent from '@testing-library/user-event'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import { saveCycleTimeSettings } from '@src/context/Metrics/metricsSlice'; +import { ERROR_MESSAGE_TIME_DURATION } from '../../../fixtures'; jest.mock('@src/context/Metrics/metricsSlice', () => ({ ...jest.requireActual('@src/context/Metrics/metricsSlice'), selectRealDoneWarningMessage: jest.fn().mockReturnValue('Test warning Message'), -})) +})); -const mockTitle = 'RealDone' -const mockLabel = 'Consider as Done' -let store = setupStore() +const mockTitle = 'RealDone'; +const mockLabel = 'Consider as Done'; +let store = setupStore(); describe('RealDone', () => { describe('when done column with more than one statuses', () => { @@ -25,125 +25,125 @@ describe('RealDone', () => { statuses: ['DONE', 'CANCELLED'], }, }, - ] + ]; const setup = () => render( - ) + ); beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should show RealDone when render RealDone component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(mockTitle)).toBeInTheDocument() - }) + expect(getByText(mockTitle)).toBeInTheDocument(); + }); it('should show consider as done when initializing', () => { - const { getByText } = setup() - const label = getByText(mockLabel) - const helperText = getByText('consider as Done') + const { getByText } = setup(); + const label = getByText(mockLabel); + const helperText = getByText('consider as Done'); - expect(label).toBeInTheDocument() - expect(helperText.tagName).toBe('STRONG') - }) + expect(label).toBeInTheDocument(); + expect(helperText.tagName).toBe('STRONG'); + }); it('should show detail options when click Consider as Done button', async () => { - const { getByRole } = setup() + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - const listBox = within(getByRole('listbox')) - expect(listBox.getByRole('option', { name: 'All' })).toBeInTheDocument() - expect(listBox.getByRole('option', { name: 'DONE' })).toBeInTheDocument() - expect(listBox.getByRole('option', { name: 'CANCELLED' })).toBeInTheDocument() - }) + const listBox = within(getByRole('listbox')); + expect(listBox.getByRole('option', { name: 'All' })).toBeInTheDocument(); + expect(listBox.getByRole('option', { name: 'DONE' })).toBeInTheDocument(); + expect(listBox.getByRole('option', { name: 'CANCELLED' })).toBeInTheDocument(); + }); it('should show other selections when cancel one option given default all selections in RealDone', async () => { - const { getByRole, queryByRole } = setup() + const { getByRole, queryByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - const listBox = within(getByRole('listbox')) + const listBox = within(getByRole('listbox')); await act(async () => { - await userEvent.click(listBox.getByRole('option', { name: mockColumnsList[0].value.statuses[0] })) - }) + await userEvent.click(listBox.getByRole('option', { name: mockColumnsList[0].value.statuses[0] })); + }); - expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[0] })).toBeInTheDocument() - expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[1] })).not.toBeInTheDocument() - }) + expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[0] })).toBeInTheDocument(); + expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[1] })).not.toBeInTheDocument(); + }); it('should clear RealDone data when check all option', async () => { - const { getByRole, queryByRole } = setup() + const { getByRole, queryByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); - const listBox = within(getByRole('listbox')) - const allOption = listBox.getByRole('option', { name: 'All' }) + const listBox = within(getByRole('listbox')); + const allOption = listBox.getByRole('option', { name: 'All' }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(getByRole('button', { name: mockColumnsList[0].value.statuses[0] })).toBeInTheDocument() - expect(getByRole('button', { name: mockColumnsList[0].value.statuses[1] })).toBeInTheDocument() + expect(getByRole('button', { name: mockColumnsList[0].value.statuses[0] })).toBeInTheDocument(); + expect(getByRole('button', { name: mockColumnsList[0].value.statuses[1] })).toBeInTheDocument(); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[0] })).not.toBeInTheDocument() - expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[1] })).not.toBeInTheDocument() - }) + expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[0] })).not.toBeInTheDocument(); + expect(queryByRole('button', { name: mockColumnsList[0].value.statuses[1] })).not.toBeInTheDocument(); + }); it('should show doing when choose Testing column is Done', async () => { - await store.dispatch(saveCycleTimeSettings([{ name: 'Done', value: 'Done' }])) - const { getByRole } = setup() + await store.dispatch(saveCycleTimeSettings([{ name: 'Done', value: 'Done' }])); + const { getByRole } = setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })) - }) - const listBox = within(getByRole('listbox')) - const allOption = listBox.getByRole('option', { name: 'All' }) + await userEvent.click(getByRole('combobox', { name: mockLabel })); + }); + const listBox = within(getByRole('listbox')); + const allOption = listBox.getByRole('option', { name: 'All' }); await act(async () => { - await userEvent.click(allOption) - }) + await userEvent.click(allOption); + }); - expect(getByRole('button', { name: 'DONE' })).toBeInTheDocument() - expect(getByRole('button', { name: 'CANCELLED' })).toBeInTheDocument() - }) + expect(getByRole('button', { name: 'DONE' })).toBeInTheDocument(); + expect(getByRole('button', { name: 'CANCELLED' })).toBeInTheDocument(); + }); it('should show warning message when realDone warning message has a value in realDone component', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText('Test warning Message')).toBeInTheDocument() - }) + expect(getByText('Test warning Message')).toBeInTheDocument(); + }); it('should clear warning message when realDone warning message has a value after four seconds in realDone component', async () => { - jest.useFakeTimers() - const { queryByText } = setup() + jest.useFakeTimers(); + const { queryByText } = setup(); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument() - }) - }) - }) + expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + }); + }); + }); describe('when done column with only one status', () => { it('should not show read done box', async () => { @@ -155,16 +155,16 @@ describe('RealDone', () => { statuses: ['DONE'], }, }, - ] + ]; const { queryByText } = render( - ) + ); - expect(queryByText(mockTitle)).not.toBeInTheDocument() - expect(queryByText(mockLabel)).not.toBeInTheDocument() - }) - }) -}) + expect(queryByText(mockTitle)).not.toBeInTheDocument(); + expect(queryByText(mockLabel)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStepper/ConfirmDialog.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStepper/ConfirmDialog.test.tsx index 8cf653eac8..5138411b48 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStepper/ConfirmDialog.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStepper/ConfirmDialog.test.tsx @@ -1,14 +1,14 @@ -import { render } from '@testing-library/react' -import { ConfirmDialog } from '@src/components/Metrics/MetricsStepper/ConfirmDialog' -import { CONFIRM_DIALOG_DESCRIPTION } from '../../../fixtures' +import { render } from '@testing-library/react'; +import { ConfirmDialog } from '@src/components/Metrics/MetricsStepper/ConfirmDialog'; +import { CONFIRM_DIALOG_DESCRIPTION } from '../../../fixtures'; -const onClose = jest.fn() -const onConfirm = jest.fn() +const onClose = jest.fn(); +const onConfirm = jest.fn(); describe('confirm dialog', () => { it('should show confirm dialog', () => { - const { getByText } = render() + const { getByText } = render(); - expect(getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument() - }) -}) + expect(getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ExpiredDialog.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ExpiredDialog.test.tsx index db0d7709e5..32f3fceed3 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ExpiredDialog.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ExpiredDialog.test.tsx @@ -1,52 +1,52 @@ -import { render, screen, waitFor } from '@testing-library/react' -import { setupStore } from '../../../utils/setupStoreUtil' -import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' -import { Provider } from 'react-redux' -import userEvent from '@testing-library/user-event' -import { EXPORT_EXPIRED_CSV_MESSAGE } from '../../../fixtures' +import { render, screen, waitFor } from '@testing-library/react'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog'; +import { Provider } from 'react-redux'; +import userEvent from '@testing-library/user-event'; +import { EXPORT_EXPIRED_CSV_MESSAGE } from '../../../fixtures'; describe('ExpiredDialog', () => { it('should show expired dialog when csv file expired and close expired dialog when click No button', async () => { - const handleOkFn = jest.fn() + const handleOkFn = jest.fn(); const { getByText, queryByText } = render( - ) + ); - expect(getByText(EXPORT_EXPIRED_CSV_MESSAGE)).toBeInTheDocument() + expect(getByText(EXPORT_EXPIRED_CSV_MESSAGE)).toBeInTheDocument(); - await userEvent.click(screen.getByText('No')) + await userEvent.click(screen.getByText('No')); await waitFor(() => { - expect(queryByText(EXPORT_EXPIRED_CSV_MESSAGE)).not.toBeInTheDocument() - }) - }) + expect(queryByText(EXPORT_EXPIRED_CSV_MESSAGE)).not.toBeInTheDocument(); + }); + }); it('should not show expired dialog when isExpired is false ', async () => { - const handleOkFn = jest.fn() + const handleOkFn = jest.fn(); const { queryByText } = render( - ) + ); - expect(queryByText(EXPORT_EXPIRED_CSV_MESSAGE)).not.toBeInTheDocument() - }) + expect(queryByText(EXPORT_EXPIRED_CSV_MESSAGE)).not.toBeInTheDocument(); + }); it('should close expired dialog given an expired dialog when click the Ok button', async () => { - const handleOkFn = jest.fn() + const handleOkFn = jest.fn(); const { getByText } = render( - ) + ); - expect(getByText(EXPORT_EXPIRED_CSV_MESSAGE)).toBeInTheDocument() + expect(getByText(EXPORT_EXPIRED_CSV_MESSAGE)).toBeInTheDocument(); - await userEvent.click(screen.getByText('Yes')) - expect(handleOkFn).toBeCalledTimes(1) - }) -}) + await userEvent.click(screen.getByText('Yes')); + expect(handleOkFn).toBeCalledTimes(1); + }); +}); diff --git a/frontend/__tests__/src/components/ProjectDescripution.test.tsx b/frontend/__tests__/src/components/ProjectDescripution.test.tsx index c90533f0c0..6da87ac6b6 100644 --- a/frontend/__tests__/src/components/ProjectDescripution.test.tsx +++ b/frontend/__tests__/src/components/ProjectDescripution.test.tsx @@ -1,11 +1,11 @@ -import { render } from '@testing-library/react' -import { ProjectDescription } from '@src/components/ProjectDescription' -import { PROJECT_DESCRIPTION } from '../fixtures' +import { render } from '@testing-library/react'; +import { ProjectDescription } from '@src/components/ProjectDescription'; +import { PROJECT_DESCRIPTION } from '../fixtures'; describe('ProjectDescription', () => { it('should show project description', () => { - const { getByRole } = render() + const { getByRole } = render(); - expect(getByRole('description').textContent).toContain(PROJECT_DESCRIPTION) - }) -}) + expect(getByRole('description').textContent).toContain(PROJECT_DESCRIPTION); + }); +}); diff --git a/frontend/__tests__/src/context/boardSlice.test.ts b/frontend/__tests__/src/context/boardSlice.test.ts index f3b752bb97..0b1b5e5c69 100644 --- a/frontend/__tests__/src/context/boardSlice.test.ts +++ b/frontend/__tests__/src/context/boardSlice.test.ts @@ -2,37 +2,37 @@ import boardReducer, { updateBoard, updateBoardVerifyState, updateJiraVerifyResponse, -} from '@src/context/config/configSlice' -import { MOCK_JIRA_VERIFY_RESPONSE } from '../fixtures' -import initialConfigState from '../initialConfigState' +} from '@src/context/config/configSlice'; +import { MOCK_JIRA_VERIFY_RESPONSE } from '../fixtures'; +import initialConfigState from '../initialConfigState'; describe('board reducer', () => { it('should return false when handle initial state', () => { - const result = boardReducer(undefined, { type: 'unknown' }) + const result = boardReducer(undefined, { type: 'unknown' }); - expect(result.board.isVerified).toEqual(false) - }) + expect(result.board.isVerified).toEqual(false); + }); it('should return true when handle changeBoardVerifyState given isBoardVerified is true', () => { - const result = boardReducer(initialConfigState, updateBoardVerifyState(true)) + const result = boardReducer(initialConfigState, updateBoardVerifyState(true)); - expect(result.board.isVerified).toEqual(true) - }) + expect(result.board.isVerified).toEqual(true); + }); it('should update board fields when change board fields input', () => { - const board = boardReducer(initialConfigState, updateBoard({ boardId: '1' })) + const board = boardReducer(initialConfigState, updateBoard({ boardId: '1' })); - expect(board.board.config.boardId).toEqual('1') - }) + expect(board.board.config.boardId).toEqual('1'); + }); describe('boardVerifyResponse reducer', () => { it('should show empty array when handle initial state', () => { - const boardVerifyResponse = boardReducer(undefined, { type: 'unknown' }) + const boardVerifyResponse = boardReducer(undefined, { type: 'unknown' }); - expect(boardVerifyResponse.board.verifiedResponse.jiraColumns).toEqual([]) - expect(boardVerifyResponse.board.verifiedResponse.targetFields).toEqual([]) - expect(boardVerifyResponse.board.verifiedResponse.users).toEqual([]) - }) + expect(boardVerifyResponse.board.verifiedResponse.jiraColumns).toEqual([]); + expect(boardVerifyResponse.board.verifiedResponse.targetFields).toEqual([]); + expect(boardVerifyResponse.board.verifiedResponse.users).toEqual([]); + }); it('should store jiraColumns,targetFields,users data when get network jira verify response', () => { const boardVerifyResponse = boardReducer( @@ -42,11 +42,11 @@ describe('board reducer', () => { targetFields: MOCK_JIRA_VERIFY_RESPONSE.targetFields, users: MOCK_JIRA_VERIFY_RESPONSE.users, }) - ) - - expect(boardVerifyResponse.board.verifiedResponse.jiraColumns).toEqual(MOCK_JIRA_VERIFY_RESPONSE.jiraColumns) - expect(boardVerifyResponse.board.verifiedResponse.targetFields).toEqual(MOCK_JIRA_VERIFY_RESPONSE.targetFields) - expect(boardVerifyResponse.board.verifiedResponse.users).toEqual(MOCK_JIRA_VERIFY_RESPONSE.users) - }) - }) -}) + ); + + expect(boardVerifyResponse.board.verifiedResponse.jiraColumns).toEqual(MOCK_JIRA_VERIFY_RESPONSE.jiraColumns); + expect(boardVerifyResponse.board.verifiedResponse.targetFields).toEqual(MOCK_JIRA_VERIFY_RESPONSE.targetFields); + expect(boardVerifyResponse.board.verifiedResponse.users).toEqual(MOCK_JIRA_VERIFY_RESPONSE.users); + }); + }); +}); diff --git a/frontend/__tests__/src/context/configSlice.test.ts b/frontend/__tests__/src/context/configSlice.test.ts index 6726232a51..3797fb6b38 100644 --- a/frontend/__tests__/src/context/configSlice.test.ts +++ b/frontend/__tests__/src/context/configSlice.test.ts @@ -5,9 +5,9 @@ import configReducer, { updateMetrics, updateProjectCreatedState, updateProjectName, -} from '@src/context/config/configSlice' -import { CHINA_CALENDAR, CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE, REGULAR_CALENDAR, VELOCITY } from '../fixtures' -import initialConfigState from '../initialConfigState' +} from '@src/context/config/configSlice'; +import { CHINA_CALENDAR, CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE, REGULAR_CALENDAR, VELOCITY } from '../fixtures'; +import initialConfigState from '../initialConfigState'; const MockBasicState = { projectName: 'Test Project', @@ -16,70 +16,70 @@ const MockBasicState = { endDate: new Date(), }, metrics: ['Metric 1', 'Metric 2'], -} +}; describe('config reducer', () => { it('should be default value when init render config page', () => { - const config = configReducer(undefined, { type: 'unknown' }).basic + const config = configReducer(undefined, { type: 'unknown' }).basic; - expect(config.projectName).toEqual('') - expect(config.calendarType).toEqual(REGULAR_CALENDAR) - expect(config.dateRange).toEqual({ startDate: null, endDate: null }) - }) + expect(config.projectName).toEqual(''); + expect(config.calendarType).toEqual(REGULAR_CALENDAR); + expect(config.dateRange).toEqual({ startDate: null, endDate: null }); + }); it('should update project name when change project name input', () => { - const config = configReducer(initialConfigState, updateProjectName('mock project name')).basic + const config = configReducer(initialConfigState, updateProjectName('mock project name')).basic; - expect(config.projectName).toEqual('mock project name') - }) + expect(config.projectName).toEqual('mock project name'); + }); it('should update calendar when change calendar types', () => { - const config = configReducer(initialConfigState, updateCalendarType(CHINA_CALENDAR)).basic + const config = configReducer(initialConfigState, updateCalendarType(CHINA_CALENDAR)).basic; - expect(config.calendarType).toEqual(CHINA_CALENDAR) - }) + expect(config.calendarType).toEqual(CHINA_CALENDAR); + }); it('should update date range when change date', () => { - const today = new Date().getMilliseconds() - const config = configReducer(initialConfigState, updateDateRange({ startDate: today, endDate: '' })).basic + const today = new Date().getMilliseconds(); + const config = configReducer(initialConfigState, updateDateRange({ startDate: today, endDate: '' })).basic; - expect(config.dateRange.startDate).toEqual(today) - expect(config.dateRange.endDate).toEqual('') - }) + expect(config.dateRange.startDate).toEqual(today); + expect(config.dateRange.endDate).toEqual(''); + }); it('should isProjectCreated is false when import file', () => { - const config = configReducer(initialConfigState, updateProjectCreatedState(false)) + const config = configReducer(initialConfigState, updateProjectCreatedState(false)); - expect(config.isProjectCreated).toEqual(false) - }) + expect(config.isProjectCreated).toEqual(false); + }); it('should update required data when change require data selections', () => { - const config = configReducer(initialConfigState, updateMetrics([VELOCITY])).basic + const config = configReducer(initialConfigState, updateMetrics([VELOCITY])).basic; - expect(config.metrics).toEqual([VELOCITY]) - }) + expect(config.metrics).toEqual([VELOCITY]); + }); it('should set warningMessage is null when projectName startDate endDate and metrics data have value', () => { const initialState = { ...initialConfigState, isProjectCreated: false, - } + }; const action = { type: 'config/updateBasicConfigState', payload: MockBasicState, - } + }; - const config = configReducer(initialState, action) + const config = configReducer(initialState, action); - expect(config.warningMessage).toBeNull() - }) + expect(config.warningMessage).toBeNull(); + }); it('should reset ImportedData when input new config', () => { - const initialState = initialConfigState + const initialState = initialConfigState; - const config = configReducer(initialState, resetImportedData()) + const config = configReducer(initialState, resetImportedData()); - expect(config).toEqual(initialConfigState) - }) + expect(config).toEqual(initialConfigState); + }); it.each([ ['projectName', { ...MockBasicState, projectName: '' }], @@ -90,14 +90,14 @@ describe('config reducer', () => { const initialState = { ...initialConfigState, isProjectCreated: false, - } + }; const action = { type: 'config/updateBasicConfigState', payload, - } + }; - const config = configReducer(initialState, action) + const config = configReducer(initialState, action); - expect(config.warningMessage).toEqual(CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE) - }) -}) + expect(config.warningMessage).toEqual(CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE); + }); +}); diff --git a/frontend/__tests__/src/context/headerSlice.test.ts b/frontend/__tests__/src/context/headerSlice.test.ts index 235ffcbe72..140c2dbcc3 100644 --- a/frontend/__tests__/src/context/headerSlice.test.ts +++ b/frontend/__tests__/src/context/headerSlice.test.ts @@ -1,16 +1,16 @@ -import headerReducer, { saveVersion } from '@src/context/header/headerSlice' -import { VERSION_RESPONSE } from '../fixtures' +import headerReducer, { saveVersion } from '@src/context/header/headerSlice'; +import { VERSION_RESPONSE } from '../fixtures'; describe('header reducer', () => { it('should get empty when handle initial state', () => { - const header = headerReducer(undefined, { type: 'unknown' }) + const header = headerReducer(undefined, { type: 'unknown' }); - expect(header.version).toEqual('') - }) + expect(header.version).toEqual(''); + }); it('should set 1.11 when handle saveVersion', () => { - const header = headerReducer(undefined, saveVersion(VERSION_RESPONSE.version)) + const header = headerReducer(undefined, saveVersion(VERSION_RESPONSE.version)); - expect(header.version).toEqual(VERSION_RESPONSE.version) - }) -}) + expect(header.version).toEqual(VERSION_RESPONSE.version); + }); +}); diff --git a/frontend/__tests__/src/context/metricsSlice.test.ts b/frontend/__tests__/src/context/metricsSlice.test.ts index 116ce03792..2090963088 100644 --- a/frontend/__tests__/src/context/metricsSlice.test.ts +++ b/frontend/__tests__/src/context/metricsSlice.test.ts @@ -16,11 +16,11 @@ import saveMetricsSettingReducer, { updatePipelineSettings, updatePipelineStep, updateTreatFlagCardAsBlock, -} from '@src/context/Metrics/metricsSlice' -import { store } from '@src/store' -import { CLASSIFICATION_WARNING_MESSAGE, NO_RESULT_DASH, PIPELINE_SETTING_TYPES } from '../fixtures' -import { ASSIGNEE_FILTER_TYPES, MESSAGE } from '@src/constants/resources' -import { setupStore } from '../utils/setupStoreUtil' +} from '@src/context/Metrics/metricsSlice'; +import { store } from '@src/store'; +import { CLASSIFICATION_WARNING_MESSAGE, NO_RESULT_DASH, PIPELINE_SETTING_TYPES } from '../fixtures'; +import { ASSIGNEE_FILTER_TYPES, MESSAGE } from '@src/constants/resources'; +import { setupStore } from '../utils/setupStoreUtil'; const initState = { jiraColumns: [], @@ -52,7 +52,7 @@ const initState = { realDoneWarningMessage: null, deploymentWarningMessage: [], leadTimeWarningMessage: [], -} +}; const mockJiraResponse = { targetFields: [{ key: 'issuetype', name: 'Issue Type', flag: false }], @@ -81,22 +81,22 @@ const mockJiraResponse = { }, }, ], -} +}; describe('saveMetricsSetting reducer', () => { it('should show empty array when handle initial state', () => { - const savedMetricsSetting = saveMetricsSettingReducer(undefined, { type: 'unknown' }) + const savedMetricsSetting = saveMetricsSettingReducer(undefined, { type: 'unknown' }); - expect(savedMetricsSetting.users).toEqual([]) - expect(savedMetricsSetting.targetFields).toEqual([]) - expect(savedMetricsSetting.jiraColumns).toEqual([]) - expect(savedMetricsSetting.doneColumn).toEqual([]) - expect(savedMetricsSetting.cycleTimeSettings).toEqual([]) + expect(savedMetricsSetting.users).toEqual([]); + expect(savedMetricsSetting.targetFields).toEqual([]); + expect(savedMetricsSetting.jiraColumns).toEqual([]); + expect(savedMetricsSetting.doneColumn).toEqual([]); + expect(savedMetricsSetting.cycleTimeSettings).toEqual([]); expect(savedMetricsSetting.deploymentFrequencySettings).toEqual([ { id: 0, organization: '', pipelineName: '', step: '', branches: [] }, - ]) - expect(savedMetricsSetting.treatFlagCardAsBlock).toBe(true) - expect(savedMetricsSetting.assigneeFilter).toBe(ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE) + ]); + expect(savedMetricsSetting.treatFlagCardAsBlock).toBe(true); + expect(savedMetricsSetting.assigneeFilter).toBe(ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE); expect(savedMetricsSetting.importedData).toEqual({ importedCrews: [], importedAssigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, @@ -108,8 +108,8 @@ describe('saveMetricsSetting reducer', () => { importedClassification: [], importedDeployment: [], importedPipelineCrews: [], - }) - }) + }); + }); it('should store updated targetFields when its value changed', () => { const mockUpdatedTargetFields = { @@ -118,60 +118,60 @@ describe('saveMetricsSetting reducer', () => { { key: 'parent', name: 'Parent', flag: false }, { key: 'customfield_10020', name: 'Sprint', flag: false }, ], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initState, saveTargetFields({ targetFields: mockUpdatedTargetFields.targetFields, }) - ) + ); - expect(savedMetricsSetting.targetFields).toEqual(mockUpdatedTargetFields) - expect(savedMetricsSetting.users).toEqual([]) - expect(savedMetricsSetting.jiraColumns).toEqual([]) - }) + expect(savedMetricsSetting.targetFields).toEqual(mockUpdatedTargetFields); + expect(savedMetricsSetting.users).toEqual([]); + expect(savedMetricsSetting.jiraColumns).toEqual([]); + }); it('should store updated doneColumn when its value changed', () => { const mockUpdatedDoneColumn = { doneColumn: ['DONE', 'CANCELLED'], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initState, saveDoneColumn({ doneColumn: mockUpdatedDoneColumn.doneColumn, }) - ) + ); - expect(savedMetricsSetting.doneColumn).toEqual(mockUpdatedDoneColumn) - }) + expect(savedMetricsSetting.doneColumn).toEqual(mockUpdatedDoneColumn); + }); it('should store updated users when its value changed', () => { const mockUpdatedUsers = { users: ['userOne', 'userTwo', 'userThree'], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initState, saveUsers({ users: mockUpdatedUsers.users, }) - ) + ); - expect(savedMetricsSetting.users).toEqual(mockUpdatedUsers) - }) + expect(savedMetricsSetting.users).toEqual(mockUpdatedUsers); + }); it('should store saved cycleTimeSettings when its value changed', () => { const mockSavedCycleTimeSettings = { cycleTimeSettings: [{ name: 'TODO', value: 'To do' }], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initState, saveCycleTimeSettings({ cycleTimeSettings: mockSavedCycleTimeSettings.cycleTimeSettings, }) - ) + ); - expect(savedMetricsSetting.cycleTimeSettings).toEqual(mockSavedCycleTimeSettings) - }) + expect(savedMetricsSetting.cycleTimeSettings).toEqual(mockSavedCycleTimeSettings); + }); it('should update metricsImportedData when its value changed given initial state', () => { const mockMetricsImportedData = { @@ -186,8 +186,11 @@ describe('saveMetricsSetting reducer', () => { deployment: [{ id: 0, organization: 'organization', pipelineName: 'pipelineName', step: 'step' }], leadTime: [], pipelineCrews: [], - } - const savedMetricsSetting = saveMetricsSettingReducer(initState, updateMetricsImportedData(mockMetricsImportedData)) + }; + const savedMetricsSetting = saveMetricsSettingReducer( + initState, + updateMetricsImportedData(mockMetricsImportedData) + ); expect(savedMetricsSetting.importedData).toEqual({ importedCrews: mockMetricsImportedData.crews, @@ -201,8 +204,8 @@ describe('saveMetricsSetting reducer', () => { importedClassification: mockMetricsImportedData.classification, importedDeployment: mockMetricsImportedData.deployment, importedLeadTime: mockMetricsImportedData.leadTime, - }) - }) + }); + }); it('should not update metricsImportedData when imported data is broken given initial state', () => { const mockMetricsImportedData = { @@ -215,26 +218,29 @@ describe('saveMetricsSetting reducer', () => { classification: null, deployment: null, leadTime: null, - } + }; - const savedMetricsSetting = saveMetricsSettingReducer(initState, updateMetricsImportedData(mockMetricsImportedData)) - - expect(savedMetricsSetting.users).toEqual([]) - expect(savedMetricsSetting.targetFields).toEqual([]) - expect(savedMetricsSetting.jiraColumns).toEqual([]) - expect(savedMetricsSetting.doneColumn).toEqual([]) - expect(savedMetricsSetting.cycleTimeSettings).toEqual([]) - expect(savedMetricsSetting.treatFlagCardAsBlock).toEqual(true) + const savedMetricsSetting = saveMetricsSettingReducer( + initState, + updateMetricsImportedData(mockMetricsImportedData) + ); + + expect(savedMetricsSetting.users).toEqual([]); + expect(savedMetricsSetting.targetFields).toEqual([]); + expect(savedMetricsSetting.jiraColumns).toEqual([]); + expect(savedMetricsSetting.doneColumn).toEqual([]); + expect(savedMetricsSetting.cycleTimeSettings).toEqual([]); + expect(savedMetricsSetting.treatFlagCardAsBlock).toEqual(true); expect(savedMetricsSetting.deploymentFrequencySettings).toEqual([ { id: 0, organization: '', pipelineName: '', step: '', branches: [] }, - ]) - }) + ]); + }); it('should update metricsState when its value changed given isProjectCreated is false and selectedDoneColumns', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -250,23 +256,23 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.targetFields).toEqual([{ key: 'issuetype', name: 'Issue Type', flag: true }]) - expect(savedMetricsSetting.users).toEqual(['User B']) + expect(savedMetricsSetting.targetFields).toEqual([{ key: 'issuetype', name: 'Issue Type', flag: true }]); + expect(savedMetricsSetting.users).toEqual(['User B']); expect(savedMetricsSetting.cycleTimeSettings).toEqual([ { name: 'Done', value: 'Done' }, { name: 'Doing', value: NO_RESULT_DASH }, { name: 'Testing', value: NO_RESULT_DASH }, - ]) - expect(savedMetricsSetting.doneColumn).toEqual(['DONE']) - }) + ]); + expect(savedMetricsSetting.doneColumn).toEqual(['DONE']); + }); it('should update metricsState when its value changed given isProjectCreated is false and no selectedDoneColumns', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { @@ -281,41 +287,41 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.doneColumn).toEqual([]) - }) + expect(savedMetricsSetting.doneColumn).toEqual([]); + }); it('should update metricsState when its value changed given isProjectCreated is true', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: true, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initState, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.targetFields).toEqual([{ key: 'issuetype', name: 'Issue Type', flag: false }]) - expect(savedMetricsSetting.users).toEqual(['User A', 'User B']) + expect(savedMetricsSetting.targetFields).toEqual([{ key: 'issuetype', name: 'Issue Type', flag: false }]); + expect(savedMetricsSetting.users).toEqual(['User A', 'User B']); expect(savedMetricsSetting.cycleTimeSettings).toEqual([ { name: 'Done', value: NO_RESULT_DASH }, { name: 'Doing', value: NO_RESULT_DASH }, { name: 'Testing', value: NO_RESULT_DASH }, - ]) - expect(savedMetricsSetting.doneColumn).toEqual([]) - }) + ]); + expect(savedMetricsSetting.doneColumn).toEqual([]); + }); it('should update deploymentFrequencySettings when handle updateDeploymentFrequencySettings given initial state', () => { const savedMetricsSetting = saveMetricsSettingReducer( initState, updateDeploymentFrequencySettings({ updateId: 0, label: 'Steps', value: 'step1' }) - ) + ); expect(savedMetricsSetting.deploymentFrequencySettings).toEqual([ { id: 0, organization: '', pipelineName: '', step: 'step1', branches: [] }, - ]) - }) + ]); + }); it('should update a deploymentFrequencySetting when handle updateDeploymentFrequencySettings given multiple deploymentFrequencySettings', () => { const multipleDeploymentFrequencySettingsInitState = { @@ -324,55 +330,55 @@ describe('saveMetricsSetting reducer', () => { { id: 0, organization: '', pipelineName: '', step: '', branches: [] }, { id: 1, organization: '', pipelineName: '', step: '', branches: [] }, ], - } + }; const updatedDeploymentFrequencySettings = [ { id: 0, organization: 'mock new organization', pipelineName: '', step: '', branches: [] }, { id: 1, organization: '', pipelineName: '', step: '', branches: [] }, - ] + ]; const savedMetricsSetting = saveMetricsSettingReducer( multipleDeploymentFrequencySettingsInitState, updateDeploymentFrequencySettings({ updateId: 0, label: 'organization', value: 'mock new organization' }) - ) + ); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(updatedDeploymentFrequencySettings) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(updatedDeploymentFrequencySettings); + }); it('should add a deploymentFrequencySetting when handle addADeploymentFrequencySettings given initial state', () => { const addedDeploymentFrequencySettings = [ { id: 0, organization: '', pipelineName: '', step: '', branches: [] }, { id: 1, organization: '', pipelineName: '', step: '', branches: [] }, - ] + ]; - const savedMetricsSetting = saveMetricsSettingReducer(initState, addADeploymentFrequencySetting()) + const savedMetricsSetting = saveMetricsSettingReducer(initState, addADeploymentFrequencySetting()); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(addedDeploymentFrequencySettings) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(addedDeploymentFrequencySettings); + }); it('should add a deploymentFrequencySetting when handle addADeploymentFrequencySettings but initState dont have DeploymentFrequencySettings', () => { - const addedDeploymentFrequencySettings = [{ id: 0, organization: '', pipelineName: '', step: '', branches: [] }] + const addedDeploymentFrequencySettings = [{ id: 0, organization: '', pipelineName: '', step: '', branches: [] }]; const initStateWithoutDeploymentFrequencySettings = { ...initState, deploymentFrequencySettings: [], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( initStateWithoutDeploymentFrequencySettings, addADeploymentFrequencySetting() - ) + ); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(addedDeploymentFrequencySettings) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(addedDeploymentFrequencySettings); + }); it('should delete a deploymentFrequencySetting when handle deleteADeploymentFrequencySettings given initial state', () => { - const savedMetricsSetting = saveMetricsSettingReducer(initState, deleteADeploymentFrequencySetting(0)) + const savedMetricsSetting = saveMetricsSettingReducer(initState, deleteADeploymentFrequencySetting(0)); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual([]) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual([]); + }); it('should return deploymentFrequencySettings when call selectDeploymentFrequencySettings functions', () => { - expect(selectDeploymentFrequencySettings(store.getState())).toEqual(initState.deploymentFrequencySettings) - }) + expect(selectDeploymentFrequencySettings(store.getState())).toEqual(initState.deploymentFrequencySettings); + }); it('should init deploymentFrequencySettings when handle initDeploymentFrequencySettings given multiple deploymentFrequencySettings', () => { const multipleDeploymentFrequencySettingsInitState = { @@ -381,31 +387,31 @@ describe('saveMetricsSetting reducer', () => { { id: 0, organization: 'mockOrgName1', pipelineName: 'mockName1', step: 'step1', branches: [] }, { id: 1, organization: 'mockOrgName2', pipelineName: 'mockName2', step: 'step2', branches: [] }, ], - } + }; const savedMetricsSetting = saveMetricsSettingReducer( multipleDeploymentFrequencySettingsInitState, initDeploymentFrequencySettings() - ) + ); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(initState.deploymentFrequencySettings) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(initState.deploymentFrequencySettings); + }); it('should return false when update TreatFlagCardAsBlock value given false', () => { - const savedMetricsSetting = saveMetricsSettingReducer(initState, updateTreatFlagCardAsBlock(false)) + const savedMetricsSetting = saveMetricsSettingReducer(initState, updateTreatFlagCardAsBlock(false)); - expect(savedMetricsSetting.treatFlagCardAsBlock).toBe(false) - }) + expect(savedMetricsSetting.treatFlagCardAsBlock).toBe(false); + }); describe('updatePipelineSettings', () => { const mockImportedDeployment = [ { id: 0, organization: 'mockOrganization1', pipelineName: 'mockPipelineName1', step: 'mockStep1', branches: [] }, { id: 1, organization: 'mockOrganization1', pipelineName: 'mockPipelineName2', step: 'mockStep2', branches: [] }, { id: 2, organization: 'mockOrganization2', pipelineName: 'mockPipelineName3', step: 'mockStep3', branches: [] }, - ] + ]; const mockImportedLeadTime = [ { id: 0, organization: 'mockOrganization1', pipelineName: 'mockPipelineName1', step: 'mockStep1', branches: [] }, - ] + ]; const mockInitState = { ...initState, importedData: { @@ -413,7 +419,7 @@ describe('saveMetricsSetting reducer', () => { importedDeployment: mockImportedDeployment, importedLeadTime: mockImportedLeadTime, }, - } + }; const mockPipelineList = [ { id: 'mockId1', @@ -424,7 +430,7 @@ describe('saveMetricsSetting reducer', () => { steps: ['mock step 1', 'mock step 2'], branches: [], }, - ] + ]; const testCases = [ { isProjectCreated: false, @@ -454,30 +460,30 @@ describe('saveMetricsSetting reducer', () => { leadTimeWarningMessage: [], }, }, - ] + ]; testCases.forEach(({ isProjectCreated, expectSetting }) => { it(`should update pipeline settings When call updatePipelineSettings given isProjectCreated ${isProjectCreated}`, () => { const savedMetricsSetting = saveMetricsSettingReducer( mockInitState, updatePipelineSettings({ pipelineList: mockPipelineList, isProjectCreated }) - ) + ); - expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(expectSetting.deploymentFrequencySettings) - expect(savedMetricsSetting.deploymentWarningMessage).toEqual(expectSetting.deploymentWarningMessage) - }) - }) - }) + expect(savedMetricsSetting.deploymentFrequencySettings).toEqual(expectSetting.deploymentFrequencySettings); + expect(savedMetricsSetting.deploymentWarningMessage).toEqual(expectSetting.deploymentWarningMessage); + }); + }); + }); describe('updatePipelineSteps', () => { const mockImportedDeployment = [ { id: 0, organization: 'mockOrganization1', pipelineName: 'mockPipelineName1', step: 'mockStep1', branches: [] }, { id: 1, organization: 'mockOrganization1', pipelineName: 'mockPipelineName2', step: 'mockStep2', branches: [] }, { id: 2, organization: 'mockOrganization2', pipelineName: 'mockPipelineName3', step: 'mockStep3', branches: [] }, - ] + ]; const mockImportedLeadTime = [ { id: 0, organization: 'mockOrganization1', pipelineName: 'mockPipelineName1', step: 'mockStep1', branches: [] }, - ] + ]; const mockInitState = { ...initState, deploymentFrequencySettings: [ @@ -503,8 +509,8 @@ describe('saveMetricsSetting reducer', () => { { id: 1, organization: null, pipelineName: null, step: null }, ], leadTimeWarningMessage: [{ id: 0, organization: null, pipelineName: null, step: null }], - } - const mockSteps = ['mockStep1'] + }; + const mockSteps = ['mockStep1']; const testSettingsCases = [ { id: 0, @@ -543,11 +549,11 @@ describe('saveMetricsSetting reducer', () => { }, ], }, - ] + ]; testSettingsCases.forEach(({ id, type, steps, expectedSettings, expectedWarning }) => { - const settingsKey = 'deploymentFrequencySettings' - const warningKey = 'deploymentWarningMessage' + const settingsKey = 'deploymentFrequencySettings'; + const warningKey = 'deploymentWarningMessage'; it(`should update ${settingsKey} step when call updatePipelineSteps with id ${id}`, () => { const savedMetricsSetting = saveMetricsSettingReducer( @@ -557,19 +563,19 @@ describe('saveMetricsSetting reducer', () => { id: id, type: type, }) - ) + ); - expect(savedMetricsSetting[settingsKey]).toEqual(expectedSettings) - expect(savedMetricsSetting[warningKey]).toEqual(expectedWarning) - }) - }) - }) + expect(savedMetricsSetting[settingsKey]).toEqual(expectedSettings); + expect(savedMetricsSetting[warningKey]).toEqual(expectedWarning); + }); + }); + }); it('should set warningMessage have value when there are more values in the import file than in the response', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -587,18 +593,18 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); expect(savedMetricsSetting.cycleTimeWarningMessage).toEqual( 'The column of ToDo is a deleted column, which means this column existed the time you saved config, but was deleted. Please confirm!' - ) - }) + ); + }); it('should set warningMessage have value when the values in the import file are less than those in the response', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -611,18 +617,18 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); expect(savedMetricsSetting.cycleTimeWarningMessage).toEqual( 'The column of Testing is a new column. Please select a value for it!' - ) - }) + ); + }); it('should set warningMessage have value when the key value in the import file matches the value in the response, but the value does not match the fixed column', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -635,18 +641,18 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); expect(savedMetricsSetting.cycleTimeWarningMessage).toEqual( 'The value of Doing in imported json is not in dropdown list now. Please select a value for it!' - ) - }) + ); + }); it('should set warningMessage null when the key value in the imported file matches the value in the response and the value matches the fixed column', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -659,16 +665,16 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.cycleTimeWarningMessage).toBeNull() - }) + expect(savedMetricsSetting.cycleTimeWarningMessage).toBeNull(); + }); it('should set warningMessage null when importedCycleTimeSettings in the imported file matches is empty', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -681,16 +687,16 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.cycleTimeWarningMessage).toBeNull() - }) + expect(savedMetricsSetting.cycleTimeWarningMessage).toBeNull(); + }); it('should set classification warningMessage null when the key value in the imported file matches the value in the response and the value matches the fixed column', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -700,16 +706,16 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.classificationWarningMessage).toBeNull() - }) + expect(savedMetricsSetting.classificationWarningMessage).toBeNull(); + }); it('should set classification warningMessage have value when the key value in the imported file matches the value in the response and the value matches the fixed column', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -719,10 +725,10 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.classificationWarningMessage).toEqual(CLASSIFICATION_WARNING_MESSAGE) - }) + expect(savedMetricsSetting.classificationWarningMessage).toEqual(CLASSIFICATION_WARNING_MESSAGE); + }); it('should set realDone warning message null when doneColumns in imported file matches the value in the response', () => { const mockUpdateMetricsStateArguments = { @@ -737,7 +743,7 @@ describe('saveMetricsSetting reducer', () => { }, ], isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -747,10 +753,10 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.realDoneWarningMessage).toBeNull() - }) + expect(savedMetricsSetting.realDoneWarningMessage).toBeNull(); + }); it('should set realDone warning message have value when doneColumns in imported file not matches the value in the response', () => { const mockUpdateMetricsStateArguments = { @@ -765,7 +771,7 @@ describe('saveMetricsSetting reducer', () => { }, ], isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -775,10 +781,10 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.realDoneWarningMessage).toEqual(MESSAGE.REAL_DONE_WARNING) - }) + expect(savedMetricsSetting.realDoneWarningMessage).toEqual(MESSAGE.REAL_DONE_WARNING); + }); it('should set realDone warning message null when doneColumns in imported file matches the value in cycleTimeSettings', () => { const mockUpdateMetricsStateArguments = { @@ -793,7 +799,7 @@ describe('saveMetricsSetting reducer', () => { }, ], isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -804,10 +810,10 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.realDoneWarningMessage).toBeNull() - }) + expect(savedMetricsSetting.realDoneWarningMessage).toBeNull(); + }); it('should set realDone warning message have value when doneColumns in imported file not matches the value in cycleTimeSettings', () => { const mockUpdateMetricsStateArguments = { @@ -822,7 +828,7 @@ describe('saveMetricsSetting reducer', () => { }, ], isProjectCreated: false, - } + }; const savedMetricsSetting = saveMetricsSettingReducer( { ...initState, @@ -833,10 +839,10 @@ describe('saveMetricsSetting reducer', () => { }, }, updateMetricsState(mockUpdateMetricsStateArguments) - ) + ); - expect(savedMetricsSetting.realDoneWarningMessage).toEqual(MESSAGE.REAL_DONE_WARNING) - }) + expect(savedMetricsSetting.realDoneWarningMessage).toEqual(MESSAGE.REAL_DONE_WARNING); + }); describe('select pipeline settings warning message', () => { const mockPipelineSettings = [ @@ -852,11 +858,11 @@ describe('saveMetricsSetting reducer', () => { pipelineName: 'mockPipelineName2', step: ' mockStep1', }, - ] + ]; const mockImportData = { deployment: mockPipelineSettings, leadTime: mockPipelineSettings, - } + }; const mockPipelineList = [ { id: 'mockId1', @@ -866,60 +872,60 @@ describe('saveMetricsSetting reducer', () => { repository: 'mockRepository1', steps: ['mock step 1', 'mock step 2'], }, - ] - const mockSteps = ['mockStep'] - const ORGANIZATION_WARNING_MESSAGE = 'This organization in import data might be removed' - const PIPELINE_NAME_WARNING_MESSAGE = 'This Pipeline in import data might be removed' - const STEP_WARNING_MESSAGE = 'Selected step of this pipeline in import data might be removed' + ]; + const mockSteps = ['mockStep']; + const ORGANIZATION_WARNING_MESSAGE = 'This organization in import data might be removed'; + const PIPELINE_NAME_WARNING_MESSAGE = 'This Pipeline in import data might be removed'; + const STEP_WARNING_MESSAGE = 'Selected step of this pipeline in import data might be removed'; - let store = setupStore() + let store = setupStore(); beforeEach(async () => { - store = setupStore() - await store.dispatch(updateMetricsImportedData(mockImportData)) - await store.dispatch(updatePipelineSettings({ pipelineList: mockPipelineList, isProjectCreated: false })) + store = setupStore(); + await store.dispatch(updateMetricsImportedData(mockImportData)); + await store.dispatch(updatePipelineSettings({ pipelineList: mockPipelineList, isProjectCreated: false })); await store.dispatch( updatePipelineStep({ steps: mockSteps, id: 0, type: PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE, }) - ) + ); await store.dispatch( updatePipelineStep({ steps: mockSteps, id: 1, type: PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE, }) - ) - }) + ); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should return organization warning message given its id and type', () => { expect( selectOrganizationWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toEqual(ORGANIZATION_WARNING_MESSAGE) + ).toEqual(ORGANIZATION_WARNING_MESSAGE); expect( selectOrganizationWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toBeNull() - }) + ).toBeNull(); + }); it('should return pipelineName warning message given its id and type', () => { expect( selectPipelineNameWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toBeNull() + ).toBeNull(); expect( selectPipelineNameWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toEqual(PIPELINE_NAME_WARNING_MESSAGE) - }) + ).toEqual(PIPELINE_NAME_WARNING_MESSAGE); + }); it('should return step warning message given its id and type', () => { expect( selectStepWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toBeNull() + ).toBeNull(); expect( selectStepWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) - ).toEqual(STEP_WARNING_MESSAGE) - }) - }) -}) + ).toEqual(STEP_WARNING_MESSAGE); + }); + }); +}); diff --git a/frontend/__tests__/src/context/pipelineToolSlice.test.ts b/frontend/__tests__/src/context/pipelineToolSlice.test.ts index a3d9186869..d4ce64ea9d 100644 --- a/frontend/__tests__/src/context/pipelineToolSlice.test.ts +++ b/frontend/__tests__/src/context/pipelineToolSlice.test.ts @@ -8,11 +8,11 @@ import { updatePipelineToolVerifyResponse, updatePipelineToolVerifyResponseSteps, updatePipelineToolVerifyState, -} from '@src/context/config/configSlice' -import configReducer from '@src/context/config/configSlice' -import initialConfigState from '../initialConfigState' -import { MOCK_BUILD_KITE_VERIFY_RESPONSE, PIPELINE_TOOL_TYPES } from '../fixtures' -import { setupStore } from '../utils/setupStoreUtil' +} from '@src/context/config/configSlice'; +import configReducer from '@src/context/config/configSlice'; +import initialConfigState from '../initialConfigState'; +import { MOCK_BUILD_KITE_VERIFY_RESPONSE, PIPELINE_TOOL_TYPES } from '../fixtures'; +import { setupStore } from '../utils/setupStoreUtil'; describe('pipelineTool reducer', () => { const MOCK_PIPElINE_TOOL_VERIFY_RESPONSE = { @@ -26,7 +26,7 @@ describe('pipelineTool reducer', () => { steps: ['step1', 'step2'], }, ], - } + }; const MOCK_PIPElINE_TOOL_VERIFY_RESPONSE_SORT = { pipelineList: [ @@ -47,30 +47,30 @@ describe('pipelineTool reducer', () => { steps: ['step3', 'step4'], }, ], - } + }; const MOCK_DATE_RANGE = { startDate: '2023-04-04T00:00:00+08:00', endDate: '2023-04-18T00:00:00+08:00', - } + }; it('should set isPipelineToolVerified false when handle initial state', () => { - const result = configReducer(undefined, { type: 'unknown' }) + const result = configReducer(undefined, { type: 'unknown' }); - expect(result.pipelineTool.isVerified).toEqual(false) - }) + expect(result.pipelineTool.isVerified).toEqual(false); + }); it('should set isPipelineToolVerified true when handle updatePipelineToolVerifyState given isPipelineToolVerified is true', () => { - const result = configReducer(initialConfigState, updatePipelineToolVerifyState(true)) + const result = configReducer(initialConfigState, updatePipelineToolVerifyState(true)); - expect(result.pipelineTool.isVerified).toEqual(true) - }) + expect(result.pipelineTool.isVerified).toEqual(true); + }); it('should update pipelineTool fields when change pipelineTool fields input', () => { - const config = configReducer(initialConfigState, updatePipelineTool({ token: 'abcd' })) + const config = configReducer(initialConfigState, updatePipelineTool({ token: 'abcd' })); - expect(config.pipelineTool.config.token).toEqual('abcd') - }) + expect(config.pipelineTool.config.token).toEqual('abcd'); + }); it('should update pipelineList when get pipelineTool steps given pipelineList and right params', () => { const mockConfigStateHasPipelineList = { @@ -97,16 +97,16 @@ describe('pipelineTool reducer', () => { pipelineCrews: [], }, }, - } + }; const mockParams = { organization: MOCK_BUILD_KITE_VERIFY_RESPONSE.pipelineList[0].orgName, pipelineName: MOCK_BUILD_KITE_VERIFY_RESPONSE.pipelineList[0].name, steps: ['mock steps'], - } + }; const pipelineVerifiedResponse = configReducer( mockConfigStateHasPipelineList, updatePipelineToolVerifyResponseSteps(mockParams) - ) + ); expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([ { @@ -117,8 +117,8 @@ describe('pipelineTool reducer', () => { repository: 'mock repository url', steps: ['mock steps'], }, - ]) - }) + ]); + }); it('should not update pipelineList when get pipelineTool steps given pipelineList and wrong params', () => { const mockConfigStateHasPipelineList = { @@ -145,16 +145,16 @@ describe('pipelineTool reducer', () => { pipelineCrews: [], }, }, - } + }; const mockParams = { organization: '', pipelineName: '', steps: ['mock steps'], - } + }; const pipelineVerifiedResponse = configReducer( mockConfigStateHasPipelineList, updatePipelineToolVerifyResponseSteps(mockParams) - ) + ); expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([ { @@ -166,35 +166,35 @@ describe('pipelineTool reducer', () => { steps: [], branches: [], }, - ]) - }) + ]); + }); it('should return empty pipelineList when get pipelineTool steps given pipelineList is empty', () => { const mockParams = { organization: MOCK_BUILD_KITE_VERIFY_RESPONSE.pipelineList[0].orgName, pipelineName: MOCK_BUILD_KITE_VERIFY_RESPONSE.pipelineList[0].name, steps: ['mock steps'], - } + }; const pipelineVerifiedResponse = configReducer( initialConfigState, updatePipelineToolVerifyResponseSteps(mockParams) - ) + ); - expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([]) - }) + expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([]); + }); describe('pipelineToolVerifyResponse reducer', () => { it('should show empty array when handle initial state', () => { - const pipelineVerifiedResponse = configReducer(undefined, { type: 'unknown' }) + const pipelineVerifiedResponse = configReducer(undefined, { type: 'unknown' }); - expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([]) - }) + expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([]); + }); it('should store pipelineTool data when get network pipelineTool verify response', () => { const pipelineVerifiedResponse = configReducer( initialConfigState, updatePipelineToolVerifyResponse(MOCK_BUILD_KITE_VERIFY_RESPONSE) - ) + ); expect(pipelineVerifiedResponse.pipelineTool.verifiedResponse.pipelineList).toEqual([ { @@ -205,31 +205,31 @@ describe('pipelineTool reducer', () => { repository: 'mock repository url', steps: [], }, - ]) - }) - }) + ]); + }); + }); describe('selectPipelineNames', () => { it('should return PipelineNames when call selectPipelineNames function', async () => { - const store = setupStore() - await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)) - expect(selectPipelineNames(store.getState(), 'mockOrgName')).toEqual(['mockName']) - }) + const store = setupStore(); + await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)); + expect(selectPipelineNames(store.getState(), 'mockOrgName')).toEqual(['mockName']); + }); it('should sort PipelineNames when call selectPipelineNames function', async () => { - const store = setupStore() - await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE_SORT)) - expect(selectPipelineNames(store.getState(), 'mockOrgName')).toEqual(['mockName', 'MockName']) - }) - }) + const store = setupStore(); + await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE_SORT)); + expect(selectPipelineNames(store.getState(), 'mockOrgName')).toEqual(['mockName', 'MockName']); + }); + }); describe('selectStepsParams', () => { - let store = setupStore() + let store = setupStore(); beforeEach(async () => { - store = setupStore() - await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)) - await store.dispatch(updateDateRange(MOCK_DATE_RANGE)) - }) + store = setupStore(); + await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)); + await store.dispatch(updateDateRange(MOCK_DATE_RANGE)); + }); it('should return true StepsParams when call selectStepsParams function given right organization name and pipeline name', async () => { expect(selectStepsParams(store.getState(), 'mockOrgName', 'mockName')).toEqual({ @@ -244,8 +244,8 @@ describe('pipelineTool reducer', () => { }, pipelineType: 'BuildKite', token: '', - }) - }) + }); + }); it('should return StepsParams when call selectStepsParams function given empty organization name and empty pipeline name', async () => { expect(selectStepsParams(store.getState(), '', '')).toEqual({ @@ -260,23 +260,23 @@ describe('pipelineTool reducer', () => { }, pipelineType: 'BuildKite', token: '', - }) - }) - }) + }); + }); + }); describe('selectSteps', () => { it('should return steps when call selectSteps function', async () => { - const store = setupStore() - await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)) - expect(selectSteps(store.getState(), 'mockOrgName', 'mockName')).toEqual([]) - }) - }) + const store = setupStore(); + await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)); + expect(selectSteps(store.getState(), 'mockOrgName', 'mockName')).toEqual([]); + }); + }); describe('selectPipelineOrganizations', () => { it('should return organizations when call selectPipelineOrganizations function', async () => { - const store = setupStore() - await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)) - expect(selectPipelineOrganizations(store.getState())).toEqual(['mockOrgName']) - }) - }) -}) + const store = setupStore(); + await store.dispatch(updatePipelineToolVerifyResponse(MOCK_PIPElINE_TOOL_VERIFY_RESPONSE)); + expect(selectPipelineOrganizations(store.getState())).toEqual(['mockOrgName']); + }); + }); +}); diff --git a/frontend/__tests__/src/context/sourceControlSlice.test.ts b/frontend/__tests__/src/context/sourceControlSlice.test.ts index a292f76a89..6f37a64be5 100644 --- a/frontend/__tests__/src/context/sourceControlSlice.test.ts +++ b/frontend/__tests__/src/context/sourceControlSlice.test.ts @@ -2,45 +2,45 @@ import sourceControlReducer, { updateSourceControl, updateSourceControlVerifiedResponse, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import { MOCK_GITHUB_VERIFY_RESPONSE } from '../fixtures' -import initialConfigState from '../initialConfigState' +} from '@src/context/config/configSlice'; +import { MOCK_GITHUB_VERIFY_RESPONSE } from '../fixtures'; +import initialConfigState from '../initialConfigState'; describe('sourceControl reducer', () => { it('should set isSourceControlVerified false when handle initial state', () => { - const sourceControl = sourceControlReducer(undefined, { type: 'unknown' }) + const sourceControl = sourceControlReducer(undefined, { type: 'unknown' }); - expect(sourceControl.sourceControl.isVerified).toEqual(false) - }) + expect(sourceControl.sourceControl.isVerified).toEqual(false); + }); it('should return true when handle changeSourceControlVerifyState given isSourceControlVerified is true', () => { - const sourceControl = sourceControlReducer(initialConfigState, updateSourceControlVerifyState(true)) + const sourceControl = sourceControlReducer(initialConfigState, updateSourceControlVerifyState(true)); - expect(sourceControl.sourceControl.isVerified).toEqual(true) - }) + expect(sourceControl.sourceControl.isVerified).toEqual(true); + }); it('should update sourceControl fields when change sourceControl fields input', () => { - const sourceControl = sourceControlReducer(initialConfigState, updateSourceControl({ token: 'token' })) + const sourceControl = sourceControlReducer(initialConfigState, updateSourceControl({ token: 'token' })); - expect(sourceControl.sourceControl.config.token).toEqual('token') - }) + expect(sourceControl.sourceControl.config.token).toEqual('token'); + }); describe('sourceControlVerifyResponse reducer', () => { it('should show empty array when handle initial state', () => { - const sourceControlVerifyResponse = sourceControlReducer(undefined, { type: 'unknown' }) + const sourceControlVerifyResponse = sourceControlReducer(undefined, { type: 'unknown' }); - expect(sourceControlVerifyResponse.sourceControl.verifiedResponse.repoList).toEqual([]) - }) + expect(sourceControlVerifyResponse.sourceControl.verifiedResponse.repoList).toEqual([]); + }); it('should store sourceControl data when get network sourceControl verify response', () => { const sourceControlResponse = sourceControlReducer( initialConfigState, updateSourceControlVerifiedResponse(MOCK_GITHUB_VERIFY_RESPONSE) - ) + ); expect(sourceControlResponse.sourceControl.verifiedResponse.repoList).toEqual( MOCK_GITHUB_VERIFY_RESPONSE.githubRepos - ) - }) - }) -}) + ); + }); + }); +}); diff --git a/frontend/__tests__/src/context/stepperSlice.test.ts b/frontend/__tests__/src/context/stepperSlice.test.ts index 3bfd6e07a8..2815f4097a 100644 --- a/frontend/__tests__/src/context/stepperSlice.test.ts +++ b/frontend/__tests__/src/context/stepperSlice.test.ts @@ -1,19 +1,19 @@ -import stepperReducer, { backStep, nextStep, resetStep, updateTimeStamp } from '@src/context/stepper/StepperSlice' -import { ZERO } from '../fixtures' +import stepperReducer, { backStep, nextStep, resetStep, updateTimeStamp } from '@src/context/stepper/StepperSlice'; +import { ZERO } from '../fixtures'; describe('stepper reducer', () => { it('should get 0 when handle initial state', () => { - const stepper = stepperReducer(undefined, { type: 'unknown' }) + const stepper = stepperReducer(undefined, { type: 'unknown' }); - expect(stepper.stepNumber).toEqual(ZERO) - }) + expect(stepper.stepNumber).toEqual(ZERO); + }); it('should reset to 0 when handle reset', () => { - const stepper = stepperReducer(undefined, resetStep) + const stepper = stepperReducer(undefined, resetStep); - expect(stepper.stepNumber).toEqual(ZERO) - expect(stepper.timeStamp).toEqual(ZERO) - }) + expect(stepper.stepNumber).toEqual(ZERO); + expect(stepper.timeStamp).toEqual(ZERO); + }); it('should get 1 when handle next step given stepNumber is 0', () => { const stepper = stepperReducer( @@ -22,10 +22,10 @@ describe('stepper reducer', () => { timeStamp: 0, }, nextStep() - ) + ); - expect(stepper.stepNumber).toEqual(1) - }) + expect(stepper.stepNumber).toEqual(1); + }); it('should get 0 when handle back step given stepNumber is 0', () => { const stepper = stepperReducer( @@ -34,10 +34,10 @@ describe('stepper reducer', () => { timeStamp: 0, }, backStep() - ) + ); - expect(stepper.stepNumber).toEqual(ZERO) - }) + expect(stepper.stepNumber).toEqual(ZERO); + }); it('should get 1 when handle back step given stepNumber is 2', () => { const stepper = stepperReducer( @@ -46,21 +46,21 @@ describe('stepper reducer', () => { timeStamp: 0, }, backStep() - ) + ); - expect(stepper.stepNumber).toEqual(1) - }) + expect(stepper.stepNumber).toEqual(1); + }); it('should get current time when handle updateTimeStamp', () => { - const mockTime = new Date().getTime() + const mockTime = new Date().getTime(); const stepper = stepperReducer( { stepNumber: 2, timeStamp: 0, }, updateTimeStamp(mockTime) - ) + ); - expect(stepper.timeStamp).toEqual(mockTime) - }) -}) + expect(stepper.timeStamp).toEqual(mockTime); + }); +}); diff --git a/frontend/__tests__/src/emojis/emoji.test.tsx b/frontend/__tests__/src/emojis/emoji.test.tsx index d558aa7fe3..eaef32ee18 100644 --- a/frontend/__tests__/src/emojis/emoji.test.tsx +++ b/frontend/__tests__/src/emojis/emoji.test.tsx @@ -1,45 +1,45 @@ -import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji' +import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji'; jest.mock('@src/utils/util', () => ({ transformToCleanedBuildKiteEmoji: jest.fn().mockReturnValue([ { image: 'abc1.png', aliases: ['zap1'] }, { image: 'abc2.png', aliases: ['zap2'] }, ]), -})) +})); describe('#emojis', () => { describe('#getEmojiUrls', () => { - const EMOJI_URL_PREFIX = 'https://buildkiteassets.com/emojis/' + const EMOJI_URL_PREFIX = 'https://buildkiteassets.com/emojis/'; it('should get empty images when can not parse name from input', () => { - const mockPipelineStepName = 'one emojis' + const mockPipelineStepName = 'one emojis'; - expect(getEmojiUrls(mockPipelineStepName)).toEqual([]) - }) + expect(getEmojiUrls(mockPipelineStepName)).toEqual([]); + }); it('should get default image url when can not match any emoji', () => { - const mockPipelineStepName = ':zap: one emojis' + const mockPipelineStepName = ':zap: one emojis'; - expect(getEmojiUrls(mockPipelineStepName)).toEqual([`${EMOJI_URL_PREFIX}img-buildkite-64/buildkite.png`]) - }) + expect(getEmojiUrls(mockPipelineStepName)).toEqual([`${EMOJI_URL_PREFIX}img-buildkite-64/buildkite.png`]); + }); it('should get single emoji image', () => { - const mockPipelineStepName = ':zap1: one emojis' + const mockPipelineStepName = ':zap1: one emojis'; - expect(getEmojiUrls(mockPipelineStepName)).toEqual([`${EMOJI_URL_PREFIX}abc1.png`]) - }) + expect(getEmojiUrls(mockPipelineStepName)).toEqual([`${EMOJI_URL_PREFIX}abc1.png`]); + }); it('should get multi-emoji images', () => { - const input = ':zap1: :zap2:one emojis' + const input = ':zap1: :zap2:one emojis'; - expect(getEmojiUrls(input)).toEqual([`${EMOJI_URL_PREFIX}abc1.png`, `${EMOJI_URL_PREFIX}abc2.png`]) - }) - }) + expect(getEmojiUrls(input)).toEqual([`${EMOJI_URL_PREFIX}abc1.png`, `${EMOJI_URL_PREFIX}abc2.png`]); + }); + }); describe('#removeExtraEmojiName', () => { it('should remove extra emojis names', () => { - const input = ':zap: :www:one emojis' + const input = ':zap: :www:one emojis'; - expect(removeExtraEmojiName(input)).toEqual(' one emojis') - }) - }) -}) + expect(removeExtraEmojiName(input)).toEqual(' one emojis'); + }); + }); +}); diff --git a/frontend/__tests__/src/fileConfig/fileConfig.test.ts b/frontend/__tests__/src/fileConfig/fileConfig.test.ts index a268771859..e86799ac9b 100644 --- a/frontend/__tests__/src/fileConfig/fileConfig.test.ts +++ b/frontend/__tests__/src/fileConfig/fileConfig.test.ts @@ -1,10 +1,10 @@ -import { convertToNewFileConfig } from '@src/fileConfig/fileConfig' +import { convertToNewFileConfig } from '@src/fileConfig/fileConfig'; import { IMPORTED_NEW_CONFIG_FIXTURE, BASIC_IMPORTED_OLD_CONFIG_FIXTURE, REGULAR_CALENDAR, CHINA_CALENDAR, -} from '../fixtures' +} from '../fixtures'; describe('#fileConfig', () => { const BASIC_NEW_CONFIG = { @@ -43,35 +43,35 @@ describe('#fileConfig', () => { organization: 'Thoughtworks-Heartbeat', }, ], - } + }; it('should return original config when it is not old config', () => { - expect(convertToNewFileConfig(IMPORTED_NEW_CONFIG_FIXTURE)).toEqual(IMPORTED_NEW_CONFIG_FIXTURE) - }) + expect(convertToNewFileConfig(IMPORTED_NEW_CONFIG_FIXTURE)).toEqual(IMPORTED_NEW_CONFIG_FIXTURE); + }); it('should convert to new config when it is old config and considerHoliday is false', () => { const expected = { ...BASIC_NEW_CONFIG, calendarType: REGULAR_CALENDAR, - } + }; expect( convertToNewFileConfig({ ...BASIC_IMPORTED_OLD_CONFIG_FIXTURE, considerHoliday: false, }) - ).toEqual(expected) - }) + ).toEqual(expected); + }); it('should convert to new config when it is old config and considerHoliday is true', () => { const expected = { ...BASIC_NEW_CONFIG, calendarType: CHINA_CALENDAR, - } + }; expect( convertToNewFileConfig({ ...BASIC_IMPORTED_OLD_CONFIG_FIXTURE, considerHoliday: true, }) - ).toEqual(expected) - }) -}) + ).toEqual(expected); + }); +}); diff --git a/frontend/__tests__/src/fixtures.ts b/frontend/__tests__/src/fixtures.ts index f9c4df1083..a094c5c754 100644 --- a/frontend/__tests__/src/fixtures.ts +++ b/frontend/__tests__/src/fixtures.ts @@ -1,43 +1,43 @@ -import { CSVReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' -import { ReportResponseDTO } from '@src/clients/report/dto/response' +import { CSVReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; -export const PROJECT_NAME = 'Heartbeat' +export const PROJECT_NAME = 'Heartbeat'; export const PROJECT_DESCRIPTION = - 'Heartbeat is a tool for tracking project delivery metrics that can help you get a better understanding of delivery performance. This product allows you easily get all aspects of source data faster and more accurate to analyze team delivery performance which enables delivery teams and team leaders focusing on driving continuous improvement and enhancing team productivity and efficiency.' + 'Heartbeat is a tool for tracking project delivery metrics that can help you get a better understanding of delivery performance. This product allows you easily get all aspects of source data faster and more accurate to analyze team delivery performance which enables delivery teams and team leaders focusing on driving continuous improvement and enhancing team productivity and efficiency.'; -export const ZERO = 0 +export const ZERO = 0; -export const REGULAR_CALENDAR = 'Regular Calendar(Weekend Considered)' +export const REGULAR_CALENDAR = 'Regular Calendar(Weekend Considered)'; -export const CHINA_CALENDAR = 'Calendar with Chinese Holiday' +export const CHINA_CALENDAR = 'Calendar with Chinese Holiday'; -export const NEXT = 'Next' +export const NEXT = 'Next'; -export const PREVIOUS = 'Previous' +export const PREVIOUS = 'Previous'; -export const SAVE = 'Save' +export const SAVE = 'Save'; -export const SHOW_MORE = 'show more >' +export const SHOW_MORE = 'show more >'; -export const BACK = 'Back' +export const BACK = 'Back'; -export const VERIFY = 'Verify' +export const VERIFY = 'Verify'; -export const RESET = 'Reset' +export const RESET = 'Reset'; -export const EXPORT_PIPELINE_DATA = 'Export pipeline data' +export const EXPORT_PIPELINE_DATA = 'Export pipeline data'; -export const EXPORT_BOARD_DATA = 'Export board data' +export const EXPORT_BOARD_DATA = 'Export board data'; -export const EXPORT_METRIC_DATA = 'Export metric data' +export const EXPORT_METRIC_DATA = 'Export metric data'; -export const VERIFIED = 'Verified' +export const VERIFIED = 'Verified'; -export const TOKEN_ERROR_MESSAGE = ['Token is invalid', 'Token is required'] +export const TOKEN_ERROR_MESSAGE = ['Token is invalid', 'Token is required']; -export const PROJECT_NAME_LABEL = 'Project name' +export const PROJECT_NAME_LABEL = 'Project name'; -export const STEPPER = ['Config', 'Metrics', 'Report'] +export const STEPPER = ['Config', 'Metrics', 'Report']; export const REQUIRED_DATA_LIST = [ 'All', @@ -48,36 +48,36 @@ export const REQUIRED_DATA_LIST = [ 'Deployment frequency', 'Change failure rate', 'Mean time to recovery', -] -export const ALL = 'All' -export const VELOCITY = 'Velocity' -export const CYCLE_TIME = 'Cycle time' -export const CLASSIFICATION = 'Classification' -export const LEAD_TIME_FOR_CHANGES = 'Lead time for changes' -export const DEPLOYMENT_FREQUENCY = 'Deployment frequency' -export const CHANGE_FAILURE_RATE = 'Change failure rate' -export const MEAN_TIME_TO_RECOVERY = 'Mean time to recovery' -export const REQUIRED_DATA = 'Required metrics' -export const TEST_PROJECT_NAME = 'test project Name' -export const ERROR_MESSAGE_COLOR = 'color: #d32f2f' -export const ERROR_DATE = '02/03/' -export const CREATE_NEW_PROJECT = 'Create a new project' -export const IMPORT_PROJECT_FROM_FILE = 'Import project from file' -export const EXPORT_EXPIRED_CSV_MESSAGE = 'The report has been expired, please generate it again' +]; +export const ALL = 'All'; +export const VELOCITY = 'Velocity'; +export const CYCLE_TIME = 'Cycle time'; +export const CLASSIFICATION = 'Classification'; +export const LEAD_TIME_FOR_CHANGES = 'Lead time for changes'; +export const DEPLOYMENT_FREQUENCY = 'Deployment frequency'; +export const CHANGE_FAILURE_RATE = 'Change failure rate'; +export const MEAN_TIME_TO_RECOVERY = 'Mean time to recovery'; +export const REQUIRED_DATA = 'Required metrics'; +export const TEST_PROJECT_NAME = 'test project Name'; +export const ERROR_MESSAGE_COLOR = 'color: #d32f2f'; +export const ERROR_DATE = '02/03/'; +export const CREATE_NEW_PROJECT = 'Create a new project'; +export const IMPORT_PROJECT_FROM_FILE = 'Import project from file'; +export const EXPORT_EXPIRED_CSV_MESSAGE = 'The report has been expired, please generate it again'; export const BOARD_TYPES = { CLASSIC_JIRA: 'Classic Jira', JIRA: 'Jira', -} +}; export const PIPELINE_TOOL_TYPES = { BUILD_KITE: 'BuildKite', GO_CD: 'GoCD', -} +}; export const SOURCE_CONTROL_TYPES = { GITHUB: 'GitHub', -} +}; export enum CONFIG_TITLE { BOARD = 'Board', @@ -85,22 +85,22 @@ export enum CONFIG_TITLE { SOURCE_CONTROL = 'Source Control', } -export const BOARD_FIELDS = ['Board', 'Board Id', 'Email', 'Project Key', 'Site', 'Token'] -export const PIPELINE_TOOL_FIELDS = ['Pipeline Tool', 'Token'] -export const SOURCE_CONTROL_FIELDS = ['Source Control', 'Token'] +export const BOARD_FIELDS = ['Board', 'Board Id', 'Email', 'Project Key', 'Site', 'Token']; +export const PIPELINE_TOOL_FIELDS = ['Pipeline Tool', 'Token']; +export const SOURCE_CONTROL_FIELDS = ['Source Control', 'Token']; -export const BASE_URL = 'api/v1' -export const MOCK_BOARD_URL_FOR_JIRA = `${BASE_URL}/boards/jira` -export const MOCK_BOARD_URL_FOR_CLASSIC_JIRA = `${BASE_URL}/boards/classic-jira` -export const MOCK_PIPELINE_URL = `${BASE_URL}/pipelines/buildkite` -export const MOCK_SOURCE_CONTROL_URL = `${BASE_URL}/source-control` -export const MOCK_REPORT_URL = `${BASE_URL}/reports` -export const MOCK_VERSION_URL = `${BASE_URL}/version` -export const MOCK_EXPORT_CSV_URL = `${BASE_URL}/reports/:dataType/:fileName` +export const BASE_URL = 'api/v1'; +export const MOCK_BOARD_URL_FOR_JIRA = `${BASE_URL}/boards/jira`; +export const MOCK_BOARD_URL_FOR_CLASSIC_JIRA = `${BASE_URL}/boards/classic-jira`; +export const MOCK_PIPELINE_URL = `${BASE_URL}/pipelines/buildkite`; +export const MOCK_SOURCE_CONTROL_URL = `${BASE_URL}/source-control`; +export const MOCK_REPORT_URL = `${BASE_URL}/reports`; +export const MOCK_VERSION_URL = `${BASE_URL}/version`; +export const MOCK_EXPORT_CSV_URL = `${BASE_URL}/reports/:dataType/:fileName`; export const VERSION_RESPONSE = { version: '1.11', -} +}; export enum VERIFY_ERROR_MESSAGE { BAD_REQUEST = 'Please reconfirm the input', @@ -112,7 +112,7 @@ export enum VERIFY_ERROR_MESSAGE { UNKNOWN = 'Unknown', } -export const VERIFY_FAILED = 'verify failed' +export const VERIFY_FAILED = 'verify failed'; export const MOCK_BOARD_VERIFY_REQUEST_PARAMS = { token: 'mockToken', @@ -122,7 +122,7 @@ export const MOCK_BOARD_VERIFY_REQUEST_PARAMS = { startTime: 1613664000000, endTime: 1614873600000, boardId: '1', -} +}; export const MOCK_CLASSIC_JIRA_BOARD_VERIFY_REQUEST_PARAMS = { token: 'mockToken', @@ -132,21 +132,21 @@ export const MOCK_CLASSIC_JIRA_BOARD_VERIFY_REQUEST_PARAMS = { startTime: 1613664000000, endTime: 1614873600000, boardId: '2', -} +}; export const MOCK_PIPELINE_VERIFY_REQUEST_PARAMS = { token: 'mockToken', type: PIPELINE_TOOL_TYPES.BUILD_KITE, startTime: 1613664000000, endTime: 1614873600000, -} +}; export const MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS = { token: 'mockToken', type: SOURCE_CONTROL_TYPES.GITHUB, startTime: 1613664000000, endTime: 1614873600000, -} +}; export const MOCK_GENERATE_REPORT_REQUEST_PARAMS: ReportRequestDTO = { metrics: [], @@ -197,7 +197,7 @@ export const MOCK_GENERATE_REPORT_REQUEST_PARAMS: ReportRequestDTO = { targetFields: [{ key: 'parent', name: 'Parent', flag: false }], doneColumn: ['Done'], }, -} +}; export const IMPORTED_NEW_CONFIG_FIXTURE = { projectName: 'ConfigFileForImporting', @@ -235,14 +235,14 @@ export const IMPORTED_NEW_CONFIG_FIXTURE = { 'Ready For Dev': 'Analysis', }, ], -} +}; export const MOCK_EXPORT_CSV_REQUEST_PARAMS: CSVReportRequestDTO = { csvTimeStamp: 1613664000000, dataType: 'pipeline', startDate: IMPORTED_NEW_CONFIG_FIXTURE.dateRange.startDate, endDate: IMPORTED_NEW_CONFIG_FIXTURE.dateRange.endDate, -} +}; export const MOCK_IMPORT_FILE = { projectName: 'Mock Project Name', @@ -252,7 +252,7 @@ export const MOCK_IMPORT_FILE = { endDate: '2023-03-29T16:00:00.000Z', }, metrics: [], -} +}; export const MOCK_JIRA_VERIFY_RESPONSE = { jiraColumns: [ @@ -283,7 +283,7 @@ export const MOCK_JIRA_VERIFY_RESPONSE = { { key: 'customfield_10017', name: 'Issue color', flag: false }, { key: 'customfield_10027', name: 'Feature/Operation', flag: false }, ], -} +}; export const MOCK_BUILD_KITE_VERIFY_RESPONSE = { pipelineList: [ @@ -296,7 +296,7 @@ export const MOCK_BUILD_KITE_VERIFY_RESPONSE = { steps: [], }, ], -} +}; export const FILTER_CYCLE_TIME_SETTINGS = [ { name: 'TODO', value: 'TODO' }, @@ -304,34 +304,34 @@ export const FILTER_CYCLE_TIME_SETTINGS = [ { name: 'IN DEV', value: 'IN DEV' }, { name: 'DOING', value: 'IN DEV' }, { name: 'DONE', value: 'DONE' }, -] +]; export const MOCK_CYCLE_TIME_SETTING = [ { name: 'TODO', value: 'TODO' }, { name: 'IN DEV', value: 'IN DEV' }, { name: 'DONE', value: 'DONE' }, -] +]; export const MOCK_JIRA_WITH_STATUES_SETTING = [ { name: 'TODO', statuses: ['TODO', 'BACKLOG'] }, { name: 'IN DEV', statuses: ['IN DEV', 'DOING'] }, { name: 'DONE', statuses: ['DONE'] }, -] +]; export const MOCK_GITHUB_VERIFY_RESPONSE = { githubRepos: ['https://github.com/xxxx1/repo1', 'https://github.com/xxxx1/repo2'], -} +}; -export const CREWS_SETTING = 'Crew settings' -export const CYCLE_TIME_SETTINGS = 'Cycle time settings' -export const CLASSIFICATION_SETTING = 'Classification setting' -export const REAL_DONE = 'Real done setting' -export const DEPLOYMENT_FREQUENCY_SETTINGS = 'Pipeline settings' +export const CREWS_SETTING = 'Crew settings'; +export const CYCLE_TIME_SETTINGS = 'Cycle time settings'; +export const CLASSIFICATION_SETTING = 'Classification setting'; +export const REAL_DONE = 'Real done setting'; +export const DEPLOYMENT_FREQUENCY_SETTINGS = 'Pipeline settings'; export enum PIPELINE_SETTING_TYPES { DEPLOYMENT_FREQUENCY_SETTINGS_TYPE = 'DeploymentFrequencySettings', } -export const CONFIRM_DIALOG_DESCRIPTION = 'All the filled data will be cleared. Continue to Home page?' +export const CONFIRM_DIALOG_DESCRIPTION = 'All the filled data will be cleared. Continue to Home page?'; export const MOCK_GET_STEPS_PARAMS = { params: { @@ -345,17 +345,17 @@ export const MOCK_GET_STEPS_PARAMS = { organizationId: 'mockOrganizationId', pipelineType: 'BuildKite', token: 'mockToken', -} +}; -export const REMOVE_BUTTON = 'Remove' -export const ORGANIZATION = 'Organization' -export const PIPELINE_NAME = 'Pipeline Name' -export const STEP = 'Step' -export const BRANCH = 'Branches' +export const REMOVE_BUTTON = 'Remove'; +export const ORGANIZATION = 'Organization'; +export const PIPELINE_NAME = 'Pipeline Name'; +export const STEP = 'Step'; +export const BRANCH = 'Branches'; -export const PR_LEAD_TIME = 'PR Lead Time' -export const PIPELINE_LEAD_TIME = 'Pipeline Lead Time' -export const TOTAL_DELAY_TIME = 'Total Lead Time' +export const PR_LEAD_TIME = 'PR Lead Time'; +export const PIPELINE_LEAD_TIME = 'Pipeline Lead Time'; +export const TOTAL_DELAY_TIME = 'Total Lead Time'; export const MOCK_REPORT_RESPONSE: ReportResponseDTO = { velocity: { @@ -473,12 +473,12 @@ export const MOCK_REPORT_RESPONSE: ReportResponseDTO = { isPipelineMetricsReady: true, isSourceControlMetricsReady: true, isAllMetricsReady: true, -} +}; export const MOCK_RETRIEVE_REPORT_RESPONSE = { callbackUrl: 'reports/123', interval: 10, -} +}; export const EXPECTED_REPORT_VALUES = { velocityList: [ @@ -622,7 +622,7 @@ export const EXPECTED_REPORT_VALUES = { }, ], exportValidityTimeMin: 30, -} +}; export const EMPTY_REPORT_VALUES: ReportResponseDTO = { velocity: null, @@ -637,10 +637,10 @@ export const EMPTY_REPORT_VALUES: ReportResponseDTO = { isPipelineMetricsReady: false, isSourceControlMetricsReady: false, isAllMetricsReady: false, -} +}; export const CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE = - 'Imported data is not perfectly matched. Please review carefully before going next!' + 'Imported data is not perfectly matched. Please review carefully before going next!'; export const BASIC_IMPORTED_OLD_CONFIG_FIXTURE = { projectName: 'ConfigFileForImporting', @@ -684,40 +684,40 @@ export const BASIC_IMPORTED_OLD_CONFIG_FIXTURE = { orgId: 'Thoughtworks-Heartbeat', }, ], -} +}; -export const ERROR_MESSAGE_TIME_DURATION = 4000 -export const CLASSIFICATION_WARNING_MESSAGE = `Some classifications in import data might be removed.` +export const ERROR_MESSAGE_TIME_DURATION = 4000; +export const CLASSIFICATION_WARNING_MESSAGE = `Some classifications in import data might be removed.`; export const HOME_VERIFY_IMPORT_WARNING_MESSAGE = - 'The content of the imported JSON file is empty. Please confirm carefully' + 'The content of the imported JSON file is empty. Please confirm carefully'; -export const INTERNAL_SERVER_ERROR_MESSAGE = 'Internal Server Error' +export const INTERNAL_SERVER_ERROR_MESSAGE = 'Internal Server Error'; -export const BASE_PAGE_ROUTE = '/' +export const BASE_PAGE_ROUTE = '/'; -export const ERROR_PAGE_ROUTE = '/error-page' +export const ERROR_PAGE_ROUTE = '/error-page'; -export const METRICS_PAGE_ROUTE = '/metrics' +export const METRICS_PAGE_ROUTE = '/metrics'; export const ERROR_PAGE_MESSAGE = - 'Something on internet is not quite right. Perhaps head back to our homepage and try again.' + 'Something on internet is not quite right. Perhaps head back to our homepage and try again.'; -export const RETRY_BUTTON = 'Go to homepage' +export const RETRY_BUTTON = 'Go to homepage'; export const NO_CARD_ERROR_MESSAGE = - 'Sorry there is no card within selected date range, please change your collection date!' + 'Sorry there is no card within selected date range, please change your collection date!'; -export const LIST_OPEN = 'Open' +export const LIST_OPEN = 'Open'; -export const NO_RESULT_DASH = '----' +export const NO_RESULT_DASH = '----'; -export const MOCK_AUTOCOMPLETE_LIST = ['Option 1', 'Option 2', 'Option 3'] +export const MOCK_AUTOCOMPLETE_LIST = ['Option 1', 'Option 2', 'Option 3']; -export const AUTOCOMPLETE_SELECT_ACTION = 'selectOption' +export const AUTOCOMPLETE_SELECT_ACTION = 'selectOption'; -export const TIME_DISPLAY_TITTLE_START = 'START' -export const TIME_DISPLAY_TITTLE_END = 'END' -export const CYCLE_TIME_SETTINGS_SECTION = 'Cycle time settings section' -export const REAL_DONE_SETTING_SECTION = 'Real done setting section' -export const SELECT_CONSIDER_AS_DONE_MESSAGE = 'Must select which you want to consider as Done' +export const TIME_DISPLAY_TITTLE_START = 'START'; +export const TIME_DISPLAY_TITTLE_END = 'END'; +export const CYCLE_TIME_SETTINGS_SECTION = 'Cycle time settings section'; +export const REAL_DONE_SETTING_SECTION = 'Real done setting section'; +export const SELECT_CONSIDER_AS_DONE_MESSAGE = 'Must select which you want to consider as Done'; diff --git a/frontend/__tests__/src/hooks/reportMapper/changeFailureRate.test.tsx b/frontend/__tests__/src/hooks/reportMapper/changeFailureRate.test.tsx index b7a7b2e1a4..91511818d6 100644 --- a/frontend/__tests__/src/hooks/reportMapper/changeFailureRate.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/changeFailureRate.test.tsx @@ -1,4 +1,4 @@ -import { changeFailureRateMapper } from '@src/hooks/reportMapper/changeFailureRate' +import { changeFailureRateMapper } from '@src/hooks/reportMapper/changeFailureRate'; describe('change failure rate data mapper', () => { const mockChangeFailureRateRes = { @@ -17,7 +17,7 @@ describe('change failure rate data mapper', () => { failureRate: 0.0, }, ], - } + }; it('maps response change failure rate values to ui display value', () => { const expectedChangeFailureRateValues = [ { @@ -40,9 +40,9 @@ describe('change failure rate data mapper', () => { }, ], }, - ] - const mappedChangeFailureRate = changeFailureRateMapper(mockChangeFailureRateRes) + ]; + const mappedChangeFailureRate = changeFailureRateMapper(mockChangeFailureRateRes); - expect(mappedChangeFailureRate).toEqual(expectedChangeFailureRateValues) - }) -}) + expect(mappedChangeFailureRate).toEqual(expectedChangeFailureRateValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/classfication.test.tsx b/frontend/__tests__/src/hooks/reportMapper/classfication.test.tsx index 4f374f975a..b4227a4b0d 100644 --- a/frontend/__tests__/src/hooks/reportMapper/classfication.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/classfication.test.tsx @@ -1,4 +1,4 @@ -import { classificationMapper } from '@src/hooks/reportMapper/classification' +import { classificationMapper } from '@src/hooks/reportMapper/classification'; describe('classification data mapper', () => { const mockClassificationRes = [ @@ -19,7 +19,7 @@ describe('classification data mapper', () => { }, ], }, - ] + ]; it('maps response Classification values to ui display value', () => { const expectedClassificationValues = [ { @@ -31,9 +31,9 @@ describe('classification data mapper', () => { { name: 'Feature Work - Unplanned', value: '7.14%' }, ], }, - ] - const mappedClassifications = classificationMapper(mockClassificationRes) + ]; + const mappedClassifications = classificationMapper(mockClassificationRes); - expect(mappedClassifications).toEqual(expectedClassificationValues) - }) -}) + expect(mappedClassifications).toEqual(expectedClassificationValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/cycleTime.test.tsx b/frontend/__tests__/src/hooks/reportMapper/cycleTime.test.tsx index df54c82e4e..ef86c576d1 100644 --- a/frontend/__tests__/src/hooks/reportMapper/cycleTime.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/cycleTime.test.tsx @@ -1,4 +1,4 @@ -import { cycleTimeMapper } from '@src/hooks/reportMapper/cycleTime' +import { cycleTimeMapper } from '@src/hooks/reportMapper/cycleTime'; describe('cycleTime data mapper', () => { const mockCycleTimeRes = { @@ -19,7 +19,7 @@ describe('cycleTime data mapper', () => { totalTime: 3.21, }, ], - } + }; it('maps response cycleTime values to ui display value', () => { const expectedCycleValues = [ { @@ -48,9 +48,9 @@ describe('cycleTime data mapper', () => { { value: '0.23', unit: '(days/card)' }, ], }, - ] - const mappedCycleValues = cycleTimeMapper(mockCycleTimeRes) + ]; + const mappedCycleValues = cycleTimeMapper(mockCycleTimeRes); - expect(mappedCycleValues).toEqual(expectedCycleValues) - }) -}) + expect(mappedCycleValues).toEqual(expectedCycleValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/deploymentFrequency.test.tsx b/frontend/__tests__/src/hooks/reportMapper/deploymentFrequency.test.tsx index 71ba5f3e7f..3e6827c646 100644 --- a/frontend/__tests__/src/hooks/reportMapper/deploymentFrequency.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/deploymentFrequency.test.tsx @@ -1,4 +1,4 @@ -import { deploymentFrequencyMapper } from '@src/hooks/reportMapper/deploymentFrequency' +import { deploymentFrequencyMapper } from '@src/hooks/reportMapper/deploymentFrequency'; describe('deployment frequency data mapper', () => { const mockDeploymentFrequencyRes = { @@ -27,7 +27,7 @@ describe('deployment frequency data mapper', () => { ], }, ], - } + }; it('maps response deployment frequency values to ui display value', () => { const expectedDeploymentFrequencyValues = [ { @@ -50,9 +50,9 @@ describe('deployment frequency data mapper', () => { }, ], }, - ] - const mappedDeploymentFrequency = deploymentFrequencyMapper(mockDeploymentFrequencyRes) + ]; + const mappedDeploymentFrequency = deploymentFrequencyMapper(mockDeploymentFrequencyRes); - expect(mappedDeploymentFrequency).toEqual(expectedDeploymentFrequencyValues) - }) -}) + expect(mappedDeploymentFrequency).toEqual(expectedDeploymentFrequencyValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/exportValidityTime.test.tsx b/frontend/__tests__/src/hooks/reportMapper/exportValidityTime.test.tsx index 6217b79847..36a435f49f 100644 --- a/frontend/__tests__/src/hooks/reportMapper/exportValidityTime.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/exportValidityTime.test.tsx @@ -1,15 +1,15 @@ -import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' +import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime'; describe('export validity time mapper', () => { it('should return 30 when call exportValidityTimeMapper given the param to 1800000', () => { - const result = exportValidityTimeMapper(1800000) + const result = exportValidityTimeMapper(1800000); - expect(result).toEqual(30) - }) + expect(result).toEqual(30); + }); it('should return null when call exportValidityTimeMapper given the param to null', () => { - const result = exportValidityTimeMapper(null) + const result = exportValidityTimeMapper(null); - expect(result).toEqual(null) - }) -}) + expect(result).toEqual(null); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/leadTimeForChanges.test.tsx b/frontend/__tests__/src/hooks/reportMapper/leadTimeForChanges.test.tsx index 611a2bcaf7..27fc3d8f15 100644 --- a/frontend/__tests__/src/hooks/reportMapper/leadTimeForChanges.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/leadTimeForChanges.test.tsx @@ -1,5 +1,5 @@ -import { PIPELINE_LEAD_TIME, PR_LEAD_TIME, TOTAL_DELAY_TIME } from '../../fixtures' -import { leadTimeForChangesMapper } from '@src/hooks/reportMapper/leadTimeForChanges' +import { PIPELINE_LEAD_TIME, PR_LEAD_TIME, TOTAL_DELAY_TIME } from '../../fixtures'; +import { leadTimeForChangesMapper } from '@src/hooks/reportMapper/leadTimeForChanges'; describe('lead time for changes data mapper', () => { const mockLeadTimeForChangesRes = { @@ -18,7 +18,7 @@ describe('lead time for changes data mapper', () => { pipelineLeadTime: 4167.97, totalDelayTime: 18313.579999999998, }, - } + }; it('maps response lead time for changes values to ui display value', () => { const expectedLeadTimeForChangesValues = [ { @@ -57,11 +57,11 @@ describe('lead time for changes data mapper', () => { }, ], }, - ] - const mappedLeadTimeForChanges = leadTimeForChangesMapper(mockLeadTimeForChangesRes) + ]; + const mappedLeadTimeForChanges = leadTimeForChangesMapper(mockLeadTimeForChangesRes); - expect(mappedLeadTimeForChanges).toEqual(expectedLeadTimeForChangesValues) - }) + expect(mappedLeadTimeForChanges).toEqual(expectedLeadTimeForChangesValues); + }); it('should map time to 0 minute when it is 0', () => { const mockLeadTimeForChangesResMock = { @@ -80,7 +80,7 @@ describe('lead time for changes data mapper', () => { pipelineLeadTime: 0, totalDelayTime: 0, }, - } + }; const expectedLeadTimeForChangesValues = [ { @@ -101,9 +101,9 @@ describe('lead time for changes data mapper', () => { { name: TOTAL_DELAY_TIME, value: '0.00' }, ], }, - ] - const mappedLeadTimeForChanges = leadTimeForChangesMapper(mockLeadTimeForChangesResMock) + ]; + const mappedLeadTimeForChanges = leadTimeForChangesMapper(mockLeadTimeForChangesResMock); - expect(mappedLeadTimeForChanges).toEqual(expectedLeadTimeForChangesValues) - }) -}) + expect(mappedLeadTimeForChanges).toEqual(expectedLeadTimeForChangesValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/meanTimeToRecovery.test.tsx b/frontend/__tests__/src/hooks/reportMapper/meanTimeToRecovery.test.tsx index 2325cd7cfe..98b6ff3029 100644 --- a/frontend/__tests__/src/hooks/reportMapper/meanTimeToRecovery.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/meanTimeToRecovery.test.tsx @@ -1,4 +1,4 @@ -import { meanTimeToRecoveryMapper } from '@src/hooks/reportMapper/meanTimeToRecovery' +import { meanTimeToRecoveryMapper } from '@src/hooks/reportMapper/meanTimeToRecovery'; describe('mean time to recovery data mapper', () => { const mockMeanTimeToRecovery = { @@ -13,7 +13,7 @@ describe('mean time to recovery data mapper', () => { timeToRecovery: 162120031.8, }, ], - } + }; it('maps response change failure rate values to ui display value', () => { const expectedMeanTimeToRecovery = [ { @@ -36,11 +36,11 @@ describe('mean time to recovery data mapper', () => { }, ], }, - ] - const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery) + ]; + const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery); - expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery) - }) + expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery); + }); it('should format time when timeToRecovery is greater than 0 but less than 1', () => { const mockMeanTimeToRecovery = { @@ -55,7 +55,7 @@ describe('mean time to recovery data mapper', () => { timeToRecovery: 0.32, }, ], - } + }; const expectedMeanTimeToRecovery = [ { id: 0, @@ -77,11 +77,11 @@ describe('mean time to recovery data mapper', () => { }, ], }, - ] - const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery) + ]; + const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery); - expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery) - }) + expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery); + }); it('should map time to 0 minute when it is 0', () => { const mockMeanTimeToRecovery = { @@ -96,7 +96,7 @@ describe('mean time to recovery data mapper', () => { timeToRecovery: 0, }, ], - } + }; const expectedMeanTimeToRecovery = [ { id: 0, @@ -118,9 +118,9 @@ describe('mean time to recovery data mapper', () => { }, ], }, - ] - const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery) + ]; + const mappedMeanTimeToRecovery = meanTimeToRecoveryMapper(mockMeanTimeToRecovery); - expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery) - }) -}) + expect(mappedMeanTimeToRecovery).toEqual(expectedMeanTimeToRecovery); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/report.test.tsx b/frontend/__tests__/src/hooks/reportMapper/report.test.tsx index 8cf8339baf..a72ffdd2c9 100644 --- a/frontend/__tests__/src/hooks/reportMapper/report.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/report.test.tsx @@ -1,10 +1,10 @@ -import { reportMapper } from '@src/hooks/reportMapper/report' -import { EXPECTED_REPORT_VALUES, MOCK_REPORT_RESPONSE } from '../../fixtures' +import { reportMapper } from '@src/hooks/reportMapper/report'; +import { EXPECTED_REPORT_VALUES, MOCK_REPORT_RESPONSE } from '../../fixtures'; describe('report response data mapper', () => { it('maps response velocity values to ui display value', () => { - const mappedReportResponseValues = reportMapper(MOCK_REPORT_RESPONSE) + const mappedReportResponseValues = reportMapper(MOCK_REPORT_RESPONSE); - expect(mappedReportResponseValues).toEqual(EXPECTED_REPORT_VALUES) - }) -}) + expect(mappedReportResponseValues).toEqual(EXPECTED_REPORT_VALUES); + }); +}); diff --git a/frontend/__tests__/src/hooks/reportMapper/velocity.test.tsx b/frontend/__tests__/src/hooks/reportMapper/velocity.test.tsx index f6d75ec869..0c18ed1645 100644 --- a/frontend/__tests__/src/hooks/reportMapper/velocity.test.tsx +++ b/frontend/__tests__/src/hooks/reportMapper/velocity.test.tsx @@ -1,18 +1,18 @@ -import { velocityMapper } from '@src/hooks/reportMapper/velocity' +import { velocityMapper } from '@src/hooks/reportMapper/velocity'; describe('velocity data mapper', () => { it('maps response velocity values to ui display value', () => { const mockVelocityRes = { velocityForSP: 20, velocityForCards: 15, - } + }; const expectedVelocityValues = [ { id: 0, name: 'Velocity(Story Point)', valueList: [{ value: 20 }] }, { id: 1, name: 'Throughput(Cards Count)', valueList: [{ value: 15 }] }, - ] + ]; - const mappedVelocityValues = velocityMapper(mockVelocityRes) + const mappedVelocityValues = velocityMapper(mockVelocityRes); - expect(mappedVelocityValues).toEqual(expectedVelocityValues) - }) -}) + expect(mappedVelocityValues).toEqual(expectedVelocityValues); + }); +}); diff --git a/frontend/__tests__/src/hooks/useExportCsvEffect.test.tsx b/frontend/__tests__/src/hooks/useExportCsvEffect.test.tsx index d0dfaca730..dd09ea54e5 100644 --- a/frontend/__tests__/src/hooks/useExportCsvEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useExportCsvEffect.test.tsx @@ -1,54 +1,54 @@ -import { act, renderHook } from '@testing-library/react' -import { csvClient } from '@src/clients/report/CSVClient' -import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -import { ERROR_MESSAGE_TIME_DURATION, MOCK_EXPORT_CSV_REQUEST_PARAMS } from '../fixtures' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { NotFoundException } from '@src/exceptions/NotFoundException' -import { HttpStatusCode } from 'axios' +import { act, renderHook } from '@testing-library/react'; +import { csvClient } from '@src/clients/report/CSVClient'; +import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect'; +import { ERROR_MESSAGE_TIME_DURATION, MOCK_EXPORT_CSV_REQUEST_PARAMS } from '../fixtures'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { HttpStatusCode } from 'axios'; describe('use export csv effect', () => { afterEach(() => { - jest.resetAllMocks() - }) + jest.resetAllMocks(); + }); it('should set error message empty when export csv throw error and last for 4 seconds', async () => { - jest.useFakeTimers() + jest.useFakeTimers(); csvClient.exportCSVData = jest.fn().mockImplementation(() => { - throw new Error('error') - }) - const { result } = renderHook(() => useExportCsvEffect()) + throw new Error('error'); + }); + const { result } = renderHook(() => useExportCsvEffect()); act(() => { - result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS) - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS); + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); - expect(result.current.errorMessage).toEqual('') - }) + expect(result.current.errorMessage).toEqual(''); + }); it('should set error message when export csv response status 500', async () => { csvClient.exportCSVData = jest.fn().mockImplementation(() => { - throw new InternalServerException('error message', HttpStatusCode.InternalServerError) - }) - const { result } = renderHook(() => useExportCsvEffect()) + throw new InternalServerException('error message', HttpStatusCode.InternalServerError); + }); + const { result } = renderHook(() => useExportCsvEffect()); act(() => { - result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS) - }) + result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS); + }); - expect(result.current.errorMessage).toEqual('failed to export csv: error message') - }) + expect(result.current.errorMessage).toEqual('failed to export csv: error message'); + }); it('should set error message when export csv response status 404', async () => { csvClient.exportCSVData = jest.fn().mockImplementation(() => { - throw new NotFoundException('error message', HttpStatusCode.NotFound) - }) - const { result } = renderHook(() => useExportCsvEffect()) + throw new NotFoundException('error message', HttpStatusCode.NotFound); + }); + const { result } = renderHook(() => useExportCsvEffect()); act(() => { - result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS) - }) + result.current.fetchExportData(MOCK_EXPORT_CSV_REQUEST_PARAMS); + }); - expect(result.current.isExpired).toEqual(true) - }) -}) + expect(result.current.isExpired).toEqual(true); + }); +}); diff --git a/frontend/__tests__/src/hooks/useGenerateReportEffect.test.tsx b/frontend/__tests__/src/hooks/useGenerateReportEffect.test.tsx index 26adf0e76e..b94fb8c24b 100644 --- a/frontend/__tests__/src/hooks/useGenerateReportEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useGenerateReportEffect.test.tsx @@ -1,360 +1,360 @@ -import { act, renderHook, waitFor } from '@testing-library/react' -import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' +import { act, renderHook, waitFor } from '@testing-library/react'; +import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect'; import { ERROR_MESSAGE_TIME_DURATION, INTERNAL_SERVER_ERROR_MESSAGE, MOCK_GENERATE_REPORT_REQUEST_PARAMS, MOCK_REPORT_RESPONSE, MOCK_RETRIEVE_REPORT_RESPONSE, -} from '../fixtures' -import { reportClient } from '@src/clients/report/ReportClient' -import { NotFoundException } from '@src/exceptions/NotFoundException' -import { UnknownException } from '@src/exceptions/UnkonwException' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { HttpStatusCode } from 'axios' -import clearAllMocks = jest.clearAllMocks -import resetAllMocks = jest.resetAllMocks +} from '../fixtures'; +import { reportClient } from '@src/clients/report/ReportClient'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { UnknownException } from '@src/exceptions/UnkonwException'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { HttpStatusCode } from 'axios'; +import clearAllMocks = jest.clearAllMocks; +import resetAllMocks = jest.resetAllMocks; jest.mock('@src/hooks/reportMapper/report', () => ({ pipelineReportMapper: jest.fn(), sourceControlReportMapper: jest.fn(), -})) +})); describe('use generate report effect', () => { afterAll(() => { - clearAllMocks() - }) + clearAllMocks(); + }); beforeEach(() => { - jest.useFakeTimers() - }) + jest.useFakeTimers(); + }); afterEach(() => { - resetAllMocks() - jest.useRealTimers() - }) + resetAllMocks(); + jest.useRealTimers(); + }); it('should set error message when generate report throw error', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new Error('error') - }) + throw new Error('error'); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: error') - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: error'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should set error message when generate report response status 404', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new NotFoundException('error message', HttpStatusCode.NotFound) - }) + throw new NotFoundException('error message', HttpStatusCode.NotFound); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: error message') - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: error message'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should set error message when generate report response status 500', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new InternalServerException(INTERNAL_SERVER_ERROR_MESSAGE, HttpStatusCode.InternalServerError) - }) + throw new InternalServerException(INTERNAL_SERVER_ERROR_MESSAGE, HttpStatusCode.InternalServerError); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should set isServerError is true when throw unknownException', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new UnknownException() - }) + throw new UnknownException(); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should return error message when calling startToRequestBoardData given pollingReport response return 5xx ', async () => { reportClient.pollingReport = jest.fn().mockImplementation(async () => { - throw new InternalServerException('error', HttpStatusCode.InternalServerError) - }) + throw new InternalServerException('error', HttpStatusCode.InternalServerError); + }); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(reportClient.pollingReport).toBeCalledTimes(1) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(reportClient.pollingReport).toBeCalledTimes(1); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should return error message when calling startToRequestBoardData given pollingReport response return 4xx ', async () => { reportClient.pollingReport = jest.fn().mockImplementation(async () => { - throw new NotFoundException('file not found', HttpStatusCode.NotFound) - }) + throw new NotFoundException('file not found', HttpStatusCode.NotFound); + }); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) - result.current.stopPollingReports = jest.fn() + const { result } = renderHook(() => useGenerateReportEffect()); + result.current.stopPollingReports = jest.fn(); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: file not found') - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: file not found'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should call polling report and setTimeout when calling startToRequestBoardData given pollingReport response return 204 ', async () => { reportClient.pollingReport = jest .fn() - .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })) + .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + }); - jest.runOnlyPendingTimers() + jest.runOnlyPendingTimers(); await waitFor(() => { - expect(reportClient.pollingReport).toHaveBeenCalledTimes(1) - }) - }) + expect(reportClient.pollingReport).toHaveBeenCalledTimes(1); + }); + }); it('should call polling report more than one time when allMetricsReady field in response is false', async () => { reportClient.pollingReport = jest.fn().mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: { ...MOCK_REPORT_RESPONSE, isAllMetricsReady: false }, - })) + })); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + }); act(() => { - jest.advanceTimersByTime(10000) - }) + jest.advanceTimersByTime(10000); + }); await waitFor(() => { - expect(reportClient.pollingReport).toHaveBeenCalledTimes(2) - }) - }) + expect(reportClient.pollingReport).toHaveBeenCalledTimes(2); + }); + }); it('should call polling report only once when calling startToRequestBoardData but startToRequestDoraData called before', async () => { reportClient.pollingReport = jest .fn() - .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })) + .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + }); - jest.runOnlyPendingTimers() + jest.runOnlyPendingTimers(); await waitFor(() => { - expect(reportClient.pollingReport).toHaveBeenCalledTimes(1) - }) - }) + expect(reportClient.pollingReport).toHaveBeenCalledTimes(1); + }); + }); it('should set error message when generate report throw error', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new Error('error') - }) + throw new Error('error'); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: error') - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: error'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should set error message when generate report response status 404', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new NotFoundException('error message', HttpStatusCode.NotFound) - }) + throw new NotFoundException('error message', HttpStatusCode.NotFound); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: error message') - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: error message'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should set error message when generate report response status 500', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new InternalServerException(INTERNAL_SERVER_ERROR_MESSAGE, HttpStatusCode.NotFound) - }) + throw new InternalServerException(INTERNAL_SERVER_ERROR_MESSAGE, HttpStatusCode.NotFound); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should set isServerError is true when throw unknownException', async () => { reportClient.retrieveReportByUrl = jest.fn().mockImplementation(async () => { - throw new UnknownException() - }) + throw new UnknownException(); + }); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should return error message when calling startToRequestDoraData given pollingReport response return 5xx ', async () => { reportClient.pollingReport = jest.fn().mockImplementation(async () => { - throw new InternalServerException('error', HttpStatusCode.InternalServerError) - }) + throw new InternalServerException('error', HttpStatusCode.InternalServerError); + }); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(reportClient.pollingReport).toBeCalledTimes(1) - expect(result.current.isServerError).toEqual(true) - }) - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(reportClient.pollingReport).toBeCalledTimes(1); + expect(result.current.isServerError).toEqual(true); + }); + }); it('should return error message when calling startToRequestDoraData given pollingReport response return 4xx ', async () => { reportClient.pollingReport = jest.fn().mockImplementation(async () => { - throw new NotFoundException('file not found', HttpStatusCode.NotFound) - }) + throw new NotFoundException('file not found', HttpStatusCode.NotFound); + }); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) - result.current.stopPollingReports = jest.fn() + const { result } = renderHook(() => useGenerateReportEffect()); + result.current.stopPollingReports = jest.fn(); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - expect(result.current.errorMessage).toEqual('generate report: file not found') - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + expect(result.current.errorMessage).toEqual('generate report: file not found'); + }); act(() => { - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); await waitFor(() => { - expect(result.current.errorMessage).toEqual('') - }) - }) + expect(result.current.errorMessage).toEqual(''); + }); + }); it('should call polling report and setTimeout when calling startToRequestDoraData given pollingReport response return 204 ', async () => { reportClient.pollingReport = jest .fn() - .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })) + .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - }) + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + }); - jest.runOnlyPendingTimers() + jest.runOnlyPendingTimers(); await waitFor(() => { - expect(reportClient.pollingReport).toHaveBeenCalledTimes(1) - }) - }) + expect(reportClient.pollingReport).toHaveBeenCalledTimes(1); + }); + }); it('should call polling report only once when calling startToRequestDoraData but startToRequestBoardData called before', async () => { reportClient.pollingReport = jest .fn() - .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })) + .mockImplementation(async () => ({ status: HttpStatusCode.NoContent, response: MOCK_REPORT_RESPONSE })); reportClient.retrieveReportByUrl = jest .fn() - .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })) + .mockImplementation(async () => ({ response: MOCK_RETRIEVE_REPORT_RESPONSE })); - const { result } = renderHook(() => useGenerateReportEffect()) + const { result } = renderHook(() => useGenerateReportEffect()); await waitFor(() => { - result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS) - }) + result.current.startToRequestBoardData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + result.current.startToRequestDoraData(MOCK_GENERATE_REPORT_REQUEST_PARAMS); + }); - jest.runOnlyPendingTimers() + jest.runOnlyPendingTimers(); await waitFor(() => { - expect(reportClient.pollingReport).toHaveBeenCalledTimes(1) - }) - }) -}) + expect(reportClient.pollingReport).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/frontend/__tests__/src/hooks/useGetMetricsStepsEffect.test.tsx b/frontend/__tests__/src/hooks/useGetMetricsStepsEffect.test.tsx index 49d1457054..2c139ea3fa 100644 --- a/frontend/__tests__/src/hooks/useGetMetricsStepsEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useGetMetricsStepsEffect.test.tsx @@ -1,45 +1,45 @@ -import { act, renderHook } from '@testing-library/react' -import { useGetMetricsStepsEffect } from '@src/hooks/useGetMetricsStepsEffect' -import { metricsClient } from '@src/clients/MetricsClient' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { ERROR_MESSAGE_TIME_DURATION, MOCK_GET_STEPS_PARAMS } from '../fixtures' -import { HttpStatusCode } from 'axios' +import { act, renderHook } from '@testing-library/react'; +import { useGetMetricsStepsEffect } from '@src/hooks/useGetMetricsStepsEffect'; +import { metricsClient } from '@src/clients/MetricsClient'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { ERROR_MESSAGE_TIME_DURATION, MOCK_GET_STEPS_PARAMS } from '../fixtures'; +import { HttpStatusCode } from 'axios'; describe('use get steps effect', () => { - const { params, buildId, organizationId, pipelineType, token } = MOCK_GET_STEPS_PARAMS + const { params, buildId, organizationId, pipelineType, token } = MOCK_GET_STEPS_PARAMS; it('should init data state when render hook', async () => { - const { result } = renderHook(() => useGetMetricsStepsEffect()) + const { result } = renderHook(() => useGetMetricsStepsEffect()); - expect(result.current.isLoading).toEqual(false) - }) + expect(result.current.isLoading).toEqual(false); + }); it('should set error message when get steps throw error', async () => { - jest.useFakeTimers() + jest.useFakeTimers(); metricsClient.getSteps = jest.fn().mockImplementation(() => { - throw new Error('error') - }) - const { result } = renderHook(() => useGetMetricsStepsEffect()) + throw new Error('error'); + }); + const { result } = renderHook(() => useGetMetricsStepsEffect()); - expect(result.current.isLoading).toEqual(false) + expect(result.current.isLoading).toEqual(false); act(() => { - result.current.getSteps(params, buildId, organizationId, pipelineType, token) - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + result.current.getSteps(params, buildId, organizationId, pipelineType, token); + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); - expect(result.current.errorMessage).toEqual('') - }) + expect(result.current.errorMessage).toEqual(''); + }); it('should set error message when get steps response status 500', async () => { metricsClient.getSteps = jest.fn().mockImplementation(() => { - throw new InternalServerException('error message', HttpStatusCode.InternalServerError) - }) - const { result } = renderHook(() => useGetMetricsStepsEffect()) + throw new InternalServerException('error message', HttpStatusCode.InternalServerError); + }); + const { result } = renderHook(() => useGetMetricsStepsEffect()); act(() => { - result.current.getSteps(params, buildId, organizationId, pipelineType, token) - }) + result.current.getSteps(params, buildId, organizationId, pipelineType, token); + }); - expect(result.current.errorMessage).toEqual('BuildKite get steps failed: error message') - }) -}) + expect(result.current.errorMessage).toEqual('BuildKite get steps failed: error message'); + }); +}); diff --git a/frontend/__tests__/src/hooks/useMetricsStepValidationCheckContext.test.tsx b/frontend/__tests__/src/hooks/useMetricsStepValidationCheckContext.test.tsx index 0fcb7d258e..3f4407bab8 100644 --- a/frontend/__tests__/src/hooks/useMetricsStepValidationCheckContext.test.tsx +++ b/frontend/__tests__/src/hooks/useMetricsStepValidationCheckContext.test.tsx @@ -1,14 +1,14 @@ -import { act, renderHook } from '@testing-library/react' -import { ContextProvider, useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext' -import React from 'react' -import { addADeploymentFrequencySetting, updateDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice' -import { Provider } from 'react-redux' -import { setupStore } from '../utils/setupStoreUtil' -import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore' +import { act, renderHook } from '@testing-library/react'; +import { ContextProvider, useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext'; +import React from 'react'; +import { addADeploymentFrequencySetting, updateDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice'; +import { Provider } from 'react-redux'; +import { setupStore } from '../utils/setupStoreUtil'; +import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore'; describe('useMetricsStepValidationCheckContext', () => { - const DEPLOYMENT_FREQUENCY_SETTINGS = 'DeploymentFrequencySettings' - const LEAD_TIME_FOR_CHANGES = 'LeadTimeForChanges' + const DEPLOYMENT_FREQUENCY_SETTINGS = 'DeploymentFrequencySettings'; + const LEAD_TIME_FOR_CHANGES = 'LeadTimeForChanges'; const duplicatedData = [ { @@ -21,92 +21,92 @@ describe('useMetricsStepValidationCheckContext', () => { { updateId: 1, label: 'organization', value: 'mockOrganization' }, { updateId: 1, label: 'pipelineName', value: 'mockPipelineName' }, { updateId: 1, label: 'step', value: 'mockstep' }, - ] + ]; const setup = () => { - const store = setupStore() + const store = setupStore(); const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} - ) - const { result } = renderHook(() => useMetricsStepValidationCheckContext(), { wrapper }) + ); + const { result } = renderHook(() => useMetricsStepValidationCheckContext(), { wrapper }); - return { result, store } - } + return { result, store }; + }; const setDuplicatedDataToStore = (store: ToolkitStore) => { - store.dispatch(addADeploymentFrequencySetting()) + store.dispatch(addADeploymentFrequencySetting()); duplicatedData.map((data) => { - store.dispatch(updateDeploymentFrequencySettings(data)) - }) - } + store.dispatch(updateDeploymentFrequencySettings(data)); + }); + }; it('should return initial ValidationContext ', () => { - const { result } = renderHook(() => useMetricsStepValidationCheckContext()) + const { result } = renderHook(() => useMetricsStepValidationCheckContext()); - expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false) - expect(result.current?.isPipelineValid(LEAD_TIME_FOR_CHANGES)).toBe(false) + expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false); + expect(result.current?.isPipelineValid(LEAD_TIME_FOR_CHANGES)).toBe(false); expect( result.current?.getDuplicatedPipeLineIds([{ id: 1, organization: '', pipelineName: '', step: '', branches: [] }]) - ).toEqual([]) - }) + ).toEqual([]); + }); it('should return useMetricsStepValidationCheckContext correctly ', () => { - const { result } = setup() + const { result } = setup(); - expect(result.current?.isPipelineValid).toBeInstanceOf(Function) - }) + expect(result.current?.isPipelineValid).toBeInstanceOf(Function); + }); it('should return true when call isPipelineValid given valid data', () => { - const { result, store } = setup() + const { result, store } = setup(); act(() => { store.dispatch( updateDeploymentFrequencySettings({ updateId: 0, label: 'organization', value: 'mockOrganization' }) - ) + ); store.dispatch( updateDeploymentFrequencySettings({ updateId: 0, label: 'pipelineName', value: 'mockPipelineName' }) - ) - store.dispatch(updateDeploymentFrequencySettings({ updateId: 0, label: 'step', value: 'mockstep' })) - }) + ); + store.dispatch(updateDeploymentFrequencySettings({ updateId: 0, label: 'step', value: 'mockstep' })); + }); act(() => { - expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(true) - }) - }) + expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(true); + }); + }); it('should return false when call isPipelineValid given duplicated data', () => { - const { result, store } = setup() + const { result, store } = setup(); act(() => { - setDuplicatedDataToStore(store) - }) + setDuplicatedDataToStore(store); + }); act(() => { - expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false) - }) - }) + expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false); + }); + }); it('should return false when call isPipelineValid given empty data', () => { - const { result } = setup() + const { result } = setup(); act(() => { - expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false) - }) - }) + expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false); + }); + }); it('multiple situation test', async () => { - const { result, store } = setup() + const { result, store } = setup(); act(() => { - setDuplicatedDataToStore(store) - store.dispatch(addADeploymentFrequencySetting()) - }) + setDuplicatedDataToStore(store); + store.dispatch(addADeploymentFrequencySetting()); + }); act(() => { - expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false) - }) + expect(result.current?.isPipelineValid(DEPLOYMENT_FREQUENCY_SETTINGS)).toBe(false); + }); act(() => { - store.dispatch(addADeploymentFrequencySetting()) - }) - }) -}) + store.dispatch(addADeploymentFrequencySetting()); + }); + }); +}); diff --git a/frontend/__tests__/src/hooks/useNotificationLayoutEffect.test.tsx b/frontend/__tests__/src/hooks/useNotificationLayoutEffect.test.tsx index 9db07332ba..9c4ba1a883 100644 --- a/frontend/__tests__/src/hooks/useNotificationLayoutEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useNotificationLayoutEffect.test.tsx @@ -1,60 +1,60 @@ -import { act, renderHook, waitFor } from '@testing-library/react' -import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect' -import clearAllMocks = jest.clearAllMocks -import { DURATION } from '@src/constants/commons' +import { act, renderHook, waitFor } from '@testing-library/react'; +import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect'; +import clearAllMocks = jest.clearAllMocks; +import { DURATION } from '@src/constants/commons'; describe('useNotificationLayoutEffect', () => { afterAll(() => { - clearAllMocks() - }) + clearAllMocks(); + }); const defaultProps = { title: '', open: false, closeAutomatically: false, durationTimeout: DURATION.NOTIFICATION_TIME, - } + }; it('should init the state of notificationProps when render hook', async () => { - const { result } = renderHook(() => useNotificationLayoutEffect()) + const { result } = renderHook(() => useNotificationLayoutEffect()); - expect(result.current.notificationProps).toEqual(defaultProps) - }) + expect(result.current.notificationProps).toEqual(defaultProps); + }); it('should reset the notificationProps when call resetProps given mock props', async () => { - const mockProps = { title: 'Test', open: true, closeAutomatically: false } - const { result } = renderHook(() => useNotificationLayoutEffect()) + const mockProps = { title: 'Test', open: true, closeAutomatically: false }; + const { result } = renderHook(() => useNotificationLayoutEffect()); act(() => { - result.current.notificationProps = mockProps - result.current.resetProps?.() - }) + result.current.notificationProps = mockProps; + result.current.resetProps?.(); + }); - expect(result.current.notificationProps).toEqual(defaultProps) - }) + expect(result.current.notificationProps).toEqual(defaultProps); + }); it('should update the notificationProps when call resetProps given mock props', async () => { - const mockProps = { title: 'Test', open: true, closeAutomatically: false } - const { result } = renderHook(() => useNotificationLayoutEffect()) + const mockProps = { title: 'Test', open: true, closeAutomatically: false }; + const { result } = renderHook(() => useNotificationLayoutEffect()); act(() => { - result.current.notificationProps = defaultProps - result.current.updateProps?.(mockProps) - }) + result.current.notificationProps = defaultProps; + result.current.updateProps?.(mockProps); + }); - expect(result.current.notificationProps).toEqual(mockProps) - }) + expect(result.current.notificationProps).toEqual(mockProps); + }); it('should reset the notificationProps when update the value of closeAutomatically given closeAutomatically equals to true', async () => { - jest.useFakeTimers() - const mockProps = { title: 'Test', open: true, closeAutomatically: true } - const { result } = renderHook(() => useNotificationLayoutEffect()) + jest.useFakeTimers(); + const mockProps = { title: 'Test', open: true, closeAutomatically: true }; + const { result } = renderHook(() => useNotificationLayoutEffect()); act(() => { - result.current.notificationProps = defaultProps - result.current.updateProps?.(mockProps) - }) + result.current.notificationProps = defaultProps; + result.current.updateProps?.(mockProps); + }); act(() => { - jest.advanceTimersByTime(DURATION.NOTIFICATION_TIME) - }) + jest.advanceTimersByTime(DURATION.NOTIFICATION_TIME); + }); await waitFor(() => { expect(result.current.notificationProps).toEqual({ @@ -62,42 +62,42 @@ describe('useNotificationLayoutEffect', () => { title: '', closeAutomatically: false, durationTimeout: DURATION.NOTIFICATION_TIME, - }) - }) + }); + }); - jest.useRealTimers() - }) + jest.useRealTimers(); + }); it('should reset the notificationProps after 5s when update the value of closeAutomatically given durationTimeout equals to 5s', async () => { - jest.useFakeTimers() - const { result } = renderHook(() => useNotificationLayoutEffect()) - const expectedTime = 5000 - const mockProps = { title: 'Test', open: true, closeAutomatically: true, durationTimeout: expectedTime } + jest.useFakeTimers(); + const { result } = renderHook(() => useNotificationLayoutEffect()); + const expectedTime = 5000; + const mockProps = { title: 'Test', open: true, closeAutomatically: true, durationTimeout: expectedTime }; const expectedProps = { open: false, title: '', closeAutomatically: false, durationTimeout: DURATION.NOTIFICATION_TIME, - } + }; act(() => { - result.current.notificationProps = defaultProps - result.current.updateProps?.(mockProps) - }) + result.current.notificationProps = defaultProps; + result.current.updateProps?.(mockProps); + }); - jest.advanceTimersByTime(1000) + jest.advanceTimersByTime(1000); await waitFor(() => { - expect(result.current.notificationProps).not.toEqual(expectedProps) - }) + expect(result.current.notificationProps).not.toEqual(expectedProps); + }); act(() => { - jest.advanceTimersByTime(expectedTime) - }) + jest.advanceTimersByTime(expectedTime); + }); await waitFor(() => { - expect(result.current.notificationProps).toEqual(expectedProps) - }) + expect(result.current.notificationProps).toEqual(expectedProps); + }); - jest.useRealTimers() - }) -}) + jest.useRealTimers(); + }); +}); diff --git a/frontend/__tests__/src/hooks/useVerifyBoardEffect.test.tsx b/frontend/__tests__/src/hooks/useVerifyBoardEffect.test.tsx index be63591fef..d9ef94e670 100644 --- a/frontend/__tests__/src/hooks/useVerifyBoardEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useVerifyBoardEffect.test.tsx @@ -1,44 +1,44 @@ -import { act, renderHook } from '@testing-library/react' -import { useVerifyBoardEffect } from '@src/hooks/useVerifyBoardEffect' -import { boardClient } from '@src/clients/board/BoardClient' -import { ERROR_MESSAGE_TIME_DURATION, MOCK_BOARD_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { HttpStatusCode } from 'axios' +import { act, renderHook } from '@testing-library/react'; +import { useVerifyBoardEffect } from '@src/hooks/useVerifyBoardEffect'; +import { boardClient } from '@src/clients/board/BoardClient'; +import { ERROR_MESSAGE_TIME_DURATION, MOCK_BOARD_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { HttpStatusCode } from 'axios'; describe('use verify board state', () => { it('should initial data state when render hook', async () => { - const { result } = renderHook(() => useVerifyBoardEffect()) + const { result } = renderHook(() => useVerifyBoardEffect()); - expect(result.current.isLoading).toEqual(false) - }) + expect(result.current.isLoading).toEqual(false); + }); it('should set error message when get verify board throw error', async () => { - jest.useFakeTimers() + jest.useFakeTimers(); boardClient.getVerifyBoard = jest.fn().mockImplementation(() => { - throw new Error('error') - }) - const { result } = renderHook(() => useVerifyBoardEffect()) + throw new Error('error'); + }); + const { result } = renderHook(() => useVerifyBoardEffect()); - expect(result.current.isLoading).toEqual(false) + expect(result.current.isLoading).toEqual(false); act(() => { - result.current.verifyJira(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + result.current.verifyJira(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); - expect(result.current.errorMessage).toEqual('') - }) + expect(result.current.errorMessage).toEqual(''); + }); it('should set error message when get verify board response status 500', async () => { boardClient.getVerifyBoard = jest.fn().mockImplementation(() => { - throw new InternalServerException('error message', HttpStatusCode.InternalServerError) - }) - const { result } = renderHook(() => useVerifyBoardEffect()) + throw new InternalServerException('error message', HttpStatusCode.InternalServerError); + }); + const { result } = renderHook(() => useVerifyBoardEffect()); act(() => { - result.current.verifyJira(MOCK_BOARD_VERIFY_REQUEST_PARAMS) - }) + result.current.verifyJira(MOCK_BOARD_VERIFY_REQUEST_PARAMS); + }); expect(result.current.errorMessage).toEqual( `${MOCK_BOARD_VERIFY_REQUEST_PARAMS.type} ${VERIFY_FAILED}: error message` - ) - }) -}) + ); + }); +}); diff --git a/frontend/__tests__/src/hooks/useVerifyPipelineToolEffect.test.tsx b/frontend/__tests__/src/hooks/useVerifyPipelineToolEffect.test.tsx index b886f708ef..aabdfa7920 100644 --- a/frontend/__tests__/src/hooks/useVerifyPipelineToolEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useVerifyPipelineToolEffect.test.tsx @@ -1,45 +1,45 @@ -import { act, renderHook } from '@testing-library/react' -import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect' -import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient' -import { ERROR_MESSAGE_TIME_DURATION, MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { HttpStatusCode } from 'axios' +import { act, renderHook } from '@testing-library/react'; +import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect'; +import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient'; +import { ERROR_MESSAGE_TIME_DURATION, MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { HttpStatusCode } from 'axios'; describe('use verify pipelineTool state', () => { it('should initial data state when render hook', async () => { - const { result } = renderHook(() => useVerifyPipelineToolEffect()) + const { result } = renderHook(() => useVerifyPipelineToolEffect()); - expect(result.current.isLoading).toEqual(false) - }) + expect(result.current.isLoading).toEqual(false); + }); it('should set error message when get verify pipelineTool throw error', async () => { - jest.useFakeTimers() + jest.useFakeTimers(); pipelineToolClient.verifyPipelineTool = jest.fn().mockImplementation(() => { - throw new Error('error') - }) - const { result } = renderHook(() => useVerifyPipelineToolEffect()) + throw new Error('error'); + }); + const { result } = renderHook(() => useVerifyPipelineToolEffect()); - expect(result.current.isLoading).toEqual(false) + expect(result.current.isLoading).toEqual(false); act(() => { - result.current.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS) - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + result.current.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); - expect(result.current.errorMessage).toEqual('') - }) + expect(result.current.errorMessage).toEqual(''); + }); it('should set error message when get verify pipeline response status 500', async () => { pipelineToolClient.verifyPipelineTool = jest.fn().mockImplementation(() => { - throw new InternalServerException('error message', HttpStatusCode.InternalServerError) - }) - const { result } = renderHook(() => useVerifyPipelineToolEffect()) + throw new InternalServerException('error message', HttpStatusCode.InternalServerError); + }); + const { result } = renderHook(() => useVerifyPipelineToolEffect()); act(() => { - result.current.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS) - }) + result.current.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); + }); expect(result.current.errorMessage).toEqual( `${MOCK_PIPELINE_VERIFY_REQUEST_PARAMS.type} ${VERIFY_FAILED}: error message` - ) - }) -}) + ); + }); +}); diff --git a/frontend/__tests__/src/hooks/useVerifySourceControlEffect.test.tsx b/frontend/__tests__/src/hooks/useVerifySourceControlEffect.test.tsx index 4db96316d4..7da8dd95a7 100644 --- a/frontend/__tests__/src/hooks/useVerifySourceControlEffect.test.tsx +++ b/frontend/__tests__/src/hooks/useVerifySourceControlEffect.test.tsx @@ -1,46 +1,46 @@ -import { act, renderHook } from '@testing-library/react' -import { useVerifySourceControlEffect } from '@src/hooks/useVeritySourceControlEffect' -import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient' -import { ERROR_MESSAGE_TIME_DURATION, MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { HttpStatusCode } from 'axios' +import { act, renderHook } from '@testing-library/react'; +import { useVerifySourceControlEffect } from '@src/hooks/useVeritySourceControlEffect'; +import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; +import { ERROR_MESSAGE_TIME_DURATION, MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, VERIFY_FAILED } from '../fixtures'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { HttpStatusCode } from 'axios'; describe('use verify sourceControl state', () => { it('should initial data state when render hook', async () => { - const { result } = renderHook(() => useVerifySourceControlEffect()) + const { result } = renderHook(() => useVerifySourceControlEffect()); - expect(result.current.isLoading).toEqual(false) - }) + expect(result.current.isLoading).toEqual(false); + }); it('should set error message when get verify sourceControl throw error', async () => { - jest.useFakeTimers() + jest.useFakeTimers(); sourceControlClient.getVerifySourceControl = jest.fn().mockImplementation(() => { - throw new Error('error') - }) - const { result } = renderHook(() => useVerifySourceControlEffect()) + throw new Error('error'); + }); + const { result } = renderHook(() => useVerifySourceControlEffect()); - expect(result.current.isLoading).toEqual(false) + expect(result.current.isLoading).toEqual(false); act(() => { - result.current.verifyGithub(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS) - jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION) - }) + result.current.verifyGithub(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS); + jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); + }); - expect(result.current.errorMessage).toEqual('') - }) + expect(result.current.errorMessage).toEqual(''); + }); it('should set error message when get verify sourceControl response status 500', async () => { sourceControlClient.getVerifySourceControl = jest.fn().mockImplementation(() => { - throw new InternalServerException('error message', HttpStatusCode.InternalServerError) - }) - const { result } = renderHook(() => useVerifySourceControlEffect()) + throw new InternalServerException('error message', HttpStatusCode.InternalServerError); + }); + const { result } = renderHook(() => useVerifySourceControlEffect()); act(() => { - result.current.verifyGithub(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS) - }) + result.current.verifyGithub(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS); + }); expect(result.current.errorMessage).toEqual( `${MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS.type} ${VERIFY_FAILED}: error message` - ) - }) -}) + ); + }); +}); diff --git a/frontend/__tests__/src/initialConfigState.ts b/frontend/__tests__/src/initialConfigState.ts index 91a08e98c7..3d47466e9d 100644 --- a/frontend/__tests__/src/initialConfigState.ts +++ b/frontend/__tests__/src/initialConfigState.ts @@ -1,5 +1,5 @@ -import { BOARD_TYPES, PIPELINE_TOOL_TYPES, REGULAR_CALENDAR, SOURCE_CONTROL_TYPES } from './fixtures' -import { BasicConfigState } from '@src/context/config/configSlice' +import { BOARD_TYPES, PIPELINE_TOOL_TYPES, REGULAR_CALENDAR, SOURCE_CONTROL_TYPES } from './fixtures'; +import { BasicConfigState } from '@src/context/config/configSlice'; const initialConfigState: BasicConfigState = { isProjectCreated: true, @@ -53,6 +53,6 @@ const initialConfigState: BasicConfigState = { }, }, warningMessage: null, -} +}; -export default initialConfigState +export default initialConfigState; diff --git a/frontend/__tests__/src/layouts/Header.test.tsx b/frontend/__tests__/src/layouts/Header.test.tsx index e61aa5edce..008b58e854 100644 --- a/frontend/__tests__/src/layouts/Header.test.tsx +++ b/frontend/__tests__/src/layouts/Header.test.tsx @@ -1,22 +1,22 @@ -import { act, fireEvent, render } from '@testing-library/react' -import Header from '@src/layouts/Header' -import { BrowserRouter, MemoryRouter } from 'react-router-dom' -import { navigateMock } from '../../setupTests' -import { PROJECT_NAME } from '../fixtures' -import { headerClient } from '@src/clients/header/HeaderClient' -import { Provider } from 'react-redux' -import { setupStore } from '../utils/setupStoreUtil' +import { act, fireEvent, render } from '@testing-library/react'; +import Header from '@src/layouts/Header'; +import { BrowserRouter, MemoryRouter } from 'react-router-dom'; +import { navigateMock } from '../../setupTests'; +import { PROJECT_NAME } from '../fixtures'; +import { headerClient } from '@src/clients/header/HeaderClient'; +import { Provider } from 'react-redux'; +import { setupStore } from '../utils/setupStoreUtil'; describe('Header', () => { - let store = setupStore() + let store = setupStore(); beforeEach(() => { - headerClient.getVersion = jest.fn().mockResolvedValue('') - store = setupStore() - }) + headerClient.getVersion = jest.fn().mockResolvedValue(''); + store = setupStore(); + }); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); const setup = () => render( @@ -25,46 +25,46 @@ describe('Header', () => {
- ) + ); it('should show project name', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(PROJECT_NAME)).toBeInTheDocument() - }) + expect(getByText(PROJECT_NAME)).toBeInTheDocument(); + }); it('should show version info when request succeed', async () => { - headerClient.getVersion = jest.fn().mockResolvedValueOnce('1.11') - const { getByText } = await act(async () => setup()) + headerClient.getVersion = jest.fn().mockResolvedValueOnce('1.11'); + const { getByText } = await act(async () => setup()); - expect(getByText(/v1.11/)).toBeInTheDocument() - }) + expect(getByText(/v1.11/)).toBeInTheDocument(); + }); it('should show version info when request failed', async () => { - headerClient.getVersion = jest.fn().mockResolvedValueOnce('') - const { queryByText } = await act(async () => setup()) + headerClient.getVersion = jest.fn().mockResolvedValueOnce(''); + const { queryByText } = await act(async () => setup()); - expect(queryByText(/v/)).not.toBeInTheDocument() - }) + expect(queryByText(/v/)).not.toBeInTheDocument(); + }); it('should show project logo', () => { - const { getByRole } = setup() + const { getByRole } = setup(); - const logoInstance = getByRole('img') - expect(logoInstance).toBeInTheDocument() - expect(logoInstance.getAttribute('alt')).toContain('logo') - }) + const logoInstance = getByRole('img'); + expect(logoInstance).toBeInTheDocument(); + expect(logoInstance.getAttribute('alt')).toContain('logo'); + }); it('should go to home page when click logo', () => { - const { getByText } = setup() + const { getByText } = setup(); - fireEvent.click(getByText(PROJECT_NAME)) + fireEvent.click(getByText(PROJECT_NAME)); - expect(window.location.pathname).toEqual('/') - }) + expect(window.location.pathname).toEqual('/'); + }); describe('HomeIcon', () => { - const homeBtnText = 'Home' + const homeBtnText = 'Home'; const setup = (pathname: string) => render( @@ -72,61 +72,61 @@ describe('Header', () => {
- ) + ); afterEach(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); it('should not show home icon when pathname is others', () => { - const { queryByTitle } = setup('/not/home/page') + const { queryByTitle } = setup('/not/home/page'); - expect(queryByTitle(homeBtnText)).not.toBeInTheDocument() - }) + expect(queryByTitle(homeBtnText)).not.toBeInTheDocument(); + }); it('should not show home icon when pathname is index.html', () => { - const { queryByTitle } = setup('/index.html') + const { queryByTitle } = setup('/index.html'); - expect(queryByTitle(homeBtnText)).not.toBeInTheDocument() - }) + expect(queryByTitle(homeBtnText)).not.toBeInTheDocument(); + }); it('should navigate to home page', () => { - const { getByTitle } = setup('/error-page') + const { getByTitle } = setup('/error-page'); - fireEvent.click(getByTitle(homeBtnText)) + fireEvent.click(getByTitle(homeBtnText)); - expect(navigateMock).toBeCalledTimes(1) - expect(navigateMock).toBeCalledWith('/') - }) + expect(navigateMock).toBeCalledTimes(1); + expect(navigateMock).toBeCalledWith('/'); + }); it('should navigate to home page', () => { - const { getByTitle } = setup('/metrics') + const { getByTitle } = setup('/metrics'); - fireEvent.click(getByTitle(homeBtnText)) + fireEvent.click(getByTitle(homeBtnText)); - expect(navigateMock).toBeCalledTimes(1) - expect(navigateMock).toBeCalledWith('/') - }) + expect(navigateMock).toBeCalledTimes(1); + expect(navigateMock).toBeCalledWith('/'); + }); it('should go to home page when click logo given a not home page path', () => { - const { getByText } = setup('/not/home/page') + const { getByText } = setup('/not/home/page'); - fireEvent.click(getByText(PROJECT_NAME)) + fireEvent.click(getByText(PROJECT_NAME)); - expect(window.location.pathname).toEqual('/') - }) + expect(window.location.pathname).toEqual('/'); + }); it('should go to home page when click logo given a not home page path', () => { - const { getByText } = setup('/index.html') + const { getByText } = setup('/index.html'); - fireEvent.click(getByText(PROJECT_NAME)) + fireEvent.click(getByText(PROJECT_NAME)); - expect(window.location.pathname).toEqual('/') - }) + expect(window.location.pathname).toEqual('/'); + }); it('should render notification button when location equals to "/metrics".', () => { - const { getByTestId } = setup('/metrics') - expect(getByTestId('NotificationButton')).toBeInTheDocument() - }) - }) -}) + const { getByTestId } = setup('/metrics'); + expect(getByTestId('NotificationButton')).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/pages/Home.test.tsx b/frontend/__tests__/src/pages/Home.test.tsx index bf801e94eb..8b8443531a 100644 --- a/frontend/__tests__/src/pages/Home.test.tsx +++ b/frontend/__tests__/src/pages/Home.test.tsx @@ -1,26 +1,26 @@ -import { render } from '@testing-library/react' -import { PROJECT_NAME } from '../fixtures' -import Home from '@src/pages/Home' -import { MemoryRouter } from 'react-router-dom' -import { setupStore } from '../utils/setupStoreUtil' -import { Provider } from 'react-redux' +import { render } from '@testing-library/react'; +import { PROJECT_NAME } from '../fixtures'; +import Home from '@src/pages/Home'; +import { MemoryRouter } from 'react-router-dom'; +import { setupStore } from '../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; const setup = () => { - store = setupStore() + store = setupStore(); return render( - ) -} -let store = null + ); +}; +let store = null; describe('Home', () => { it('should render home page', () => { - const { getByText } = setup() + const { getByText } = setup(); - expect(getByText(PROJECT_NAME)).toBeInTheDocument() - }) -}) + expect(getByText(PROJECT_NAME)).toBeInTheDocument(); + }); +}); diff --git a/frontend/__tests__/src/pages/Metrics.test.tsx b/frontend/__tests__/src/pages/Metrics.test.tsx index e031f957a3..09f325d8ad 100644 --- a/frontend/__tests__/src/pages/Metrics.test.tsx +++ b/frontend/__tests__/src/pages/Metrics.test.tsx @@ -1,8 +1,8 @@ -import { render } from '@testing-library/react' -import Metrics from '@src/pages/Metrics' -import { Provider } from 'react-redux' -import { store } from '@src/store' -import { MemoryRouter } from 'react-router-dom' +import { render } from '@testing-library/react'; +import Metrics from '@src/pages/Metrics'; +import { Provider } from 'react-redux'; +import { store } from '@src/store'; +import { MemoryRouter } from 'react-router-dom'; describe('Metrics', () => { it('should render Metrics page', () => { @@ -12,11 +12,11 @@ describe('Metrics', () => { - ) - const steps = ['Config', 'Metrics', 'Report'] + ); + const steps = ['Config', 'Metrics', 'Report']; steps.map((label) => { - expect(getByText(label)).toBeVisible() - }) - }) -}) + expect(getByText(label)).toBeVisible(); + }); + }); +}); diff --git a/frontend/__tests__/src/router.test.tsx b/frontend/__tests__/src/router.test.tsx index b0049bcacd..aa039153c4 100644 --- a/frontend/__tests__/src/router.test.tsx +++ b/frontend/__tests__/src/router.test.tsx @@ -1,15 +1,15 @@ -import { render, waitFor } from '@testing-library/react' -import { MemoryRouter } from 'react-router-dom' -import Router from '@src/router' -import { Provider } from 'react-redux' -import { store } from '@src/store' -import { ERROR_PAGE_MESSAGE, ERROR_PAGE_ROUTE, BASE_PAGE_ROUTE, METRICS_PAGE_ROUTE } from './fixtures' +import { render, waitFor } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import Router from '@src/router'; +import { Provider } from 'react-redux'; +import { store } from '@src/store'; +import { ERROR_PAGE_MESSAGE, ERROR_PAGE_ROUTE, BASE_PAGE_ROUTE, METRICS_PAGE_ROUTE } from './fixtures'; jest.mock('@src/pages/Metrics', () => ({ __esModule: true, default: () =>
Mocked Metrics Page
, -})) -jest.useFakeTimers() +})); +jest.useFakeTimers(); describe('router', () => { const setup = (routeUrl: string) => render( @@ -18,39 +18,39 @@ describe('router', () => { - ) + ); it('should show home page when loading on a bad page', async () => { - const badRoute = '/some/bad/route' + const badRoute = '/some/bad/route'; - setup(badRoute) + setup(badRoute); await waitFor(() => { - expect(window.location.pathname).toEqual('/') - }) - }) + expect(window.location.pathname).toEqual('/'); + }); + }); it('should show home page when go through base route', async () => { - setup(BASE_PAGE_ROUTE) + setup(BASE_PAGE_ROUTE); await waitFor(() => { - expect(window.location.pathname).toEqual('/') - }) - }) + expect(window.location.pathname).toEqual('/'); + }); + }); it('should show Metrics page when go Metrics page', async () => { - const { getByText } = setup(METRICS_PAGE_ROUTE) + const { getByText } = setup(METRICS_PAGE_ROUTE); await waitFor(() => { - expect(getByText('Mocked Metrics Page')).toBeInTheDocument() - }) - }) + expect(getByText('Mocked Metrics Page')).toBeInTheDocument(); + }); + }); it('should show error page when go error page', async () => { - const { getByText } = setup(ERROR_PAGE_ROUTE) + const { getByText } = setup(ERROR_PAGE_ROUTE); await waitFor(() => { - expect(getByText(ERROR_PAGE_MESSAGE)).toBeInTheDocument() - }) - }) -}) + expect(getByText(ERROR_PAGE_MESSAGE)).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/__tests__/src/updatedConfigState.ts b/frontend/__tests__/src/updatedConfigState.ts index 8699d8597d..0bfcc3a271 100644 --- a/frontend/__tests__/src/updatedConfigState.ts +++ b/frontend/__tests__/src/updatedConfigState.ts @@ -4,7 +4,7 @@ import { CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE, PIPELINE_TOOL_TYPES, SOURCE_CONTROL_TYPES, -} from './fixtures' +} from './fixtures'; const updatedConfigState = { isProjectCreated: true, @@ -57,6 +57,6 @@ const updatedConfigState = { }, }, warningMessage: CONFIG_PAGE_VERIFY_IMPORT_ERROR_MESSAGE, -} +}; -export default updatedConfigState +export default updatedConfigState; diff --git a/frontend/__tests__/src/utils/Util.test.tsx b/frontend/__tests__/src/utils/Util.test.tsx index 99de1930e4..0d60391ee6 100644 --- a/frontend/__tests__/src/utils/Util.test.tsx +++ b/frontend/__tests__/src/utils/Util.test.tsx @@ -6,27 +6,27 @@ import { formatMinToHours, getJiraBoardToken, transformToCleanedBuildKiteEmoji, -} from '@src/utils/util' -import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/emojis/emoji' -import { EMPTY_STRING } from '@src/constants/commons' +} from '@src/utils/util'; +import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/emojis/emoji'; +import { EMPTY_STRING } from '@src/constants/commons'; import { FILTER_CYCLE_TIME_SETTINGS, MOCK_CYCLE_TIME_SETTING, MOCK_JIRA_WITH_STATUES_SETTING, PIPELINE_TOOL_TYPES, -} from '../fixtures' +} from '../fixtures'; describe('exportToJsonFile function', () => { it('should create a link element with the correct attributes and click it', () => { - const filename = 'test' - const json = { key: 'value' } - const documentCreateSpy = jest.spyOn(document, 'createElement') + const filename = 'test'; + const json = { key: 'value' }; + const documentCreateSpy = jest.spyOn(document, 'createElement'); - exportToJsonFile(filename, json) + exportToJsonFile(filename, json); - expect(documentCreateSpy).toHaveBeenCalledWith('a') - }) -}) + expect(documentCreateSpy).toHaveBeenCalledWith('a'); + }); +}); describe('transformToCleanedBuildKiteEmoji function', () => { it('should transform to cleaned emoji', () => { @@ -34,111 +34,111 @@ describe('transformToCleanedBuildKiteEmoji function', () => { name: 'zap', image: 'abc.com', aliases: [], - } + }; const expectedCleanedEmoji: CleanedBuildKiteEmoji = { image: 'abc.com', aliases: ['zap'], - } + }; - const [result] = transformToCleanedBuildKiteEmoji([mockOriginEmoji]) + const [result] = transformToCleanedBuildKiteEmoji([mockOriginEmoji]); - expect(result).toEqual(expectedCleanedEmoji) - }) -}) + expect(result).toEqual(expectedCleanedEmoji); + }); +}); describe('getJiraToken function', () => { it('should return an valid string when token is not empty string', () => { - const email = 'test@example.com' - const token = 'myToken' + const email = 'test@example.com'; + const token = 'myToken'; - const jiraToken = getJiraBoardToken(token, email) - const encodedMsg = `Basic ${btoa(`${email}:${token}`)}` + const jiraToken = getJiraBoardToken(token, email); + const encodedMsg = `Basic ${btoa(`${email}:${token}`)}`; - expect(jiraToken).toBe(encodedMsg) - }) + expect(jiraToken).toBe(encodedMsg); + }); it('should return an empty string when token is missing', () => { - const email = 'test@example.com' - const token = '' + const email = 'test@example.com'; + const token = ''; - const jiraToken = getJiraBoardToken(token, email) + const jiraToken = getJiraBoardToken(token, email); - expect(jiraToken).toBe('') - }) -}) + expect(jiraToken).toBe(''); + }); +}); describe('findCaseInsensitiveType function', () => { it('Should return "BuildKite" when passing a type given case insensitive input bUildkite', () => { - const selectedValue = 'bUildkite' - const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue) - expect(value).toBe(PIPELINE_TOOL_TYPES.BUILD_KITE) - }) + const selectedValue = 'bUildkite'; + const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue); + expect(value).toBe(PIPELINE_TOOL_TYPES.BUILD_KITE); + }); it('Should return "GoCD" when passing a type given case sensitive input GoCD', () => { - const selectedValue = 'GoCD' - const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue) - expect(value).toBe(PIPELINE_TOOL_TYPES.GO_CD) - }) + const selectedValue = 'GoCD'; + const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue); + expect(value).toBe(PIPELINE_TOOL_TYPES.GO_CD); + }); it('Should return "GoCD" when passing a type given case insensitive input Gocd', () => { - const selectedValue = 'Gocd' - const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue) - expect(value).toBe(PIPELINE_TOOL_TYPES.GO_CD) - }) + const selectedValue = 'Gocd'; + const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue); + expect(value).toBe(PIPELINE_TOOL_TYPES.GO_CD); + }); it('Should return "_BuildKite" when passing a type given the value mismatches with PIPELINE_TOOL_TYPES', () => { - const selectedValue = '_BuildKite' - const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue) - expect(value).not.toBe(PIPELINE_TOOL_TYPES.BUILD_KITE) - expect(value).not.toBe(PIPELINE_TOOL_TYPES.GO_CD) - expect(value).toBe(selectedValue) - }) + const selectedValue = '_BuildKite'; + const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue); + expect(value).not.toBe(PIPELINE_TOOL_TYPES.BUILD_KITE); + expect(value).not.toBe(PIPELINE_TOOL_TYPES.GO_CD); + expect(value).toBe(selectedValue); + }); it('Should return empty string when passing a type given empty string', () => { - const selectedValue = '' - const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue) - expect(value).toBe(EMPTY_STRING) - }) -}) + const selectedValue = ''; + const value = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), selectedValue); + expect(value).toBe(EMPTY_STRING); + }); +}); describe('filterAndMapCycleTimeSettings function', () => { it('should filter and map CycleTimeSettings when generate report', () => { - const value = filterAndMapCycleTimeSettings(MOCK_CYCLE_TIME_SETTING, MOCK_JIRA_WITH_STATUES_SETTING) - expect(value).toStrictEqual(FILTER_CYCLE_TIME_SETTINGS) - }) + const value = filterAndMapCycleTimeSettings(MOCK_CYCLE_TIME_SETTING, MOCK_JIRA_WITH_STATUES_SETTING); + expect(value).toStrictEqual(FILTER_CYCLE_TIME_SETTINGS); + }); it('should filter and map CycleTimeSettings when generate report', () => { const filterCycleTimeSettings = [ { name: 'IN DEV', value: 'IN DEV' }, { name: 'DOING', value: 'IN DEV' }, { name: 'DONE', value: 'DONE' }, - ] + ]; const MOCK_CYCLE_TIME_SETTING = [ { name: 'TODO', value: 'TODO' }, { name: 'IN DEV', value: 'IN DEV' }, { name: 'DONE', value: 'DONE' }, - ] + ]; const MOCK_JIRA_WITH_STATUES_SETTING = [ { name: 'todo', statuses: ['TODO', 'BACKLOG'] }, { name: 'IN DEV', statuses: ['IN DEV', 'DOING'] }, { name: 'DONE', statuses: ['DONE'] }, - ] - const value = filterAndMapCycleTimeSettings(MOCK_CYCLE_TIME_SETTING, MOCK_JIRA_WITH_STATUES_SETTING) - expect(value).toStrictEqual(filterCycleTimeSettings) - }) + ]; + const value = filterAndMapCycleTimeSettings(MOCK_CYCLE_TIME_SETTING, MOCK_JIRA_WITH_STATUES_SETTING); + expect(value).toStrictEqual(filterCycleTimeSettings); + }); it('Should return 2 hours when passing a min', () => { - const expected = 2 - const result = formatMinToHours(120) - expect(result).toEqual(expected) - }) + const expected = 2; + const result = formatMinToHours(120); + expect(result).toEqual(expected); + }); it('Should return 2 hours when passing a Milliseconds', () => { - const expected = 2 - const result = formatMillisecondsToHours(7200000) - expect(result).toEqual(expected) - }) -}) + const expected = 2; + const result = formatMillisecondsToHours(7200000); + expect(result).toEqual(expected); + }); +}); diff --git a/frontend/__tests__/src/utils/setupStoreUtil.tsx b/frontend/__tests__/src/utils/setupStoreUtil.tsx index 03add66c2b..d4349076d8 100644 --- a/frontend/__tests__/src/utils/setupStoreUtil.tsx +++ b/frontend/__tests__/src/utils/setupStoreUtil.tsx @@ -1,8 +1,8 @@ -import { configureStore } from '@reduxjs/toolkit' -import { stepperSlice } from '@src/context/stepper/StepperSlice' -import { configSlice } from '@src/context/config/configSlice' -import { metricsSlice } from '@src/context/Metrics/metricsSlice' -import { headerSlice } from '@src/context/header/headerSlice' +import { configureStore } from '@reduxjs/toolkit'; +import { stepperSlice } from '@src/context/stepper/StepperSlice'; +import { configSlice } from '@src/context/config/configSlice'; +import { metricsSlice } from '@src/context/Metrics/metricsSlice'; +import { headerSlice } from '@src/context/header/headerSlice'; export const setupStore = () => { return configureStore({ @@ -13,5 +13,5 @@ export const setupStore = () => { [headerSlice.name]: headerSlice.reducer, }, middleware: [], - }) -} + }); +}; diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts index 0f9234ebe1..44e71af90b 100644 --- a/frontend/cypress.config.ts +++ b/frontend/cypress.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'cypress' +import { defineConfig } from 'cypress'; export default defineConfig({ video: process.env.APP_ENV !== 'local', @@ -8,12 +8,12 @@ export default defineConfig({ baseUrl: process.env.APP_ORIGIN || 'http://localhost:4321', setupNodeEvents(on, config) { // eslint-disable-next-line @typescript-eslint/no-var-requires - require('./cypress/plugins/readDir.ts')(on, config) + require('./cypress/plugins/readDir.ts')(on, config); // eslint-disable-next-line @typescript-eslint/no-var-requires - require('./cypress/plugins/clearDownloadFile.ts')(on, config) + require('./cypress/plugins/clearDownloadFile.ts')(on, config); // eslint-disable-next-line @typescript-eslint/no-var-requires - require('cypress-mochawesome-reporter/plugin')(on) - return config + require('cypress-mochawesome-reporter/plugin')(on); + return config; }, }, chromeWebSecurity: false, diff --git a/frontend/cypress/e2e/createANewProject.cy.ts b/frontend/cypress/e2e/createANewProject.cy.ts index 8b2cf41346..3449bd9427 100644 --- a/frontend/cypress/e2e/createANewProject.cy.ts +++ b/frontend/cypress/e2e/createANewProject.cy.ts @@ -1,31 +1,31 @@ -import { GITHUB_TOKEN, METRICS_TITLE } from '../fixtures/fixtures' -import homePage from '../pages/home' -import configPage from '../pages/metrics/config' -import metricsPage from '../pages/metrics/metrics' -import reportPage from '../pages/metrics/report' -import { TIPS } from '../../src/constants/resources' +import { GITHUB_TOKEN, METRICS_TITLE } from '../fixtures/fixtures'; +import homePage from '../pages/home'; +import configPage from '../pages/metrics/config'; +import metricsPage from '../pages/metrics/metrics'; +import reportPage from '../pages/metrics/report'; +import { TIPS } from '../../src/constants/resources'; const cycleTimeData = [ { label: 'Average Cycle Time(Days/SP)', value: '6.75' }, { label: 'Average Cycle Time(Days/Card)', value: '9.85' }, -] +]; const velocityData = [ { label: 'Velocity(Story Point)', value: '17.5' }, { label: 'Throughput(Cards Count)', value: '12' }, -] +]; -const deploymentFrequencyData = [{ label: 'Deployment Frequency(Deployments/Day)', value: '2.36' }] +const deploymentFrequencyData = [{ label: 'Deployment Frequency(Deployments/Day)', value: '2.36' }]; -const meanTimeToRecoveryData = [{ label: 'Mean Time To Recovery(Hours)', value: '4.43' }] +const meanTimeToRecoveryData = [{ label: 'Mean Time To Recovery(Hours)', value: '4.43' }]; const leadTimeForChangeData = [ { label: 'PR Lead Time(Hours)', value: '0.00' }, { label: 'Pipeline Lead Time(Hours)', value: '-4.87' }, { label: 'Total Lead Time(Hours)', value: '-4.87' }, -] +]; -const changeFailureRateData = [{ label: 'Failure Rate', value: '49' }] +const changeFailureRateData = [{ label: 'Failure Rate', value: '49' }]; const metricsTextList = [ 'Board configuration', @@ -80,12 +80,12 @@ const metricsTextList = [ 'Time to Detect - Hrs', 'Cause by - System', 'Pipeline settings', -] +]; const pipelineSettingsAutoCompleteTextList = [ { name: 'Organization', value: 'XXXX' }, { name: 'Step', value: 'RECORD RELEASE TO PROD' }, -] +]; const cycleTimeSettingsAutoCompleteTextList = [ { name: 'In Analysis', value: 'Analysis' }, @@ -96,7 +96,7 @@ const cycleTimeSettingsAutoCompleteTextList = [ { name: 'In Test', value: 'Testing' }, { name: 'Ready to Deploy', value: 'Review' }, { name: 'Done', value: 'Done' }, -] +]; const configTextList = [ 'Project name *', @@ -104,7 +104,7 @@ const configTextList = [ 'Classic Jira', 'BuildKite', 'GitHub', -] +]; const textInputValues = [ { index: 0, value: 'E2E Project' }, @@ -114,31 +114,31 @@ const textInputValues = [ { index: 4, value: 'test@test.com' }, { index: 5, value: 'PLL' }, { index: 6, value: 'site' }, -] +]; const tokenInputValues = [ { index: 0, value: 'mockToken' }, { index: 1, value: 'mock1234'.repeat(5) }, { index: 2, value: `${GITHUB_TOKEN}` }, -] +]; interface MetricsDataItem { - label: string - value?: string + label: string; + value?: string; } const checkMetricsCalculation = (testId: string, boardData: MetricsDataItem[]) => { - cy.get(testId).should('exist') + cy.get(testId).should('exist'); cy.get(testId) .children('[data-test-id="report-section"]') .children() .each((section, index) => { cy.wrap(section).within(() => { - cy.contains(boardData[index].label).should('exist') - cy.contains(boardData[index].value).should('exist') - }) - }) -} + cy.contains(boardData[index].label).should('exist'); + cy.contains(boardData[index].value).should('exist'); + }); + }); +}; const checkBoardShowMore = () => { reportPage.showMoreBoardButton.should('exist') @@ -169,74 +169,74 @@ const checkDoraShowMore = () => { } const checkCycleTimeTooltip = () => { - metricsPage.cycleTimeTitleTooltip.trigger('mouseover') - cy.contains(TIPS.CYCLE_TIME).should('be.visible') -} + metricsPage.cycleTimeTitleTooltip.trigger('mouseover'); + cy.contains(TIPS.CYCLE_TIME).should('be.visible'); +}; const clearDownloadFile = () => { - cy.task('clearDownloads') - cy.wait(500) -} + cy.task('clearDownloads'); + cy.wait(500); +}; const checkMetricCSV = () => { - cy.wait(2000) + cy.wait(2000); cy.fixture('metric.csv').then((localFileContent) => { cy.task('readDir', 'cypress/downloads').then((files: string[]) => { - expect(files).to.match(new RegExp(/metric-.*\.csv/)) + expect(files).to.match(new RegExp(/metric-.*\.csv/)); files.forEach((file: string) => { if (file.match(/metric-.*\.csv/)) { cy.readFile(`cypress/downloads/${file}`).then((fileContent) => { - expect(fileContent).to.contains(localFileContent) - }) + expect(fileContent).to.contains(localFileContent); + }); } - }) - }) - }) -} + }); + }); + }); +}; const checkPipelineCSV = () => { - cy.wait(2000) + cy.wait(2000); return cy.task('readDir', 'cypress/downloads').then((files) => { - expect(files).to.match(new RegExp(/pipeline-.*\.csv/)) - }) -} + expect(files).to.match(new RegExp(/pipeline-.*\.csv/)); + }); +}; const checkBoardCSV = () => { - cy.wait(2000) + cy.wait(2000); return cy.task('readDir', 'cypress/downloads').then((files) => { - expect(files).to.match(new RegExp(/board-.*\.csv/)) - }) -} + expect(files).to.match(new RegExp(/board-.*\.csv/)); + }); +}; const checkFieldsExist = (fields: string[]) => { fields.forEach((item) => { - cy.contains(item).should('exist') - }) -} + cy.contains(item).should('exist'); + }); +}; const checkPipelineSettingsAutoCompleteFields = (fields: { name: string; value: string }[]) => { fields.forEach((item) => { - metricsPage.getPipelineSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value) - }) -} + metricsPage.getPipelineSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value); + }); +}; const checkCycleTimeSettingsAutoCompleteFields = (fields: { name: string; value: string }[]) => { fields.forEach((item) => { - metricsPage.getCycleTimeSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value) - }) -} + metricsPage.getCycleTimeSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value); + }); +}; const checkTextInputValuesExist = (fields: { index: number; value: string }[]) => { fields.forEach(({ index, value }) => { - cy.get('.MuiInputBase-root input[type="text"]').eq(index).should('have.value', value) - }) -} + cy.get('.MuiInputBase-root input[type="text"]').eq(index).should('have.value', value); + }); +}; const checkTokenInputValuesExist = (fields: { index: number; value: string }[]) => { fields.forEach(({ index, value }) => { - cy.get('[type="password"]').eq(index).should('have.value', value) - }) -} + cy.get('[type="password"]').eq(index).should('have.value', value); + }); +}; describe('Create a new project', () => { beforeEach(() => { @@ -244,131 +244,131 @@ describe('Create a new project', () => { method: '*', pattern: '/api/**', alias: 'api', - }) - }) + }); + }); it('Should create a new project manually', () => { - homePage.navigate() + homePage.navigate(); - homePage.headerVersion.should('exist') + homePage.headerVersion.should('exist'); - homePage.createANewProject() - cy.url().should('include', '/metrics') + homePage.createANewProject(); + cy.url().should('include', '/metrics'); - configPage.typeProjectName('E2E Project') + configPage.typeProjectName('E2E Project'); - configPage.goHomePage() - cy.url().should('include', '/') + configPage.goHomePage(); + cy.url().should('include', '/'); - homePage.createANewProject() - cy.contains('Project name *').should('have.value', '') + homePage.createANewProject(); + cy.contains('Project name *').should('have.value', ''); - configPage.typeProjectName('E2E Project') + configPage.typeProjectName('E2E Project'); - configPage.selectDateRange() + configPage.selectDateRange(); - configPage.nextStepButton.should('be.disabled') + configPage.nextStepButton.should('be.disabled'); - configPage.selectMetricsData() + configPage.selectMetricsData(); - configPage.fillBoardInfoAndVerifyWithClassicJira('1963', 'test@test.com', 'PLL', 'site', 'mockToken') - configPage.getVerifiedButton(configPage.boardConfigSection).should('be.disabled') - configPage.getResetButton(configPage.boardConfigSection).should('be.enabled') + configPage.fillBoardInfoAndVerifyWithClassicJira('1963', 'test@test.com', 'PLL', 'site', 'mockToken'); + configPage.getVerifiedButton(configPage.boardConfigSection).should('be.disabled'); + configPage.getResetButton(configPage.boardConfigSection).should('be.enabled'); - configPage.fillPipelineToolFieldsInfoAndVerify('mock1234'.repeat(5)) + configPage.fillPipelineToolFieldsInfoAndVerify('mock1234'.repeat(5)); - configPage.fillSourceControlFieldsInfoAndVerify(`${GITHUB_TOKEN}`) + configPage.fillSourceControlFieldsInfoAndVerify(`${GITHUB_TOKEN}`); - configPage.nextStepButton.should('be.enabled') + configPage.nextStepButton.should('be.enabled'); - configPage.CancelBackToHomePage() + configPage.CancelBackToHomePage(); - configPage.goMetricsStep() + configPage.goMetricsStep(); - configPage.nextStepButton.should('be.disabled') + configPage.nextStepButton.should('be.disabled'); - checkCycleTimeTooltip() + checkCycleTimeTooltip(); - metricsPage.checkCycleTime() + metricsPage.checkCycleTime(); - cy.contains('Real done').should('exist') + cy.contains('Real done').should('exist'); - metricsPage.clickRealDone() + metricsPage.clickRealDone(); - metricsPage.clickClassification() + metricsPage.clickClassification(); - metricsPage.pipelineSettingTitle.should('be.exist') + metricsPage.pipelineSettingTitle.should('be.exist'); - metricsPage.addOneCorrectPipelineConfig(0) - metricsPage.selectBranchOption() + metricsPage.addOneCorrectPipelineConfig(0); + metricsPage.selectBranchOption(); - metricsPage.addOnePipelineButton.click() - metricsPage.addOneErrorPipelineConfig(1) - metricsPage.buildKiteStepNotFoundTips.should('exist') - metricsPage.pipelineRemoveButton.click() + metricsPage.addOnePipelineButton.click(); + metricsPage.addOneErrorPipelineConfig(1); + metricsPage.buildKiteStepNotFoundTips.should('exist'); + metricsPage.pipelineRemoveButton.click(); - metricsPage.addOnePipelineButton.click() - metricsPage.addOneCorrectPipelineConfig(1) - cy.contains('This pipeline is the same as another one!').should('exist') - metricsPage.pipelineRemoveButton.click() + metricsPage.addOnePipelineButton.click(); + metricsPage.addOneCorrectPipelineConfig(1); + cy.contains('This pipeline is the same as another one!').should('exist'); + metricsPage.pipelineRemoveButton.click(); - configPage.nextStepButton.should('be.enabled') + configPage.nextStepButton.should('be.enabled'); - metricsPage.goReportStep() + metricsPage.goReportStep(); - reportPage.pageIndicator.should('be.visible') + reportPage.pageIndicator.should('be.visible'); - reportPage.firstNotification.should('exist') + reportPage.firstNotification.should('exist'); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.VELOCITY}"]`, velocityData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.VELOCITY}"]`, velocityData); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.CYCLE_TIME}"]`, cycleTimeData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.CYCLE_TIME}"]`, cycleTimeData); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.DEPLOYMENT_FREQUENCY}"]`, deploymentFrequencyData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.DEPLOYMENT_FREQUENCY}"]`, deploymentFrequencyData); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.MEAN_TIME_TO_RECOVERY}"]`, meanTimeToRecoveryData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.MEAN_TIME_TO_RECOVERY}"]`, meanTimeToRecoveryData); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.LEAD_TIME_FOR_CHANGES}"]`, leadTimeForChangeData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.LEAD_TIME_FOR_CHANGES}"]`, leadTimeForChangeData); - checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.CHANGE_FAILURE_RATE}"]`, changeFailureRateData) + checkMetricsCalculation(`[data-test-id="${METRICS_TITLE.CHANGE_FAILURE_RATE}"]`, changeFailureRateData); - clearDownloadFile() + clearDownloadFile(); - reportPage.exportMetricDataButton.should('be.enabled') + reportPage.exportMetricDataButton.should('be.enabled'); - reportPage.exportMetricData() + reportPage.exportMetricData(); - checkMetricCSV() + checkMetricCSV(); - reportPage.exportPipelineDataButton.should('be.enabled') + reportPage.exportPipelineDataButton.should('be.enabled'); - reportPage.exportPipelineData() + reportPage.exportPipelineData(); - checkPipelineCSV() + checkPipelineCSV(); - reportPage.exportBoardDataButton.should('be.enabled') + reportPage.exportBoardDataButton.should('be.enabled'); - reportPage.exportBoardData() + reportPage.exportBoardData(); - checkBoardCSV() + checkBoardCSV(); - reportPage.firstNotification.should('not.exist') + reportPage.firstNotification.should('not.exist'); checkBoardShowMore() checkDoraShowMore() // checkpoint back to metrics step - reportPage.backToMetricsStep() + reportPage.backToMetricsStep(); - checkFieldsExist(metricsTextList) - checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList) - checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList) + checkFieldsExist(metricsTextList); + checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList); + checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList); // checkpoint back to config step - metricsPage.BackToConfigStep() + metricsPage.BackToConfigStep(); - checkFieldsExist(configTextList) - checkTextInputValuesExist(textInputValues) - checkTokenInputValuesExist(tokenInputValues) - }) -}) + checkFieldsExist(configTextList); + checkTextInputValuesExist(textInputValues); + checkTokenInputValuesExist(tokenInputValues); + }); +}); diff --git a/frontend/cypress/e2e/importAProject.cy.ts b/frontend/cypress/e2e/importAProject.cy.ts index f70122dc44..f4e64a863f 100644 --- a/frontend/cypress/e2e/importAProject.cy.ts +++ b/frontend/cypress/e2e/importAProject.cy.ts @@ -1,9 +1,9 @@ -import homePage from '../pages/home' -import configPage from '../pages/metrics/config' -import metricsPage from '../pages/metrics/metrics' -import reportPage from '../pages/metrics/report' -import { GITHUB_TOKEN } from '../fixtures/fixtures' -import { Metrics } from '../pages/metrics/metrics' +import homePage from '../pages/home'; +import configPage from '../pages/metrics/config'; +import metricsPage from '../pages/metrics/metrics'; +import reportPage from '../pages/metrics/report'; +import { GITHUB_TOKEN } from '../fixtures/fixtures'; +import { Metrics } from '../pages/metrics/metrics'; const metricsTextList = [ 'Board configuration', @@ -21,12 +21,12 @@ const metricsTextList = [ 'FS R&D Classification', 'Parent', 'Pipeline settings', -] +]; const pipelineSettingsAutoCompleteTextList = [ { name: 'Organization', value: 'XXXX' }, { name: 'Step', value: 'publish gradle-cache image to cloudsmith' }, -] +]; const cycleTimeSettingsAutoCompleteTextList = [ { name: 'In Analysis', value: 'Analysis' }, @@ -37,7 +37,7 @@ const cycleTimeSettingsAutoCompleteTextList = [ { name: 'In Test', value: 'Testing' }, { name: 'Ready to Deploy', value: 'Review' }, { name: 'Done', value: 'Done' }, -] +]; const configTextList = [ 'Project name *', @@ -45,7 +45,7 @@ const configTextList = [ 'Classic Jira', 'BuildKite', 'GitHub', -] +]; const textInputValues = [ { index: 0, value: 'ConfigFileForImporting' }, @@ -55,84 +55,84 @@ const textInputValues = [ { index: 4, value: 'test@test.com' }, { index: 5, value: 'PLL' }, { index: 6, value: 'mockSite' }, -] +]; const tokenInputValues = [ { index: 0, value: 'mockToken' }, { index: 1, value: 'mockToken' }, { index: 2, value: `${GITHUB_TOKEN}` }, -] +]; const checkFieldsExist = (fields: string[]) => { fields.forEach((item) => { - cy.contains(item).should('exist') - }) -} + cy.contains(item).should('exist'); + }); +}; const checkPipelineSettingsAutoCompleteFields = (fields: { name: string; value: string }[]) => { fields.forEach((item) => { - metricsPage.getPipelineSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value) - }) -} + metricsPage.getPipelineSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value); + }); +}; const checkCycleTimeSettingsAutoCompleteFields = (fields: { name: string; value: string }[]) => { fields.forEach((item) => { - metricsPage.getCycleTimeSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value) - }) -} + metricsPage.getCycleTimeSettingsAutoCompleteField(item.name).find('input').should('have.value', item.value); + }); +}; const checkTextInputValuesExist = (fields: { index: number; value: string }[]) => { fields.forEach(({ index, value }) => { - cy.get('.MuiInputBase-root input[type="text"]').eq(index).should('have.value', value) - }) -} + cy.get('.MuiInputBase-root input[type="text"]').eq(index).should('have.value', value); + }); +}; const checkTokenInputValuesExist = (fields: { index: number; value: string }[]) => { fields.forEach(({ index, value }) => { - cy.get('[type="password"]').eq(index).should('have.value', value) - }) -} + cy.get('[type="password"]').eq(index).should('have.value', value); + }); +}; const checkMeanTimeToRecovery = () => { - reportPage.meanTimeToRecoveryTitle.should('exist') -} + reportPage.meanTimeToRecoveryTitle.should('exist'); +}; const checkPipelineToolExist = () => { - cy.contains('Pipeline Tool').should('exist') -} + cy.contains('Pipeline Tool').should('exist'); +}; const checkInputValue = (selector, expectedValue) => { cy.get(selector) .invoke('val') .then((value) => { - expect(value).to.equal(expectedValue) - }) -} + expect(value).to.equal(expectedValue); + }); +}; const checkRequiredFields = () => { - metricsPage.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.noneValue) - metricsPage.nextButton.should('be.disabled') - metricsPage.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.doneValue) - metricsPage.clickRealDone() - metricsPage.nextButton.should('be.enabled') - - metricsPage.classificationClear.click({ force: true }) - metricsPage.nextButton.should('be.disabled') - metricsPage.clickClassification() - metricsPage.nextButton.should('be.enabled') -} + metricsPage.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.noneValue); + metricsPage.nextButton.should('be.disabled'); + metricsPage.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.doneValue); + metricsPage.clickRealDone(); + metricsPage.nextButton.should('be.enabled'); + + metricsPage.classificationClear.click({ force: true }); + metricsPage.nextButton.should('be.disabled'); + metricsPage.clickClassification(); + metricsPage.nextButton.should('be.enabled'); +}; const checkProjectConfig = () => { - cy.wait(2000) + cy.wait(2000); cy.fixture('config.json').then((localFileContent) => { cy.readFile('cypress/downloads/config.json').then((fileContent) => { - expect(fileContent.sourceControl.token).to.eq(GITHUB_TOKEN) + expect(fileContent.sourceControl.token).to.eq(GITHUB_TOKEN); for (const key in localFileContent) { - expect(fileContent[key]).to.deep.eq(localFileContent[key]) + expect(fileContent[key]).to.deep.eq(localFileContent[key]); } - }) - }) -} + }); + }); +}; describe('Import project from file', () => { beforeEach(() => { @@ -140,88 +140,88 @@ describe('Import project from file', () => { method: '*', pattern: '/api/**', alias: 'api', - }) - }) + }); + }); it('Should import a new config project manually', () => { - homePage.navigate() + homePage.navigate(); - homePage.importProjectFromFile('NewConfigFileForImporting.json') - cy.url().should('include', '/metrics') - checkPipelineToolExist() - checkInputValue('.MuiInput-input', 'ConfigFileForImporting') + homePage.importProjectFromFile('NewConfigFileForImporting.json'); + cy.url().should('include', '/metrics'); + checkPipelineToolExist(); + checkInputValue('.MuiInput-input', 'ConfigFileForImporting'); - cy.waitForNetworkIdle('@api', 2000) - configPage.verifyAndClickNextToMetrics() + cy.waitForNetworkIdle('@api', 2000); + configPage.verifyAndClickNextToMetrics(); - configPage.goMetricsStep() + configPage.goMetricsStep(); - checkFieldsExist(metricsTextList) - checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList) - checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList) + checkFieldsExist(metricsTextList); + checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList); + checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList); - checkRequiredFields() + checkRequiredFields(); - metricsPage.goReportStep() + metricsPage.goReportStep(); - reportPage.pageIndicator.should('exist') + reportPage.pageIndicator.should('exist'); - checkMeanTimeToRecovery() + checkMeanTimeToRecovery(); - reportPage.exportProjectConfig() + reportPage.exportProjectConfig(); - checkProjectConfig() + checkProjectConfig(); - reportPage.backToMetricsStep() + reportPage.backToMetricsStep(); - checkFieldsExist(metricsTextList) - checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList) - checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList) + checkFieldsExist(metricsTextList); + checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList); + checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList); - metricsPage.BackToConfigStep() + metricsPage.BackToConfigStep(); - checkFieldsExist(configTextList) + checkFieldsExist(configTextList); - checkTextInputValuesExist(textInputValues) + checkTextInputValuesExist(textInputValues); - checkTokenInputValuesExist(tokenInputValues) - }) + checkTokenInputValuesExist(tokenInputValues); + }); it('Should import a old config project manually', () => { - homePage.navigate() + homePage.navigate(); - homePage.importProjectFromFile('OldConfigFileForImporting.json') - cy.url().should('include', '/metrics') - checkPipelineToolExist() - checkInputValue('.MuiInput-input', 'ConfigFileForImporting') + homePage.importProjectFromFile('OldConfigFileForImporting.json'); + cy.url().should('include', '/metrics'); + checkPipelineToolExist(); + checkInputValue('.MuiInput-input', 'ConfigFileForImporting'); - cy.waitForNetworkIdle('@api', 2000) - configPage.verifyAndClickNextToMetrics() + cy.waitForNetworkIdle('@api', 2000); + configPage.verifyAndClickNextToMetrics(); - configPage.goMetricsStep() + configPage.goMetricsStep(); - checkFieldsExist(metricsTextList) - checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList) - checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList) + checkFieldsExist(metricsTextList); + checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList); + checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList); - metricsPage.goReportStep() + metricsPage.goReportStep(); - reportPage.pageIndicator.should('exist') + reportPage.pageIndicator.should('exist'); - checkMeanTimeToRecovery() + checkMeanTimeToRecovery(); - reportPage.backToMetricsStep() + reportPage.backToMetricsStep(); - checkFieldsExist(metricsTextList) - checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList) - checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList) + checkFieldsExist(metricsTextList); + checkPipelineSettingsAutoCompleteFields(pipelineSettingsAutoCompleteTextList); + checkCycleTimeSettingsAutoCompleteFields(cycleTimeSettingsAutoCompleteTextList); - metricsPage.BackToConfigStep() + metricsPage.BackToConfigStep(); - checkFieldsExist(configTextList) + checkFieldsExist(configTextList); - checkTextInputValuesExist(textInputValues) + checkTextInputValuesExist(textInputValues); - checkTokenInputValuesExist(tokenInputValues) - }) -}) + checkTokenInputValuesExist(tokenInputValues); + }); +}); diff --git a/frontend/cypress/fixtures/fixtures.ts b/frontend/cypress/fixtures/fixtures.ts index b538d0f724..a5fae2d818 100644 --- a/frontend/cypress/fixtures/fixtures.ts +++ b/frontend/cypress/fixtures/fixtures.ts @@ -1,9 +1,9 @@ -export const MOCK_EMAIL = 'test@test.com' -export const BOARD_TOKEN = 'mockToken' -export const WEB_SITE = 'https://url.com' -export const BOARD_PROJECT_KEY = 'mockProjectKey' +export const MOCK_EMAIL = 'test@test.com'; +export const BOARD_TOKEN = 'mockToken'; +export const WEB_SITE = 'https://url.com'; +export const BOARD_PROJECT_KEY = 'mockProjectKey'; -export const GITHUB_TOKEN = `ghp_${'Abc123'.repeat(6)}` +export const GITHUB_TOKEN = `ghp_${'Abc123'.repeat(6)}`; export enum METRICS_TITLE { VELOCITY = 'Velocity', diff --git a/frontend/cypress/pages/home.ts b/frontend/cypress/pages/home.ts index f7b713bb4c..5daaa0c4d8 100644 --- a/frontend/cypress/pages/home.ts +++ b/frontend/cypress/pages/home.ts @@ -1,46 +1,46 @@ -import { GITHUB_TOKEN } from '../fixtures/fixtures' +import { GITHUB_TOKEN } from '../fixtures/fixtures'; class Home { get createANewProjectButton() { - return cy.contains('Create a new project') + return cy.contains('Create a new project'); } get importProjectFromFileButton() { - return cy.contains('Import project from file') + return cy.contains('Import project from file'); } get headerVersion() { - return cy.get('span[title="Heartbeat"]').parent().next() + return cy.get('span[title="Heartbeat"]').parent().next(); } navigate() { - cy.visit('/index.html') + cy.visit('/index.html'); } createANewProject() { - this.createANewProjectButton.click() + this.createANewProjectButton.click(); } importProjectFromFile(configFixtureName) { - this.importProjectFromFileButton.click() + this.importProjectFromFileButton.click(); cy.fixture(configFixtureName).then((fileContent) => { // Add Randomly generated token - fileContent.sourceControl.token = GITHUB_TOKEN + fileContent.sourceControl.token = GITHUB_TOKEN; cy.get('#importJson').then((e) => { const testFile = new File([JSON.stringify(fileContent)], configFixtureName, { type: 'application/json', - }) - const dataTransfer = new DataTransfer() - dataTransfer.items.add(testFile) - const input = e[0] - input.files = dataTransfer.files - cy.wrap(input).trigger('change', { force: true }) - }) - }) + }); + const dataTransfer = new DataTransfer(); + dataTransfer.items.add(testFile); + const input = e[0]; + input.files = dataTransfer.files; + cy.wrap(input).trigger('change', { force: true }); + }); + }); } } -const homePage = new Home() +const homePage = new Home(); -export default homePage +export default homePage; diff --git a/frontend/cypress/pages/metrics/config.ts b/frontend/cypress/pages/metrics/config.ts index 4b209da4d2..2607d088f1 100644 --- a/frontend/cypress/pages/metrics/config.ts +++ b/frontend/cypress/pages/metrics/config.ts @@ -1,139 +1,139 @@ class Config { get backButton() { - return cy.contains('button', 'Previous') + return cy.contains('button', 'Previous'); } get yesButton() { - return cy.contains('button', 'Yes') + return cy.contains('button', 'Yes'); } get cancelButton() { - return cy.contains('button', 'Cancel') + return cy.contains('button', 'Cancel'); } get projectNameInput() { - return this.basicInformationConfigSection.contains('label', 'Project name').parent() + return this.basicInformationConfigSection.contains('label', 'Project name').parent(); } get collectionDateFrom() { - return this.basicInformationConfigSection.contains('label', 'From').parent() + return this.basicInformationConfigSection.contains('label', 'From').parent(); } get requiredDataSelect() { - return this.basicInformationConfigSection.contains('label', 'Required metrics').parent() + return this.basicInformationConfigSection.contains('label', 'Required metrics').parent(); } get requiredDataAllSelectOption() { - return cy.contains('All') + return cy.contains('All'); } get requiredDataModelCloseElement() { - return cy.get('div.MuiBackdrop-root.MuiBackdrop-invisible.MuiModal-backdrop') + return cy.get('div.MuiBackdrop-root.MuiBackdrop-invisible.MuiModal-backdrop'); } get boardInfoSelectionJira() { - return cy.contains('Jira') + return cy.contains('Jira'); } get boardInfoSelectionClassicJira() { - return cy.contains('Classic Jira') + return cy.contains('Classic Jira'); } get boardInfoBoardIdInput() { - return this.boardConfigSection.contains('label', 'Board Id').parent() + return this.boardConfigSection.contains('label', 'Board Id').parent(); } get boardInfoEmailInput() { - return this.boardConfigSection.contains('label', 'Email').parent() + return this.boardConfigSection.contains('label', 'Email').parent(); } get boardInfoProjectKeyInput() { - return this.boardConfigSection.contains('label', 'Project Key').parent() + return this.boardConfigSection.contains('label', 'Project Key').parent(); } get boardInfoSiteInput() { - return this.boardConfigSection.contains('label', 'Site').parent() + return this.boardConfigSection.contains('label', 'Site').parent(); } get boardInfoTokenInput() { - return this.boardConfigSection.contains('label', 'Token').parent() + return this.boardConfigSection.contains('label', 'Token').parent(); } get basicInformationConfigSection() { - return cy.get('[aria-label="Basic information"]') + return cy.get('[aria-label="Basic information"]'); } get boardConfigSection() { - return cy.get('[aria-label="Board Config"]') + return cy.get('[aria-label="Board Config"]'); } get pipelineToolConfigSection() { - return cy.get('[aria-label="Pipeline Tool Config"]') + return cy.get('[aria-label="Pipeline Tool Config"]'); } get sourceControlConfigSection() { - return cy.get('[aria-label="Source Control Config"]') + return cy.get('[aria-label="Source Control Config"]'); } get nextStepButton() { - return cy.contains('button', 'Next') + return cy.contains('button', 'Next'); } get boardVerifyButton() { - return this.getVerifyButton(this.boardConfigSection) + return this.getVerifyButton(this.boardConfigSection); } get pipelineToolTokenInput() { - return this.pipelineToolConfigSection.contains('label', 'Token').parent() + return this.pipelineToolConfigSection.contains('label', 'Token').parent(); } get pipelineToolVerifyButton() { - return this.getVerifyButton(this.pipelineToolConfigSection) + return this.getVerifyButton(this.pipelineToolConfigSection); } get sourceControlTokenInput() { - return this.sourceControlConfigSection.contains('label', 'Token').parent() + return this.sourceControlConfigSection.contains('label', 'Token').parent(); } get sourceControlVerifyButton() { - return this.getVerifyButton(this.sourceControlConfigSection) + return this.getVerifyButton(this.sourceControlConfigSection); } getVerifyButton(section: Cypress.Chainable) { - return section.contains('button', 'Verify') + return section.contains('button', 'Verify'); } getVerifiedButton(section: Cypress.Chainable) { - return section.contains('button', 'Verified') + return section.contains('button', 'Verified'); } getResetButton(section: Cypress.Chainable) { - return section.contains('button', 'Reset') + return section.contains('button', 'Reset'); } navigate() { - cy.visit(Cypress.env('url') + '/metrics') + cy.visit(Cypress.env('url') + '/metrics'); } goHomePage() { - this.backButton.click() - this.yesButton.click() + this.backButton.click(); + this.yesButton.click(); } typeProjectName(projectName: string) { - this.projectNameInput.type(projectName) + this.projectNameInput.type(projectName); } selectDateRange() { - this.collectionDateFrom.type('09012022') + this.collectionDateFrom.type('09012022'); } selectMetricsData() { - this.requiredDataSelect.click() + this.requiredDataSelect.click(); - this.requiredDataAllSelectOption.click() + this.requiredDataAllSelectOption.click(); - this.requiredDataModelCloseElement.click({ force: true }) + this.requiredDataModelCloseElement.click({ force: true }); } fillBoardInfoAndVerifyWithClassicJira( @@ -143,44 +143,44 @@ class Config { site: string, token: string ) { - this.boardInfoSelectionJira.click() - this.boardInfoSelectionClassicJira.click() + this.boardInfoSelectionJira.click(); + this.boardInfoSelectionClassicJira.click(); - this.boardInfoBoardIdInput.type(boardId) - this.boardInfoEmailInput.type(email) - this.boardInfoProjectKeyInput.type(projectKey) - this.boardInfoSiteInput.type(site) - this.boardInfoTokenInput.type(token) - this.getVerifyButton(this.boardConfigSection).click() + this.boardInfoBoardIdInput.type(boardId); + this.boardInfoEmailInput.type(email); + this.boardInfoProjectKeyInput.type(projectKey); + this.boardInfoSiteInput.type(site); + this.boardInfoTokenInput.type(token); + this.getVerifyButton(this.boardConfigSection).click(); } fillPipelineToolFieldsInfoAndVerify(token: string) { - this.pipelineToolTokenInput.type(token) + this.pipelineToolTokenInput.type(token); - this.pipelineToolVerifyButton.click() + this.pipelineToolVerifyButton.click(); } fillSourceControlFieldsInfoAndVerify(token: string) { - this.sourceControlTokenInput.type(token) + this.sourceControlTokenInput.type(token); - this.sourceControlVerifyButton.click() + this.sourceControlVerifyButton.click(); } verifyAndClickNextToMetrics() { - this.boardVerifyButton.click() - this.pipelineToolVerifyButton.click() - this.sourceControlVerifyButton.click() + this.boardVerifyButton.click(); + this.pipelineToolVerifyButton.click(); + this.sourceControlVerifyButton.click(); } CancelBackToHomePage() { - this.backButton.click() - this.cancelButton.click() + this.backButton.click(); + this.cancelButton.click(); } goMetricsStep() { - this.nextStepButton.click() + this.nextStepButton.click(); } } -const configPage = new Config() -export default configPage +const configPage = new Config(); +export default configPage; diff --git a/frontend/cypress/pages/metrics/metrics.ts b/frontend/cypress/pages/metrics/metrics.ts index f670dbd648..eec8632dee 100644 --- a/frontend/cypress/pages/metrics/metrics.ts +++ b/frontend/cypress/pages/metrics/metrics.ts @@ -8,7 +8,7 @@ export class Metrics { testingLabel: 'In Test', reviewLabel: 'Ready to Deploy', doneLabel: 'Done', - } + }; static CYCLE_TIME_VALUE = { analysisValue: 'Analysis', todoValue: 'To do', @@ -19,180 +19,180 @@ export class Metrics { reviewValue: 'Review', doneValue: 'Done', noneValue: '----', - } + }; get realDoneSelect() { - return cy.contains('label', 'Consider as Done').parent() + return cy.contains('label', 'Consider as Done').parent(); } get RealDoneSelectAllOption() { - return cy.contains('All') + return cy.contains('All'); } get closeModelElement() { return cy.get( '.Mui-expanded > .MuiFormControl-root > .MuiInputBase-root > .MuiAutocomplete-endAdornment > .MuiAutocomplete-popupIndicator > [data-testid="ArrowDropDownIcon"] > path' - ) + ); } get classificationSelect() { - return cy.contains('label', 'Distinguished By').parent() + return cy.contains('label', 'Distinguished By').parent(); } get classificationSelectAllOption() { - return cy.contains('All') + return cy.contains('All'); } get pipelineSettingTitle() { - return cy.contains('Pipeline settings') + return cy.contains('Pipeline settings'); } get pipelineOfOrgXXXX() { - return cy.get('li[role="option"]').contains('XXXX') + return cy.get('li[role="option"]').contains('XXXX'); } get pipelineSelectOneOption() { - return cy.get('li[role="option"]').contains('fs-platform-payment-selector') + return cy.get('li[role="option"]').contains('fs-platform-payment-selector'); } get stepSelectSomeOption() { - return cy.get('li[role="option"]').contains('RECORD RELEASE TO PROD') + return cy.get('li[role="option"]').contains('RECORD RELEASE TO PROD'); } get addOnePipelineButton() { - return cy.get('[data-testid="AddIcon"]:first') + return cy.get('[data-testid="AddIcon"]:first'); } get classificationClear() { - return this.classificationSelect.find('[aria-label="Clear"]') + return this.classificationSelect.find('[aria-label="Clear"]'); } get pipelineSelectOnboardingOption() { - return cy.get('[data-test-id="single-selection-pipeline-name"]:contains("fs-platform-onboarding")') + return cy.get('[data-test-id="single-selection-pipeline-name"]:contains("fs-platform-onboarding")'); } get pipelineSelectUIOption() { - return cy.get('li[role="option"]').contains('payment-selector-ui') + return cy.get('li[role="option"]').contains('payment-selector-ui'); } get buildKiteStepNotFoundTips() { - return cy.contains('BuildKite get steps failed: 404 Not Found') + return cy.contains('BuildKite get steps failed: 404 Not Found'); } get pipelineRemoveButton() { - return cy.get('[data-test-id="remove-button"]').eq(1) + return cy.get('[data-test-id="remove-button"]').eq(1); } get branchSelect() { - return cy.contains('Branches').eq(0).siblings() + return cy.contains('Branches').eq(0).siblings(); } get branchSelectSomeOption() { - return cy.contains('All') + return cy.contains('All'); } get pipelineStepSelectXXOption() { - return cy.get('[data-test-id="single-selection-step"]:contains("RECORD RELEASE TO UAT"):last') + return cy.get('[data-test-id="single-selection-step"]:contains("RECORD RELEASE TO UAT"):last'); } get pipelineStepXXOption() { - return cy.get('[data-test-id="single-selection-pipeline-name"]:contains("payment-selector-ui")') + return cy.get('[data-test-id="single-selection-pipeline-name"]:contains("payment-selector-ui")'); } get leadTimeForChangeAddOneButton() { - return cy.get('[data-testid="AddIcon"]:last') + return cy.get('[data-testid="AddIcon"]:last'); } get headerBar() { - return cy.get('[data-test-id="Header"]') + return cy.get('[data-test-id="Header"]'); } get nextButton() { - return cy.contains('Next') + return cy.contains('Next'); } get backButton() { - return cy.contains('Previous') + return cy.contains('Previous'); } get cycleTimeTitleTooltip() { - return cy.get('[data-test-id="tooltip') + return cy.get('[data-test-id="tooltip'); } chooseDropdownOption = (label: string, value: string) => { - this.getCycleTimeSettingsAutoCompleteField(label).click() - cy.get('li[role="option"]').contains(value).click() - } + this.getCycleTimeSettingsAutoCompleteField(label).click(); + cy.get('li[role="option"]').contains(value).click(); + }; - getStepOfSomePipelineSelect = (i: number) => cy.get('[data-test-id="single-selection-step"]').eq(i) + getStepOfSomePipelineSelect = (i: number) => cy.get('[data-test-id="single-selection-step"]').eq(i); - getOrganizationSelect = (i: number) => cy.get('[data-test-id="single-selection-organization"]').eq(i) + getOrganizationSelect = (i: number) => cy.get('[data-test-id="single-selection-organization"]').eq(i); - getPipelineSelect = (i: number) => cy.get('[data-test-id="single-selection-pipeline-name"]').eq(i) - getCycleTimeSettingsAutoCompleteField = (name: string) => cy.get(`[aria-label="Cycle time select for ${name}"]`) - getPipelineSettingsAutoCompleteField = (name: string) => cy.contains(name).siblings().eq(0) + getPipelineSelect = (i: number) => cy.get('[data-test-id="single-selection-pipeline-name"]').eq(i); + getCycleTimeSettingsAutoCompleteField = (name: string) => cy.get(`[aria-label="Cycle time select for ${name}"]`); + getPipelineSettingsAutoCompleteField = (name: string) => cy.contains(name).siblings().eq(0); checkCycleTime() { - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.analysisLabel, Metrics.CYCLE_TIME_VALUE.analysisValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.todoLabel, Metrics.CYCLE_TIME_VALUE.todoValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.inDevLabel, Metrics.CYCLE_TIME_VALUE.inDevValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.blockLabel, Metrics.CYCLE_TIME_VALUE.blockValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.waitingLabel, Metrics.CYCLE_TIME_VALUE.waitingValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.testingLabel, Metrics.CYCLE_TIME_VALUE.testingValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.reviewLabel, Metrics.CYCLE_TIME_VALUE.reviewValue) - this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.doneValue) + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.analysisLabel, Metrics.CYCLE_TIME_VALUE.analysisValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.todoLabel, Metrics.CYCLE_TIME_VALUE.todoValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.inDevLabel, Metrics.CYCLE_TIME_VALUE.inDevValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.blockLabel, Metrics.CYCLE_TIME_VALUE.blockValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.waitingLabel, Metrics.CYCLE_TIME_VALUE.waitingValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.testingLabel, Metrics.CYCLE_TIME_VALUE.testingValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.reviewLabel, Metrics.CYCLE_TIME_VALUE.reviewValue); + this.chooseDropdownOption(Metrics.CYCLE_TIME_LABEL.doneLabel, Metrics.CYCLE_TIME_VALUE.doneValue); } clickRealDone() { - this.realDoneSelect.click() + this.realDoneSelect.click(); - this.RealDoneSelectAllOption.click() - this.closeModelElement.click({ force: true }) + this.RealDoneSelectAllOption.click(); + this.closeModelElement.click({ force: true }); } clickClassification() { - this.classificationSelect.click() + this.classificationSelect.click(); - this.classificationSelectAllOption.click() - this.closeModelElement.click({ force: true }) + this.classificationSelectAllOption.click(); + this.closeModelElement.click({ force: true }); } addOneCorrectPipelineConfig(position = 0) { - this.getOrganizationSelect(position).click() - this.pipelineOfOrgXXXX.click() - this.getPipelineSelect(position).click() - cy.waitForNetworkIdle('@api', 2000) - this.pipelineSelectOneOption.click() - this.getStepOfSomePipelineSelect(position).click() - this.stepSelectSomeOption.click() + this.getOrganizationSelect(position).click(); + this.pipelineOfOrgXXXX.click(); + this.getPipelineSelect(position).click(); + cy.waitForNetworkIdle('@api', 2000); + this.pipelineSelectOneOption.click(); + this.getStepOfSomePipelineSelect(position).click(); + this.stepSelectSomeOption.click(); } selectBranchOption() { - this.branchSelect.click() - this.branchSelectSomeOption.click() - this.closeOptions() + this.branchSelect.click(); + this.branchSelectSomeOption.click(); + this.closeOptions(); } addOneErrorPipelineConfig(position = 0) { - this.getOrganizationSelect(position).click() - this.pipelineOfOrgXXXX.click() - this.getPipelineSelect(position).click() - cy.waitForNetworkIdle('@api', 2000) - this.pipelineSelectUIOption.click() + this.getOrganizationSelect(position).click(); + this.pipelineOfOrgXXXX.click(); + this.getPipelineSelect(position).click(); + cy.waitForNetworkIdle('@api', 2000); + this.pipelineSelectUIOption.click(); } closeOptions() { - this.headerBar.click() + this.headerBar.click(); } goReportStep() { - this.nextButton.click() + this.nextButton.click(); } BackToConfigStep() { - this.backButton.click() + this.backButton.click(); } } -const metricsPage = new Metrics() -export default metricsPage +const metricsPage = new Metrics(); +export default metricsPage; diff --git a/frontend/cypress/pages/metrics/report.ts b/frontend/cypress/pages/metrics/report.ts index 5a8d8fd57d..f7b18b6370 100644 --- a/frontend/cypress/pages/metrics/report.ts +++ b/frontend/cypress/pages/metrics/report.ts @@ -1,34 +1,34 @@ class Report { get pageIndicator() { - return cy.get('[data-test-id="report-section"]', { timeout: 60000 }) + return cy.get('[data-test-id="report-section"]', { timeout: 60000 }); } get meanTimeToRecoveryTitle() { - return cy.contains('Mean Time To Recovery') + return cy.contains('Mean Time To Recovery'); } get backButton() { - return cy.contains('Previous') + return cy.contains('Previous'); } get saveButton() { - return cy.contains('Save') + return cy.contains('Save'); } get exportMetricDataButton() { - return cy.contains('Export metric data') + return cy.contains('Export metric data'); } get exportPipelineDataButton() { - return cy.contains('Export pipeline data') + return cy.contains('Export pipeline data'); } get exportBoardDataButton() { - return cy.contains('Export board data') + return cy.contains('Export board data'); } get firstNotification() { - return cy.contains('The file needs to be exported within 30 minutes, otherwise it will expire.') + return cy.contains('The file needs to be exported within 30 minutes, otherwise it will expire.'); } get showMoreBoardButton() { @@ -60,25 +60,25 @@ class Report { } backToMetricsStep() { - this.backButton.click({ force: true }) + this.backButton.click({ force: true }); } exportMetricData() { - this.exportMetricDataButton.click() + this.exportMetricDataButton.click(); } exportPipelineData() { - this.exportPipelineDataButton.click() + this.exportPipelineDataButton.click(); } exportBoardData() { - this.exportBoardDataButton.click() + this.exportBoardDataButton.click(); } exportProjectConfig() { - this.saveButton.click({ force: true }) + this.saveButton.click({ force: true }); } } -const reportPage = new Report() -export default reportPage +const reportPage = new Report(); +export default reportPage; diff --git a/frontend/cypress/plugins/clearDownloadFile.ts b/frontend/cypress/plugins/clearDownloadFile.ts index d013968649..06014c68de 100644 --- a/frontend/cypress/plugins/clearDownloadFile.ts +++ b/frontend/cypress/plugins/clearDownloadFile.ts @@ -1,24 +1,24 @@ // -import fs = require('fs') +import fs = require('fs'); module.exports = (on, config) => { on('task', { clearDownloads: () => { - const downloadsFolder = 'cypress/downloads' - clearDownloadsFolder(downloadsFolder) - return null + const downloadsFolder = 'cypress/downloads'; + clearDownloadsFolder(downloadsFolder); + return null; }, - }) -} + }); +}; function clearDownloadsFolder(folder) { try { fs.readdirSync(folder).forEach((file) => { - const filePath = `${folder}/${file}` - fs.unlinkSync(filePath) - console.log(`Deleted: ${filePath}`) - }) + const filePath = `${folder}/${file}`; + fs.unlinkSync(filePath); + console.log(`Deleted: ${filePath}`); + }); } catch (error) { - console.error('Error clearing downloads folder:', error) + console.error('Error clearing downloads folder:', error); } } diff --git a/frontend/cypress/plugins/readDir.ts b/frontend/cypress/plugins/readDir.ts index 01f3c4768c..90fba84308 100644 --- a/frontend/cypress/plugins/readDir.ts +++ b/frontend/cypress/plugins/readDir.ts @@ -1,5 +1,5 @@ // -import fs = require('fs') +import fs = require('fs'); module.exports = (on) => { on('task', { @@ -7,12 +7,12 @@ module.exports = (on) => { return new Promise((resolve, reject) => { fs.readdir(path, (err, files) => { if (err) { - reject(err) + reject(err); } else { - resolve(files) + resolve(files); } - }) - }) + }); + }); }, - }) -} + }); +}; diff --git a/frontend/cypress/support/e2e.ts b/frontend/cypress/support/e2e.ts index a3a96a3a26..5561fe41c4 100644 --- a/frontend/cypress/support/e2e.ts +++ b/frontend/cypress/support/e2e.ts @@ -12,10 +12,10 @@ // You can read more here: // https://on.cypress.io/configuration // *********************************************************** -import 'cypress-mochawesome-reporter/register' -import 'cypress-network-idle' +import 'cypress-mochawesome-reporter/register'; +import 'cypress-network-idle'; // Import commands.js using ES2015 syntax: -import './commands' +import './commands'; // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/frontend/global-setup.ts b/frontend/global-setup.ts index 859542ed27..5a75a6f367 100644 --- a/frontend/global-setup.ts +++ b/frontend/global-setup.ts @@ -1,5 +1,5 @@ -export {} +export {}; module.exports = async () => { - process.env.TZ = 'PRC' -} + process.env.TZ = 'PRC'; +}; diff --git a/frontend/scripts/runE2eWithServer.ts b/frontend/scripts/runE2eWithServer.ts index b5a7637276..a8ea445364 100644 --- a/frontend/scripts/runE2eWithServer.ts +++ b/frontend/scripts/runE2eWithServer.ts @@ -1,143 +1,143 @@ -import path from 'path' -import process from 'process' -import fetch from 'node-fetch' +import path from 'path'; +import process from 'process'; +import fetch from 'node-fetch'; -const LOCALHOST = 'http://127.0.0.1' -const WAIT_TIMEOUT = 30000 -const WAIT_INTERVAL = 3000 +const LOCALHOST = 'http://127.0.0.1'; +const WAIT_TIMEOUT = 30000; +const WAIT_INTERVAL = 3000; const HEALTH_ENDPOINT = { FRONT_END: `${LOCALHOST}:4321/`, BACK_END: `${LOCALHOST}:4322/api/v1/health`, STUB: `${LOCALHOST}:4323/health`, -} +}; const DIR = { FRONT_END: path.resolve(__dirname, '../'), BACK_END: path.resolve(__dirname, '../../backend'), STUB: path.resolve(__dirname, '../../stubs'), -} +}; const main = async (args: string[]) => { - const { $ } = await import('execa') + const { $ } = await import('execa'); const healthCheck = (url: string) => new Promise((resolve) => fetch(url) .then((response) => { if (response.ok) { - console.log(`Successfully detected service health at ${url}`) - resolve(true) + console.log(`Successfully detected service health at ${url}`); + resolve(true); } else { - resolve(false) + resolve(false); } }) .catch(() => resolve(false)) - ) + ); const checkEndpoints = async () => { - const endpoints = Object.values(HEALTH_ENDPOINT) - const checkEndpoints = endpoints.map((url) => healthCheck(url)) - const checkingResult = await Promise.all(checkEndpoints) - const healthyEndpoints = endpoints.filter((_, idx) => checkingResult[idx]) - const unhealthyEndpoints = endpoints.filter((name) => !healthyEndpoints.includes(name)) + const endpoints = Object.values(HEALTH_ENDPOINT); + const checkEndpoints = endpoints.map((url) => healthCheck(url)); + const checkingResult = await Promise.all(checkEndpoints); + const healthyEndpoints = endpoints.filter((_, idx) => checkingResult[idx]); + const unhealthyEndpoints = endpoints.filter((name) => !healthyEndpoints.includes(name)); return { unhealthyEndpoints, healthyEndpoints, - } - } + }; + }; - const startFE = () => $({ cwd: DIR.FRONT_END, stderr: 'inherit', shell: true })`pnpm run start` + const startFE = () => $({ cwd: DIR.FRONT_END, stderr: 'inherit', shell: true })`pnpm run start`; const startBE = () => $({ cwd: DIR.BACK_END, stderr: 'inherit', shell: true, - })`./gradlew bootRun --args='--spring.profiles.active=local --MOCK_SERVER_URL=${LOCALHOST}:4323'` + })`./gradlew bootRun --args='--spring.profiles.active=local --MOCK_SERVER_URL=${LOCALHOST}:4323'`; const startSTUB = () => $({ cwd: DIR.STUB, stderr: 'inherit', shell: true, - })`docker-compose up` + })`docker-compose up`; const waitForUrl = (url: string) => new Promise((resolve) => { - const startTime = Date.now() + const startTime = Date.now(); const check = () => { healthCheck(url).then((result) => { if (result) { - resolve(true) - return + resolve(true); + return; } - const elapsedTime = Date.now() - startTime + const elapsedTime = Date.now() - startTime; if (elapsedTime < WAIT_TIMEOUT) { - setTimeout(check, WAIT_INTERVAL) + setTimeout(check, WAIT_INTERVAL); } else { - console.error(`Failed to detect service health at ${url}`) - process.exit(1) + console.error(`Failed to detect service health at ${url}`); + process.exit(1); } - }) - } + }); + }; - check() - }) + check(); + }); const startServices = async (unhealthyEndpoints: string[]) => { if (unhealthyEndpoints.length <= 0) { - return [] + return []; } const processes = unhealthyEndpoints.map((name) => { switch (name) { case HEALTH_ENDPOINT.BACK_END: - console.log(`Start to run back-end service`) - return startBE() + console.log(`Start to run back-end service`); + return startBE(); case HEALTH_ENDPOINT.FRONT_END: - console.log(`Start to run front-end service`) - return startFE() + console.log(`Start to run front-end service`); + return startFE(); case HEALTH_ENDPOINT.STUB: - console.log(`Start to run stub service`) - return startSTUB() + console.log(`Start to run stub service`); + return startSTUB(); default: - console.error(`Failed to run service: ${name}`) + console.error(`Failed to run service: ${name}`); } - }) + }); - await Promise.all(unhealthyEndpoints.map((url) => waitForUrl(url))) + await Promise.all(unhealthyEndpoints.map((url) => waitForUrl(url))); - return processes - } + return processes; + }; - const e2eCommand = args[0] + const e2eCommand = args[0]; - console.log('Start to check E2E rely on services') - const { unhealthyEndpoints } = await checkEndpoints() - const newlyStartProcesses = await startServices(unhealthyEndpoints) + console.log('Start to check E2E rely on services'); + const { unhealthyEndpoints } = await checkEndpoints(); + const newlyStartProcesses = await startServices(unhealthyEndpoints); - console.log('Start to run E2E') - const e2eProcess = $({ cwd: DIR.FRONT_END, stdout: 'inherit', stderr: 'inherit', shell: true })`${e2eCommand}` + console.log('Start to run E2E'); + const e2eProcess = $({ cwd: DIR.FRONT_END, stdout: 'inherit', stderr: 'inherit', shell: true })`${e2eCommand}`; e2eProcess.on('close', (code: number, signal: string) => { if (code === 0) { - console.log(`Successfully finish E2E testing. Code: ${code}; signal: ${signal}.`) + console.log(`Successfully finish E2E testing. Code: ${code}; signal: ${signal}.`); } else { - console.error(`Failed to run E2E testing. Code: ${code}; signal: ${signal}.`) + console.error(`Failed to run E2E testing. Code: ${code}; signal: ${signal}.`); } newlyStartProcesses.forEach((pro) => { - console.log(`Start to clean up process ${pro?.pid}`) - pro?.kill() - }) + console.log(`Start to clean up process ${pro?.pid}`); + pro?.kill(); + }); - process.exit(code) - }) -} + process.exit(code); + }); +}; -const [_, __, ...args] = process.argv +const [_, __, ...args] = process.argv; -main(args) +main(args); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3df619cca8..4cc4bf0a52 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,13 +1,13 @@ -import './App.css' -import { BrowserRouter } from 'react-router-dom' -import Router from './router' -import styled from '@emotion/styled' +import './App.css'; +import { BrowserRouter } from 'react-router-dom'; +import Router from './router'; +import styled from '@emotion/styled'; const AppContainer = styled.div({ display: 'flex', flexDirection: 'column', height: '100%', -}) +}); function App() { return ( @@ -16,7 +16,7 @@ function App() { - ) + ); } -export default App +export default App; diff --git a/frontend/src/clients/Httpclient.ts b/frontend/src/clients/Httpclient.ts index bf2ecaa5f9..e4abd7a14b 100644 --- a/frontend/src/clients/Httpclient.ts +++ b/frontend/src/clients/Httpclient.ts @@ -1,48 +1,48 @@ -import axios, { AxiosInstance, HttpStatusCode } from 'axios' -import { BadRequestException } from '@src/exceptions/BadRequestException' -import { UnauthorizedException } from '@src/exceptions/UnauthorizedException' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { UnknownException } from '@src/exceptions/UnkonwException' -import { NotFoundException } from '@src/exceptions/NotFoundException' -import { ForbiddenException } from '@src/exceptions/ForbiddenException' -import { TimeoutException } from '@src/exceptions/TimeoutException' +import axios, { AxiosInstance, HttpStatusCode } from 'axios'; +import { BadRequestException } from '@src/exceptions/BadRequestException'; +import { UnauthorizedException } from '@src/exceptions/UnauthorizedException'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { UnknownException } from '@src/exceptions/UnkonwException'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { ForbiddenException } from '@src/exceptions/ForbiddenException'; +import { TimeoutException } from '@src/exceptions/TimeoutException'; export class HttpClient { - protected httpTimeout = 300000 - protected axiosInstance: AxiosInstance + protected httpTimeout = 300000; + protected axiosInstance: AxiosInstance; constructor() { this.axiosInstance = axios.create({ baseURL: '/api/v1', timeout: this.httpTimeout, - }) + }); this.axiosInstance.interceptors.response.use( (res) => res, (error) => { - const { response } = error + const { response } = error; if (response && response.status) { - const { status, data, statusText } = response - const errorMessage = data?.hintInfo ?? statusText + const { status, data, statusText } = response; + const errorMessage = data?.hintInfo ?? statusText; switch (status) { case HttpStatusCode.BadRequest: - throw new BadRequestException(errorMessage, status) + throw new BadRequestException(errorMessage, status); case HttpStatusCode.Unauthorized: - throw new UnauthorizedException(errorMessage, status) + throw new UnauthorizedException(errorMessage, status); case HttpStatusCode.NotFound: - throw new NotFoundException(errorMessage, status) + throw new NotFoundException(errorMessage, status); case HttpStatusCode.Forbidden: - throw new ForbiddenException(errorMessage, status) + throw new ForbiddenException(errorMessage, status); case HttpStatusCode.InternalServerError: - throw new InternalServerException(errorMessage, status) + throw new InternalServerException(errorMessage, status); case HttpStatusCode.ServiceUnavailable: - throw new TimeoutException(errorMessage, status) + throw new TimeoutException(errorMessage, status); default: - throw new UnknownException() + throw new UnknownException(); } } else { - throw new UnknownException() + throw new UnknownException(); } } - ) + ); } } diff --git a/frontend/src/clients/MetricsClient.ts b/frontend/src/clients/MetricsClient.ts index b59e625c1a..83a7f77ca6 100644 --- a/frontend/src/clients/MetricsClient.ts +++ b/frontend/src/clients/MetricsClient.ts @@ -1,19 +1,19 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { HttpStatusCode } from 'axios' +import { HttpClient } from '@src/clients/Httpclient'; +import { HttpStatusCode } from 'axios'; export interface getStepsParams { - pipelineName: string - repository: string - orgName: string - startTime: number - endTime: number + pipelineName: string; + repository: string; + orgName: string; + startTime: number; + endTime: number; } export class MetricsClient extends HttpClient { - steps: string[] = [] - haveStep = true - branches: string[] = [] - pipelineCrews: string[] = [] + steps: string[] = []; + haveStep = true; + branches: string[] = []; + pipelineCrews: string[] = []; getSteps = async ( params: getStepsParams, @@ -22,8 +22,8 @@ export class MetricsClient extends HttpClient { pipelineType: string, token: string ) => { - this.steps = [] - this.haveStep = true + this.steps = []; + this.haveStep = true; const result = await this.axiosInstance.get( `/pipelines/${pipelineType}/${organizationId}/pipelines/${buildId}/steps`, { @@ -32,22 +32,22 @@ export class MetricsClient extends HttpClient { }, params, } - ) + ); if (result.status === HttpStatusCode.NoContent) { - this.haveStep = false + this.haveStep = false; } else { - this.steps = result.data.steps - this.haveStep = true + this.steps = result.data.steps; + this.haveStep = true; } - this.branches = result.status === HttpStatusCode.NoContent ? [] : result.data.branches - this.pipelineCrews = result.status === HttpStatusCode.NoContent ? [] : result.data.pipelineCrews + this.branches = result.status === HttpStatusCode.NoContent ? [] : result.data.branches; + this.pipelineCrews = result.status === HttpStatusCode.NoContent ? [] : result.data.pipelineCrews; return { response: this.steps, haveStep: this.haveStep, branches: this.branches, pipelineCrews: this.pipelineCrews, - } - } + }; + }; } -export const metricsClient = new MetricsClient() +export const metricsClient = new MetricsClient(); diff --git a/frontend/src/clients/board/BoardClient.ts b/frontend/src/clients/board/BoardClient.ts index b72ba12bdf..577c7c28e5 100644 --- a/frontend/src/clients/board/BoardClient.ts +++ b/frontend/src/clients/board/BoardClient.ts @@ -1,42 +1,42 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { HttpStatusCode } from 'axios' -import { BoardRequestDTO } from '@src/clients/board/dto/request' +import { HttpClient } from '@src/clients/Httpclient'; +import { HttpStatusCode } from 'axios'; +import { BoardRequestDTO } from '@src/clients/board/dto/request'; export class BoardClient extends HttpClient { - isBoardVerify = false - haveDoneCard = true - response = {} + isBoardVerify = false; + haveDoneCard = true; + response = {}; getVerifyBoard = async (params: BoardRequestDTO) => { - this.isBoardVerify = false - this.haveDoneCard = true - this.response = {} + this.isBoardVerify = false; + this.haveDoneCard = true; + this.response = {}; try { - const boardType = params.type === 'Classic Jira' ? 'classic-jira' : params.type.toLowerCase() - const result = await this.axiosInstance.post(`/boards/${boardType}`, params) + const boardType = params.type === 'Classic Jira' ? 'classic-jira' : params.type.toLowerCase(); + const result = await this.axiosInstance.post(`/boards/${boardType}`, params); result.status === HttpStatusCode.NoContent ? this.handleBoardNoDoneCard() - : this.handleBoardVerifySucceed(result.data) + : this.handleBoardVerifySucceed(result.data); } catch (e) { - this.isBoardVerify = false - throw e + this.isBoardVerify = false; + throw e; } return { response: this.response, isBoardVerify: this.isBoardVerify, haveDoneCard: this.haveDoneCard, - } - } + }; + }; handleBoardNoDoneCard = () => { - this.isBoardVerify = false - this.haveDoneCard = false - } + this.isBoardVerify = false; + this.haveDoneCard = false; + }; handleBoardVerifySucceed = (res: object) => { - this.isBoardVerify = true - this.response = res - } + this.isBoardVerify = true; + this.response = res; + }; } -export const boardClient = new BoardClient() +export const boardClient = new BoardClient(); diff --git a/frontend/src/clients/board/dto/request.ts b/frontend/src/clients/board/dto/request.ts index a9c9b7c3b5..9f6557e74d 100644 --- a/frontend/src/clients/board/dto/request.ts +++ b/frontend/src/clients/board/dto/request.ts @@ -1,9 +1,9 @@ export interface BoardRequestDTO { - token: string - type: string - site: string - projectKey: string - startTime: number | null - endTime: number | null - boardId: string + token: string; + type: string; + site: string; + projectKey: string; + startTime: number | null; + endTime: number | null; + boardId: string; } diff --git a/frontend/src/clients/header/HeaderClient.ts b/frontend/src/clients/header/HeaderClient.ts index 6e09393abc..e5ddbfefb6 100644 --- a/frontend/src/clients/header/HeaderClient.ts +++ b/frontend/src/clients/header/HeaderClient.ts @@ -1,23 +1,23 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { VersionResponseDTO } from '@src/clients/header/dto/request' +import { HttpClient } from '@src/clients/Httpclient'; +import { VersionResponseDTO } from '@src/clients/header/dto/request'; export class HeaderClient extends HttpClient { response: VersionResponseDTO = { version: '', - } + }; getVersion = async () => { try { - const res = await this.axiosInstance.get(`/version`) - this.response = res.data + const res = await this.axiosInstance.get(`/version`); + this.response = res.data; } catch (e) { this.response = { version: '', - } - throw e + }; + throw e; } - return this.response.version - } + return this.response.version; + }; } -export const headerClient = new HeaderClient() +export const headerClient = new HeaderClient(); diff --git a/frontend/src/clients/header/dto/request.ts b/frontend/src/clients/header/dto/request.ts index ebd423cb2e..05a6641137 100644 --- a/frontend/src/clients/header/dto/request.ts +++ b/frontend/src/clients/header/dto/request.ts @@ -1,3 +1,3 @@ export interface VersionResponseDTO { - version: string + version: string; } diff --git a/frontend/src/clients/pipeline/PipelineToolClient.ts b/frontend/src/clients/pipeline/PipelineToolClient.ts index ab1b37d5f8..33a626a1b0 100644 --- a/frontend/src/clients/pipeline/PipelineToolClient.ts +++ b/frontend/src/clients/pipeline/PipelineToolClient.ts @@ -1,27 +1,27 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { PipelineRequestDTO } from '@src/clients/pipeline/dto/request' +import { HttpClient } from '@src/clients/Httpclient'; +import { PipelineRequestDTO } from '@src/clients/pipeline/dto/request'; export class PipelineToolClient extends HttpClient { - isPipelineToolVerified = false - response = {} + isPipelineToolVerified = false; + response = {}; verifyPipelineTool = async (params: PipelineRequestDTO) => { try { - const result = await this.axiosInstance.post(`/pipelines/${params.type}`, params) - this.handlePipelineToolVerifySucceed(result.data) + const result = await this.axiosInstance.post(`/pipelines/${params.type}`, params); + this.handlePipelineToolVerifySucceed(result.data); } catch (e) { - this.isPipelineToolVerified = false - throw e + this.isPipelineToolVerified = false; + throw e; } return { response: this.response, isPipelineToolVerified: this.isPipelineToolVerified, - } - } + }; + }; handlePipelineToolVerifySucceed = (res: object) => { - this.isPipelineToolVerified = true - this.response = res - } + this.isPipelineToolVerified = true; + this.response = res; + }; } -export const pipelineToolClient = new PipelineToolClient() +export const pipelineToolClient = new PipelineToolClient(); diff --git a/frontend/src/clients/pipeline/dto/request.ts b/frontend/src/clients/pipeline/dto/request.ts index 51e35fd4aa..5916deab31 100644 --- a/frontend/src/clients/pipeline/dto/request.ts +++ b/frontend/src/clients/pipeline/dto/request.ts @@ -1,6 +1,6 @@ export interface PipelineRequestDTO { - type: string - token: string - startTime: string | number | null - endTime: string | number | null + type: string; + token: string; + startTime: string | number | null; + endTime: string | number | null; } diff --git a/frontend/src/clients/report/CSVClient.ts b/frontend/src/clients/report/CSVClient.ts index bc5766c96c..ac5fe126c9 100644 --- a/frontend/src/clients/report/CSVClient.ts +++ b/frontend/src/clients/report/CSVClient.ts @@ -1,11 +1,11 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { CSVReportRequestDTO } from '@src/clients/report/dto/request' -import dayjs from 'dayjs' -import { downloadCSV } from '@src/utils/util' +import { HttpClient } from '@src/clients/Httpclient'; +import { CSVReportRequestDTO } from '@src/clients/report/dto/request'; +import dayjs from 'dayjs'; +import { downloadCSV } from '@src/utils/util'; export class CSVClient extends HttpClient { - parseTimeStampToHumanDate = (csvTimeStamp: number | undefined): string => dayjs(csvTimeStamp).format('HHmmSSS') - parseCollectionDateToHumanDate = (date: string) => dayjs(date).format('YYYYMMDD') + parseTimeStampToHumanDate = (csvTimeStamp: number | undefined): string => dayjs(csvTimeStamp).format('HHmmSSS'); + parseCollectionDateToHumanDate = (date: string) => dayjs(date).format('YYYYMMDD'); exportCSVData = async (params: CSVReportRequestDTO) => { await this.axiosInstance @@ -15,13 +15,13 @@ export class CSVClient extends HttpClient { params.startDate )}-${this.parseCollectionDateToHumanDate(params.endDate)}-${this.parseTimeStampToHumanDate( params.csvTimeStamp - )}.csv` - downloadCSV(exportedFilename, res.data) + )}.csv`; + downloadCSV(exportedFilename, res.data); }) .catch((e) => { - throw e - }) - } + throw e; + }); + }; } -export const csvClient = new CSVClient() +export const csvClient = new CSVClient(); diff --git a/frontend/src/clients/report/ReportClient.ts b/frontend/src/clients/report/ReportClient.ts index ec2cb059ca..822e194857 100644 --- a/frontend/src/clients/report/ReportClient.ts +++ b/frontend/src/clients/report/ReportClient.ts @@ -1,13 +1,13 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { ReportRequestDTO } from '@src/clients/report/dto/request' -import { ReportCallbackResponse, ReportResponseDTO } from '@src/clients/report/dto/response' +import { HttpClient } from '@src/clients/Httpclient'; +import { ReportRequestDTO } from '@src/clients/report/dto/request'; +import { ReportCallbackResponse, ReportResponseDTO } from '@src/clients/report/dto/response'; export class ReportClient extends HttpClient { - status = 0 + status = 0; reportCallbackResponse: ReportCallbackResponse = { callbackUrl: '', interval: 0, - } + }; reportResponse: ReportResponseDTO = { velocity: { velocityForSP: 0, @@ -63,37 +63,37 @@ export class ReportClient extends HttpClient { isPipelineMetricsReady: false, isSourceControlMetricsReady: false, isAllMetricsReady: false, - } + }; retrieveReportByUrl = async (params: ReportRequestDTO, url: string) => { await this.axiosInstance .post(url, params, {}) .then((res) => { - this.reportCallbackResponse = res.data + this.reportCallbackResponse = res.data; }) .catch((e) => { - throw e - }) + throw e; + }); return { response: this.reportCallbackResponse, - } - } + }; + }; pollingReport = async (url: string) => { await this.axiosInstance .get(url) .then((res) => { - this.status = res.status - this.reportResponse = res.data + this.status = res.status; + this.reportResponse = res.data; }) .catch((e) => { - throw e - }) + throw e; + }); return { status: this.status, response: this.reportResponse, - } - } + }; + }; } -export const reportClient = new ReportClient() +export const reportClient = new ReportClient(); diff --git a/frontend/src/clients/report/dto/request.ts b/frontend/src/clients/report/dto/request.ts index 0f3c8308d3..fef6d7628d 100644 --- a/frontend/src/clients/report/dto/request.ts +++ b/frontend/src/clients/report/dto/request.ts @@ -1,77 +1,77 @@ export interface ReportRequestDTO { - metrics: string[] - startTime: string | null - endTime: string | null - considerHoliday: boolean + metrics: string[]; + startTime: string | null; + endTime: string | null; + considerHoliday: boolean; buildKiteSetting?: { - type: string - token: string - pipelineCrews: string[] + type: string; + token: string; + pipelineCrews: string[]; deploymentEnvList: | { - id: string - name: string - orgId: string - orgName: string - repository: string - step: string - branches: string[] + id: string; + name: string; + orgId: string; + orgName: string; + repository: string; + step: string; + branches: string[]; }[] - | [] - } + | []; + }; codebaseSetting?: { - type: string - token: string + type: string; + token: string; leadTime: { - id: string - name: string - orgId: string - orgName: string - repository: string - step: string - branches: string[] - }[] - } + id: string; + name: string; + orgId: string; + orgName: string; + repository: string; + step: string; + branches: string[]; + }[]; + }; jiraBoardSetting?: { - token: string - type: string - site: string - projectKey: string - boardId: string - boardColumns: { name: string; value: string }[] - treatFlagCardAsBlock: boolean - users: string[] - assigneeFilter: string - targetFields: { key: string; name: string; flag: boolean }[] - doneColumn: string[] - } - csvTimeStamp?: number + token: string; + type: string; + site: string; + projectKey: string; + boardId: string; + boardColumns: { name: string; value: string }[]; + treatFlagCardAsBlock: boolean; + users: string[]; + assigneeFilter: string; + targetFields: { key: string; name: string; flag: boolean }[]; + doneColumn: string[]; + }; + csvTimeStamp?: number; } export interface BoardReportRequestDTO { - considerHoliday: boolean - startTime: string | null - endTime: string | null - metrics: string[] + considerHoliday: boolean; + startTime: string | null; + endTime: string | null; + metrics: string[]; jiraBoardSetting?: { - token: string - type: string - site: string - projectKey: string - boardId: string - boardColumns: { name: string; value: string }[] - treatFlagCardAsBlock: boolean - users: string[] - assigneeFilter: string - targetFields: { key: string; name: string; flag: boolean }[] - doneColumn: string[] - } - csvTimeStamp?: number + token: string; + type: string; + site: string; + projectKey: string; + boardId: string; + boardColumns: { name: string; value: string }[]; + treatFlagCardAsBlock: boolean; + users: string[]; + assigneeFilter: string; + targetFields: { key: string; name: string; flag: boolean }[]; + doneColumn: string[]; + }; + csvTimeStamp?: number; } export interface CSVReportRequestDTO { - dataType: string - csvTimeStamp: number - startDate: string - endDate: string + dataType: string; + csvTimeStamp: number; + startDate: string; + endDate: string; } diff --git a/frontend/src/clients/sourceControl/SourceControlClient.ts b/frontend/src/clients/sourceControl/SourceControlClient.ts index f0a56109af..3d54b0e7f8 100644 --- a/frontend/src/clients/sourceControl/SourceControlClient.ts +++ b/frontend/src/clients/sourceControl/SourceControlClient.ts @@ -1,28 +1,28 @@ -import { HttpClient } from '@src/clients/Httpclient' -import { SourceControlRequestDTO } from '@src/clients/sourceControl/dto/request' +import { HttpClient } from '@src/clients/Httpclient'; +import { SourceControlRequestDTO } from '@src/clients/sourceControl/dto/request'; export class SourceControlClient extends HttpClient { - isSourceControlVerify = false - response = {} + isSourceControlVerify = false; + response = {}; getVerifySourceControl = async (params: SourceControlRequestDTO) => { try { - const result = await this.axiosInstance.post('/source-control', params) - this.handleSourceControlVerifySucceed(result.data) + const result = await this.axiosInstance.post('/source-control', params); + this.handleSourceControlVerifySucceed(result.data); } catch (e) { - this.isSourceControlVerify = false - throw e + this.isSourceControlVerify = false; + throw e; } return { response: this.response, isSourceControlVerify: this.isSourceControlVerify, - } - } + }; + }; handleSourceControlVerifySucceed = (res: object) => { - this.isSourceControlVerify = true - this.response = res - } + this.isSourceControlVerify = true; + this.response = res; + }; } -export const sourceControlClient = new SourceControlClient() +export const sourceControlClient = new SourceControlClient(); diff --git a/frontend/src/clients/sourceControl/dto/request.ts b/frontend/src/clients/sourceControl/dto/request.ts index d57aed0daa..2d64d5d330 100644 --- a/frontend/src/clients/sourceControl/dto/request.ts +++ b/frontend/src/clients/sourceControl/dto/request.ts @@ -1,6 +1,6 @@ export interface SourceControlRequestDTO { - type: string - token: string - startTime: string | number | null - endTime: string | number | null + type: string; + token: string; + startTime: string | number | null; + endTime: string | number | null; } diff --git a/frontend/src/components/Common/Buttons.ts b/frontend/src/components/Common/Buttons.ts index 11105283b8..d9120bf6b1 100644 --- a/frontend/src/components/Common/Buttons.ts +++ b/frontend/src/components/Common/Buttons.ts @@ -1,16 +1,16 @@ -import Button from '@mui/material/Button' -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' +import Button from '@mui/material/Button'; +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; export const BasicButton = styled(Button)({ width: '3rem', fontSize: '0.8rem', fontFamily: theme.main.font.secondary, fontWeight: 'bold', -}) +}); -export const VerifyButton = styled(BasicButton)({}) +export const VerifyButton = styled(BasicButton)({}); export const ResetButton = styled(BasicButton)({ color: '#f44336', marginLeft: '0.5rem', -}) +}); diff --git a/frontend/src/components/Common/CollectionDuration/index.tsx b/frontend/src/components/Common/CollectionDuration/index.tsx index 61156d8a2b..0326bad7d4 100644 --- a/frontend/src/components/Common/CollectionDuration/index.tsx +++ b/frontend/src/components/Common/CollectionDuration/index.tsx @@ -6,41 +6,41 @@ import { MonthYearText, DateTitle, ColoredTopArea, -} from '@src/components/Common/CollectionDuration/style' -import dayjs from 'dayjs' +} from '@src/components/Common/CollectionDuration/style'; +import dayjs from 'dayjs'; type Props = { - startDate: string - endDate: string -} + startDate: string; + endDate: string; +}; type DisplayedDateInfo = { - year: string - month: string - day: string -} + year: string; + month: string; + day: string; +}; type DateInformation = { - formattedDate: DisplayedDateInfo - dateTittle: string -} + formattedDate: DisplayedDateInfo; + dateTittle: string; +}; const CollectionDuration = ({ startDate, endDate }: Props) => { - const toBeFormattedStartDate = dayjs(startDate) - const toBeFormattedEndDate = dayjs(endDate) + const toBeFormattedStartDate = dayjs(startDate); + const toBeFormattedEndDate = dayjs(endDate); const startDateInfo: DisplayedDateInfo = { year: toBeFormattedStartDate.format('YY'), month: toBeFormattedStartDate.format('MMM'), day: toBeFormattedStartDate.format('DD'), - } + }; const endDateInfo: DisplayedDateInfo = { year: toBeFormattedEndDate.format('YY'), month: toBeFormattedEndDate.format('MMM'), day: toBeFormattedEndDate.format('DD'), - } + }; const DateDisplay = (props: DateInformation) => { - const { dateTittle, formattedDate } = props + const { dateTittle, formattedDate } = props; return (
{dateTittle} @@ -52,8 +52,8 @@ const CollectionDuration = ({ startDate, endDate }: Props) => {
- ) - } + ); + }; return ( @@ -61,7 +61,7 @@ const CollectionDuration = ({ startDate, endDate }: Props) => { - ) -} + ); +}; -export default CollectionDuration +export default CollectionDuration; diff --git a/frontend/src/components/Common/CollectionDuration/style.tsx b/frontend/src/components/Common/CollectionDuration/style.tsx index 14d0b5fe80..a07dc01391 100644 --- a/frontend/src/components/Common/CollectionDuration/style.tsx +++ b/frontend/src/components/Common/CollectionDuration/style.tsx @@ -1,12 +1,12 @@ -import { styled } from '@mui/material/styles' -import { Paper } from '@mui/material' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import { Paper } from '@mui/material'; +import { theme } from '@src/theme'; export const CollectionDateContainer = styled('div')({ display: 'flex', alignItems: 'flex-end', margin: '0 auto 2rem', -}) +}); export const TextBox = styled(Paper)({ width: '3rem', @@ -20,7 +20,7 @@ export const TextBox = styled(Paper)({ flexDirection: 'column', alignItems: 'center', justifyContent: 'center', -}) +}); export const GreyTransitionBox = styled('div')({ width: '10rem', @@ -28,7 +28,7 @@ export const GreyTransitionBox = styled('div')({ backgroundColor: '#CCCCCC47', border: 0, borderRadius: 0, -}) +}); export const ColoredTopArea = styled('div')((props: { isStart: boolean }) => ({ width: '100%', @@ -36,7 +36,7 @@ export const ColoredTopArea = styled('div')((props: { isStart: boolean }) => ({ backgroundColor: props.isStart ? theme.palette.info.light : theme.palette.info.dark, borderTopLeftRadius: '0.375rem', borderTopRightRadius: '0.375rem', -})) +})); export const DateTitle = styled('div')((props: { isStart: boolean }) => ({ fontSize: '0.625rem', @@ -46,14 +46,14 @@ export const DateTitle = styled('div')((props: { isStart: boolean }) => ({ padding: '0.25rem 0', fontWeight: 'bold', marginBottom: '0.125rem', -})) +})); export const DateText = styled('div')({ fontSize: '1.5rem', letterSpacing: '0.1rem', -}) +}); export const MonthYearText = styled('div')({ fontSize: '0.625rem', marginBottom: '0.3rem', -}) +}); diff --git a/frontend/src/components/Common/ConfigForms.ts b/frontend/src/components/Common/ConfigForms.ts index 347a75ff45..11a4ce2c45 100644 --- a/frontend/src/components/Common/ConfigForms.ts +++ b/frontend/src/components/Common/ConfigForms.ts @@ -1,9 +1,9 @@ -import { styled } from '@mui/material/styles' -import { FormControl, TextField } from '@mui/material' -import { theme } from '@src/theme' -import { MetricSelectionWrapper } from '@src/components/Metrics/MetricsStep/style' +import { styled } from '@mui/material/styles'; +import { FormControl, TextField } from '@mui/material'; +import { theme } from '@src/theme'; +import { MetricSelectionWrapper } from '@src/components/Metrics/MetricsStep/style'; -export const ConfigSectionContainer = styled(MetricSelectionWrapper)({}) +export const ConfigSectionContainer = styled(MetricSelectionWrapper)({}); export const StyledForm = styled('form')({ display: 'grid', @@ -14,9 +14,9 @@ export const StyledForm = styled('form')({ [theme.breakpoints.down('md')]: { gridTemplateColumns: '1fr', }, -}) +}); -export const StyledTypeSelections = styled(FormControl)({}) +export const StyledTypeSelections = styled(FormControl)({}); export const StyledTextField = styled(TextField)` input { @@ -28,9 +28,9 @@ export const StyledTextField = styled(TextField)` input:-webkit-autofill:active { transition: background-color 5000s ease-in-out 0s; } -` +`; export const StyledButtonGroup = styled('div')({ justifySelf: 'end', gridColumn: '2 / 3', -}) +}); diff --git a/frontend/src/components/Common/DateRangeViewer/index.tsx b/frontend/src/components/Common/DateRangeViewer/index.tsx index 845cada1cf..e9b127a746 100644 --- a/frontend/src/components/Common/DateRangeViewer/index.tsx +++ b/frontend/src/components/Common/DateRangeViewer/index.tsx @@ -1,10 +1,10 @@ -import { DateRangeContainer, StyledArrowForward, StyledCalendarToday } from './style' -import { formatDate } from '@src/utils/util' +import { DateRangeContainer, StyledArrowForward, StyledCalendarToday } from './style'; +import { formatDate } from '@src/utils/util'; type Props = { - startDate: string - endDate: string -} + startDate: string; + endDate: string; +}; const DateRangeViewer = ({ startDate, endDate }: Props) => { return ( @@ -14,7 +14,7 @@ const DateRangeViewer = ({ startDate, endDate }: Props) => { {formatDate(endDate)} - ) -} + ); +}; -export default DateRangeViewer +export default DateRangeViewer; diff --git a/frontend/src/components/Common/DateRangeViewer/style.tsx b/frontend/src/components/Common/DateRangeViewer/style.tsx index 0cf1ff725a..5cb4648199 100644 --- a/frontend/src/components/Common/DateRangeViewer/style.tsx +++ b/frontend/src/components/Common/DateRangeViewer/style.tsx @@ -1,6 +1,6 @@ -import styled from '@emotion/styled' -import { ArrowForward, CalendarToday } from '@mui/icons-material' -import { theme } from '@src/theme' +import styled from '@emotion/styled'; +import { ArrowForward, CalendarToday } from '@mui/icons-material'; +import { theme } from '@src/theme'; export const DateRangeContainer = styled.div({ display: 'flex', @@ -14,16 +14,16 @@ export const DateRangeContainer = styled.div({ padding: '.75rem', color: theme.palette.text.disabled, fontSize: '.875rem', -}) +}); export const StyledArrowForward = styled(ArrowForward)({ color: theme.palette.text.disabled, margin: '0 .5rem', fontSize: '.875rem', -}) +}); export const StyledCalendarToday = styled(CalendarToday)({ color: theme.palette.text.disabled, marginLeft: '1rem', fontSize: '.875rem', -}) +}); diff --git a/frontend/src/components/Common/EllipsisText/index.tsx b/frontend/src/components/Common/EllipsisText/index.tsx index 21cabb091e..46d0a1c344 100644 --- a/frontend/src/components/Common/EllipsisText/index.tsx +++ b/frontend/src/components/Common/EllipsisText/index.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import { StyledText } from '@src/components/Common/EllipsisText/style' +import React from 'react'; +import { StyledText } from '@src/components/Common/EllipsisText/style'; interface IEllipsisTextProps { - children: React.ReactNode - fitContent: boolean - ref: React.ForwardedRef + children: React.ReactNode; + fitContent: boolean; + ref: React.ForwardedRef; } export default React.forwardRef(function RefWrapper( @@ -15,5 +15,5 @@ export default React.forwardRef(functi {props.children} - ) -}) + ); +}); diff --git a/frontend/src/components/Common/EllipsisText/style.tsx b/frontend/src/components/Common/EllipsisText/style.tsx index 18f8c4b2bc..82154be379 100644 --- a/frontend/src/components/Common/EllipsisText/style.tsx +++ b/frontend/src/components/Common/EllipsisText/style.tsx @@ -1,8 +1,8 @@ -import styled from '@emotion/styled' -import { ellipsisProps } from '@src/layouts/style' +import styled from '@emotion/styled'; +import { ellipsisProps } from '@src/layouts/style'; export const StyledText = styled.p(({ fitContent }: { fitContent: boolean }) => ({ maxWidth: '100%', width: fitContent ? 'fit-content' : 'auto', ...ellipsisProps, -})) +})); diff --git a/frontend/src/components/Common/MetricsSettingButton/index.tsx b/frontend/src/components/Common/MetricsSettingButton/index.tsx index 2019c2ef30..a94d1456a2 100644 --- a/frontend/src/components/Common/MetricsSettingButton/index.tsx +++ b/frontend/src/components/Common/MetricsSettingButton/index.tsx @@ -1,12 +1,12 @@ -import { Add } from '@mui/icons-material' -import React from 'react' +import { Add } from '@mui/icons-material'; +import React from 'react'; import { MetricsSettingAddButtonContainer, MetricsSettingAddButtonItem, -} from '@src/components/Common/MetricsSettingButton/style' +} from '@src/components/Common/MetricsSettingButton/style'; interface metricsSettingAddButtonProps { - onAddPipeline: () => void + onAddPipeline: () => void; } export const MetricsSettingAddButton = ({ onAddPipeline }: metricsSettingAddButtonProps) => { @@ -16,5 +16,5 @@ export const MetricsSettingAddButton = ({ onAddPipeline }: metricsSettingAddButt New Pipeline - ) -} + ); +}; diff --git a/frontend/src/components/Common/MetricsSettingButton/style.tsx b/frontend/src/components/Common/MetricsSettingButton/style.tsx index 26bf4078e6..4bf2e8fd6f 100644 --- a/frontend/src/components/Common/MetricsSettingButton/style.tsx +++ b/frontend/src/components/Common/MetricsSettingButton/style.tsx @@ -1,5 +1,5 @@ -import styled from '@emotion/styled' -import { Button } from '@mui/material' +import styled from '@emotion/styled'; +import { Button } from '@mui/material'; export const MetricsSettingAddButtonContainer = styled.div({ display: 'flex', @@ -7,8 +7,8 @@ export const MetricsSettingAddButtonContainer = styled.div({ borderRadius: '0.25rem', border: '0.07rem dashed rgba(67, 80, 175, 1)', marginBottom: '1rem', -}) +}); export const MetricsSettingAddButtonItem = styled(Button)({ width: '100%', -}) +}); diff --git a/frontend/src/components/Common/MetricsSettingTitle/index.tsx b/frontend/src/components/Common/MetricsSettingTitle/index.tsx index 273a9bde14..4fc8ab1c2e 100644 --- a/frontend/src/components/Common/MetricsSettingTitle/index.tsx +++ b/frontend/src/components/Common/MetricsSettingTitle/index.tsx @@ -1,12 +1,12 @@ -import styled from '@emotion/styled' +import styled from '@emotion/styled'; export const MetricsSettingTitleContainer = styled.div({ margin: '1.25rem 0', fontSize: '1rem', lineHeight: '1.25rem', fontWeight: '600', -}) +}); export const MetricsSettingTitle = (props: { title: string }) => ( {props.title} -) +); diff --git a/frontend/src/components/Common/MetricsSettingTitle/style.tsx b/frontend/src/components/Common/MetricsSettingTitle/style.tsx index 421c8e9bd2..b178f959a9 100644 --- a/frontend/src/components/Common/MetricsSettingTitle/style.tsx +++ b/frontend/src/components/Common/MetricsSettingTitle/style.tsx @@ -1,8 +1,8 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; export const Divider = styled('div')({ paddingLeft: '0.5rem', borderLeft: `0.4rem solid ${theme.main.backgroundColor}`, margin: '0', -}) +}); diff --git a/frontend/src/components/Common/MultiAutoComplete/index.tsx b/frontend/src/components/Common/MultiAutoComplete/index.tsx index eb26c558bf..95f60e059e 100644 --- a/frontend/src/components/Common/MultiAutoComplete/index.tsx +++ b/frontend/src/components/Common/MultiAutoComplete/index.tsx @@ -1,19 +1,19 @@ -import { Checkbox, createFilterOptions, TextField } from '@mui/material' -import React from 'react' -import { StyledAutocompleted } from './styles' -import { Z_INDEX } from '@src/constants/commons' +import { Checkbox, createFilterOptions, TextField } from '@mui/material'; +import React from 'react'; +import { StyledAutocompleted } from './styles'; +import { Z_INDEX } from '@src/constants/commons'; type Props = { - optionList: string[] - selectedOption: string[] + optionList: string[]; + selectedOption: string[]; // There is an any because m-ui strictly define its type, but the parameters are not that strict. Maybe because of version diff - onChangeHandler: any - isSelectAll: boolean - textFieldLabel: string - isError: boolean - testId?: string - isBoardCrews?: boolean -} + onChangeHandler: any; + isSelectAll: boolean; + textFieldLabel: string; + isError: boolean; + testId?: string; + isBoardCrews?: boolean; +}; const MultiAutoComplete = ({ optionList, selectedOption, @@ -24,7 +24,7 @@ const MultiAutoComplete = ({ testId, isBoardCrews = true, }: Props) => { - const filter = createFilterOptions() + const filter = createFilterOptions(); return ( { - const filtered = filter(options, params) - return ['All', ...filtered] + const filtered = filter(options, params); + return ['All', ...filtered]; }} getOptionLabel={(option) => option as string} onChange={onChangeHandler} renderOption={(props, option, { selected }) => { - const selectAllProps = option === 'All' ? { checked: isSelectAll } : {} + const selectAllProps = option === 'All' ? { checked: isSelectAll } : {}; return (
  • {option as string}
  • - ) + ); }} renderInput={(params) => ( - ) -} + ); +}; -export default MultiAutoComplete +export default MultiAutoComplete; diff --git a/frontend/src/components/Common/MultiAutoComplete/styles.ts b/frontend/src/components/Common/MultiAutoComplete/styles.ts index c5ca5912e3..de46257b6f 100644 --- a/frontend/src/components/Common/MultiAutoComplete/styles.ts +++ b/frontend/src/components/Common/MultiAutoComplete/styles.ts @@ -1,5 +1,5 @@ -import { styled } from '@mui/material/styles' -import { Autocomplete } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { Autocomplete } from '@mui/material'; export const StyledAutocompleted = styled(Autocomplete)` & .MuiAutocomplete-tag { @@ -7,4 +7,4 @@ export const StyledAutocompleted = styled(Autocomplete)` border: 0.05rem solid rgba(0, 0, 0, 0.26); font-size: 0.9rem; } -` +`; diff --git a/frontend/src/components/Common/NotificationButton/index.tsx b/frontend/src/components/Common/NotificationButton/index.tsx index 487e65f06f..b013035326 100644 --- a/frontend/src/components/Common/NotificationButton/index.tsx +++ b/frontend/src/components/Common/NotificationButton/index.tsx @@ -1,16 +1,16 @@ -import { NotificationIconWrapper, sx } from '@src/components/Common/NotificationButton/style' -import { ClickAwayListener, Tooltip } from '@mui/material' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' +import { NotificationIconWrapper, sx } from '@src/components/Common/NotificationButton/style'; +import { ClickAwayListener, Tooltip } from '@mui/material'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; export const NotificationButton = ({ notificationProps, updateProps }: useNotificationLayoutEffectInterface) => { const handleTooltipClose = () => { - if (notificationProps === undefined) return + if (notificationProps === undefined) return; updateProps?.({ title: notificationProps.title, open: false, closeAutomatically: false, - }) - } + }); + }; return ( <> @@ -38,5 +38,5 @@ export const NotificationButton = ({ notificationProps, updateProps }: useNotifi - ) -} + ); +}; diff --git a/frontend/src/components/Common/NotificationButton/style.tsx b/frontend/src/components/Common/NotificationButton/style.tsx index d4a15a1cfe..6f6d38b114 100644 --- a/frontend/src/components/Common/NotificationButton/style.tsx +++ b/frontend/src/components/Common/NotificationButton/style.tsx @@ -1,12 +1,12 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { Z_INDEX } from '@src/constants/commons' -import { NotificationsRounded } from '@mui/icons-material' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; +import { Z_INDEX } from '@src/constants/commons'; +import { NotificationsRounded } from '@mui/icons-material'; export const NotificationIconWrapper = styled(NotificationsRounded)({ color: theme.main.color, marginRight: '0.5rem', -}) +}); export const sx = { width: '12rem', @@ -19,4 +19,4 @@ export const sx = { color: theme.components!.tip.color, }, zIndex: Z_INDEX.TOOLTIP, -} +}; diff --git a/frontend/src/components/Common/ReportForThreeColumns/index.tsx b/frontend/src/components/Common/ReportForThreeColumns/index.tsx index 17152a1378..babb754824 100644 --- a/frontend/src/components/Common/ReportForThreeColumns/index.tsx +++ b/frontend/src/components/Common/ReportForThreeColumns/index.tsx @@ -1,31 +1,31 @@ -import { Table, TableBody, TableHead, TableRow } from '@mui/material' +import { Table, TableBody, TableHead, TableRow } from '@mui/material'; import { BorderTableCell, ColumnTableCell, Container, Row, StyledTableCell, -} from '@src/components/Common/ReportForTwoColumns/style' -import React, { Fragment } from 'react' -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { AVERAGE_FIELD, REPORT_SUFFIX_UNITS } from '@src/constants/resources' -import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji' -import { EmojiWrap, StyledAvatar, StyledTypography } from '@src/emojis/style' -import { ReportSelectionTitle } from '@src/components/Metrics/MetricsStep/style' +} from '@src/components/Common/ReportForTwoColumns/style'; +import React, { Fragment } from 'react'; +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { AVERAGE_FIELD, REPORT_SUFFIX_UNITS } from '@src/constants/resources'; +import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji'; +import { EmojiWrap, StyledAvatar, StyledTypography } from '@src/emojis/style'; +import { ReportSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; interface ReportForThreeColumnsProps { - title: string - fieldName: string - listName: string - data: ReportDataWithThreeColumns[] + title: string; + fieldName: string; + listName: string; + data: ReportDataWithThreeColumns[]; } export const ReportForThreeColumns = ({ title, fieldName, listName, data }: ReportForThreeColumnsProps) => { const emojiRow = (row: ReportDataWithThreeColumns) => { - const { name } = row - const emojiUrls: string[] = getEmojiUrls(name) + const { name } = row; + const emojiUrls: string[] = getEmojiUrls(name); if (name.includes(':') && emojiUrls.length > 0) { - const [prefix, suffix] = row.name.split('/') + const [prefix, suffix] = row.name.split('/'); return ( {prefix}/ @@ -34,10 +34,10 @@ export const ReportForThreeColumns = ({ title, fieldName, listName, data }: Repo ))} {removeExtraEmojiName(suffix)} - ) + ); } - return {name} - } + return {name}; + }; const renderRows = () => data.slice(0, data.length === 2 && data[1].name === AVERAGE_FIELD ? 1 : data.length).map((row) => ( @@ -52,7 +52,7 @@ export const ReportForThreeColumns = ({ title, fieldName, listName, data }: Repo ))} - )) + )); return ( <> @@ -75,7 +75,7 @@ export const ReportForThreeColumns = ({ title, fieldName, listName, data }: Repo
    - ) -} + ); +}; -export default ReportForThreeColumns +export default ReportForThreeColumns; diff --git a/frontend/src/components/Common/ReportForTwoColumns/index.tsx b/frontend/src/components/Common/ReportForTwoColumns/index.tsx index c711776cd3..38f1341766 100644 --- a/frontend/src/components/Common/ReportForTwoColumns/index.tsx +++ b/frontend/src/components/Common/ReportForTwoColumns/index.tsx @@ -1,19 +1,18 @@ -import { Table, TableBody, TableHead, TableRow } from '@mui/material' +import { Table, TableBody, TableHead, TableRow } from '@mui/material'; import { BorderTableCell, ColumnTableCell, Container, Row, StyledTableCell, -} from '@src/components/Common/ReportForTwoColumns/style' -import { Fragment } from 'react' -import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { ReportSelectionTitle } from '@src/components/Metrics/MetricsStep/style' -import React from 'react' +} from '@src/components/Common/ReportForTwoColumns/style'; +import React, { Fragment } from 'react'; +import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { ReportSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; interface ReportForTwoColumnsProps { - title: string - data: ReportDataWithTwoColumns[] + title: string; + data: ReportDataWithTwoColumns[]; } export const ReportForTwoColumns = ({ title, data }: ReportForTwoColumnsProps) => { @@ -32,8 +31,8 @@ export const ReportForTwoColumns = ({ title, data }: ReportForTwoColumnsProps) = ))} - )) - } + )); + }; return ( <> @@ -50,7 +49,7 @@ export const ReportForTwoColumns = ({ title, data }: ReportForTwoColumnsProps) = - ) -} + ); +}; -export default ReportForTwoColumns +export default ReportForTwoColumns; diff --git a/frontend/src/components/Common/ReportForTwoColumns/style.tsx b/frontend/src/components/Common/ReportForTwoColumns/style.tsx index edcb6921a5..2058951209 100644 --- a/frontend/src/components/Common/ReportForTwoColumns/style.tsx +++ b/frontend/src/components/Common/ReportForTwoColumns/style.tsx @@ -1,25 +1,25 @@ -import { styled } from '@mui/material/styles' -import { TableCell, TableRow } from '@mui/material' -import { MetricSelectionWrapper } from '@src/components/Metrics/MetricsStep/style' -import { tableCellClasses } from '@mui/material/TableCell' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import { TableCell, TableRow } from '@mui/material'; +import { MetricSelectionWrapper } from '@src/components/Metrics/MetricsStep/style'; +import { tableCellClasses } from '@mui/material/TableCell'; +import { theme } from '@src/theme'; -export const Container = styled(MetricSelectionWrapper)({}) +export const Container = styled(MetricSelectionWrapper)({}); -export const Row = styled(TableRow)({}) +export const Row = styled(TableRow)({}); export const StyledTableCell = styled(TableCell)(() => ({ [`&.${tableCellClasses.head}`]: { backgroundColor: theme.palette.secondary.dark, fontWeight: 600, }, -})) +})); export const BorderTableCell = styled(TableCell)(() => ({ border: '0.06rem solid #E0E0E0', borderRight: 'none', color: theme.palette.secondary.contrastText, -})) +})); export const ColumnTableCell = styled(BorderTableCell)(() => ({ borderLeft: 'none', -})) +})); diff --git a/frontend/src/components/Common/ReportGrid/ReportCard/index.tsx b/frontend/src/components/Common/ReportGrid/ReportCard/index.tsx index 6a646c2edf..7096386401 100644 --- a/frontend/src/components/Common/ReportGrid/ReportCard/index.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportCard/index.tsx @@ -2,43 +2,43 @@ import { StyledItemSection, StyledReportCard, StyledReportCardTitle, -} from '@src/components/Common/ReportGrid/ReportCard/style' -import React, { HTMLAttributes } from 'react' -import { ReportCardItem, ReportCardItemProps } from '@src/components/Common/ReportGrid/ReportCardItem' -import { GRID_CONFIG } from '@src/constants/commons' -import { Loading } from '@src/components/Loading' +} from '@src/components/Common/ReportGrid/ReportCard/style'; +import React, { HTMLAttributes } from 'react'; +import { ReportCardItem, ReportCardItemProps } from '@src/components/Common/ReportGrid/ReportCardItem'; +import { GRID_CONFIG } from '@src/constants/commons'; +import { Loading } from '@src/components/Loading'; interface ReportCardProps extends HTMLAttributes { - title: string - items?: ReportCardItemProps[] | null - xs: number + title: string; + items?: ReportCardItemProps[] | null; + xs: number; } export const ReportCard = ({ title, items, xs }: ReportCardProps) => { - const defaultFlex = 1 + const defaultFlex = 1; const getReportItems = () => { - let style = GRID_CONFIG.FULL + let style = GRID_CONFIG.FULL; switch (xs) { case GRID_CONFIG.HALF.XS: - style = GRID_CONFIG.HALF - break + style = GRID_CONFIG.HALF; + break; case GRID_CONFIG.FULL.XS: - style = GRID_CONFIG.FULL - break + style = GRID_CONFIG.FULL; + break; } const getFlex = (length: number) => { if (length <= 1) { - return defaultFlex + return defaultFlex; } else { switch (xs) { case GRID_CONFIG.FULL.XS: - return GRID_CONFIG.FULL.FLEX + return GRID_CONFIG.FULL.FLEX; case GRID_CONFIG.HALF.XS: - return GRID_CONFIG.HALF.FLEX + return GRID_CONFIG.HALF.FLEX; } } - } + }; return ( @@ -58,8 +58,8 @@ export const ReportCard = ({ title, items, xs }: ReportCardProps) => { ) )} - ) - } + ); + }; return ( @@ -67,5 +67,5 @@ export const ReportCard = ({ title, items, xs }: ReportCardProps) => { {title} {items && getReportItems()} - ) -} + ); +}; diff --git a/frontend/src/components/Common/ReportGrid/ReportCard/style.tsx b/frontend/src/components/Common/ReportGrid/ReportCard/style.tsx index 887ee9ab55..0099582117 100644 --- a/frontend/src/components/Common/ReportGrid/ReportCard/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportCard/style.tsx @@ -1,6 +1,6 @@ -import { theme } from '@src/theme' -import styled from '@emotion/styled' -import { Typography } from '@mui/material' +import { theme } from '@src/theme'; +import styled from '@emotion/styled'; +import { Typography } from '@mui/material'; export const StyledReportCard = styled.div({ position: 'relative', @@ -10,16 +10,16 @@ export const StyledReportCard = styled.div({ border: theme.main.cardBorder, background: theme.main.color, boxShadow: theme.main.cardShadow, -}) +}); export const StyledItemSection = styled.div({ display: 'flex', alignItems: 'center', minWidth: '25%', padding: '0.75rem 0', -}) +}); export const StyledReportCardTitle = styled(Typography)({ fontWeight: 500, fontSize: '1rem', -}) +}); diff --git a/frontend/src/components/Common/ReportGrid/ReportCardItem/index.tsx b/frontend/src/components/Common/ReportGrid/ReportCardItem/index.tsx index 150eebb7bf..51c7c5e45d 100644 --- a/frontend/src/components/Common/ReportGrid/ReportCardItem/index.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportCardItem/index.tsx @@ -7,17 +7,17 @@ import { StyledValue, StyledValueSection, StyledWrapper, -} from '@src/components/Common/ReportGrid/ReportCardItem/style' -import DividingLine from '@src/assets/DividingLine.svg' -import React, { HTMLAttributes } from 'react' -import { Tooltip } from '@mui/material' +} from '@src/components/Common/ReportGrid/ReportCardItem/style'; +import DividingLine from '@src/assets/DividingLine.svg'; +import React, { HTMLAttributes } from 'react'; +import { Tooltip } from '@mui/material'; export interface ReportCardItemProps extends HTMLAttributes { - value: number - isToFixed?: boolean - extraValue?: string - subtitle: string - showDividingLine?: boolean + value: number; + isToFixed?: boolean; + extraValue?: string; + subtitle: string; + showDividingLine?: boolean; } export const ReportCardItem = ({ @@ -43,5 +43,5 @@ export const ReportCardItem = ({ - ) -} + ); +}; diff --git a/frontend/src/components/Common/ReportGrid/ReportCardItem/style.tsx b/frontend/src/components/Common/ReportGrid/ReportCardItem/style.tsx index 8600c4237a..a43565e11e 100644 --- a/frontend/src/components/Common/ReportGrid/ReportCardItem/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportCardItem/style.tsx @@ -1,24 +1,24 @@ -import styled from '@emotion/styled' -import { Typography } from '@mui/material' -import { theme } from '@src/theme' +import styled from '@emotion/styled'; +import { Typography } from '@mui/material'; +import { theme } from '@src/theme'; export const StyledItem = styled.div({ display: 'flex', alignItems: 'center', width: '100%', overflow: 'hidden', -}) +}); export const StyledContent = styled('div')({ width: '100%', display: 'flex', alignItems: 'end', justifyContent: 'space-between', -}) +}); export const StyledWrapper = styled('div')({ width: '100%', -}) +}); export const StyledValue = styled(Typography)({ fontSize: '2rem', @@ -29,7 +29,7 @@ export const StyledValue = styled(Typography)({ [theme.breakpoints.down('lg')]: { fontSize: '1.8rem', }, -}) +}); export const StyledSubtitle = styled('div')({ width: '90%', @@ -43,7 +43,7 @@ export const StyledSubtitle = styled('div')({ fontStyle: 'normal', color: theme.main.secondColor, opacity: 0.65, -}) +}); export const StyledDividingLine = styled.img({ marginRight: '2rem', @@ -52,7 +52,7 @@ export const StyledDividingLine = styled.img({ [theme.breakpoints.down('lg')]: { marginRight: '1.5rem', }, -}) +}); export const StyledValueSection = styled.div({ width: '100%', @@ -60,7 +60,7 @@ export const StyledValueSection = styled.div({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', -}) +}); export const StyledExtraValue = styled.div({ width: '100%', @@ -69,4 +69,4 @@ export const StyledExtraValue = styled.div({ paddingTop: '0.4rem', marginLeft: '0.8rem', whiteSpace: 'nowrap', -}) +}); diff --git a/frontend/src/components/Common/ReportGrid/ReportTitle/index.tsx b/frontend/src/components/Common/ReportGrid/ReportTitle/index.tsx index f2f14f1473..2564b8dcaf 100644 --- a/frontend/src/components/Common/ReportGrid/ReportTitle/index.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportTitle/index.tsx @@ -1,12 +1,12 @@ -import React from 'react' +import React from 'react'; import { StyledMetricsSign, StyledMetricsTitle, StyledMetricsTitleSection, -} from '@src/components/Common/ReportGrid/ReportTitle/style' +} from '@src/components/Common/ReportGrid/ReportTitle/style'; interface ReportTitleProps { - title: string + title: string; } export const ReportTitle = ({ title }: ReportTitleProps) => { return ( @@ -14,5 +14,5 @@ export const ReportTitle = ({ title }: ReportTitleProps) => { {title} - ) -} + ); +}; diff --git a/frontend/src/components/Common/ReportGrid/index.tsx b/frontend/src/components/Common/ReportGrid/index.tsx index 58d267f372..edf8538afd 100644 --- a/frontend/src/components/Common/ReportGrid/index.tsx +++ b/frontend/src/components/Common/ReportGrid/index.tsx @@ -1,46 +1,46 @@ -import React from 'react' -import { Grid } from '@mui/material' -import { ReportCard } from '@src/components/Common/ReportGrid/ReportCard' -import { GRID_CONFIG } from '@src/constants/commons' -import { ReportCardItemProps } from '@src/components/Common/ReportGrid/ReportCardItem' +import React from 'react'; +import { Grid } from '@mui/material'; +import { ReportCard } from '@src/components/Common/ReportGrid/ReportCard'; +import { GRID_CONFIG } from '@src/constants/commons'; +import { ReportCardItemProps } from '@src/components/Common/ReportGrid/ReportCardItem'; export interface ReportGridProps { - lastGrid?: boolean - reportDetails: ReportDetailProps[] + lastGrid?: boolean; + reportDetails: ReportDetailProps[]; } export interface ReportDetailProps { - title: string - items?: ReportCardItemProps[] | null + title: string; + items?: ReportCardItemProps[] | null; } export const ReportGrid = ({ lastGrid, reportDetails }: ReportGridProps) => { const getXS = (index: number) => { if (needTakeUpAWholeLine(index)) { - return GRID_CONFIG.FULL.XS + return GRID_CONFIG.FULL.XS; } else if (reportDetails.length > 1) { - return GRID_CONFIG.HALF.XS + return GRID_CONFIG.HALF.XS; } else { - return GRID_CONFIG.FULL.XS + return GRID_CONFIG.FULL.XS; } - } + }; const needTakeUpAWholeLine = (index: number) => { - const size = reportDetails.length - const isOddSize = size % 2 === 1 - return isOddSize && lastGrid && size - 1 == index - } + const size = reportDetails.length; + const isOddSize = size % 2 === 1; + return isOddSize && lastGrid && size - 1 == index; + }; return ( {reportDetails.map((detail, index) => { - const xs = getXS(index) + const xs = getXS(index); return ( - ) + ); })} - ) -} + ); +}; diff --git a/frontend/src/components/Common/WarningNotification/index.tsx b/frontend/src/components/Common/WarningNotification/index.tsx index d0ade48fdf..a4b9e30532 100644 --- a/frontend/src/components/Common/WarningNotification/index.tsx +++ b/frontend/src/components/Common/WarningNotification/index.tsx @@ -1,22 +1,22 @@ -import { useEffect, useState } from 'react' -import { StyledAlert, WarningBar } from './style' -import { DURATION } from '@src/constants/commons' +import { useEffect, useState } from 'react'; +import { StyledAlert, WarningBar } from './style'; +import { DURATION } from '@src/constants/commons'; export const WarningNotification = (props: { message: string }) => { - const { message } = props - const [open, setOpen] = useState(true) + const { message } = props; + const [open, setOpen] = useState(true); useEffect(() => { const timer = setTimeout(() => { - setOpen(false) - }, DURATION.ERROR_MESSAGE_TIME) + setOpen(false); + }, DURATION.ERROR_MESSAGE_TIME); return () => { - clearTimeout(timer) - } - }, []) + clearTimeout(timer); + }; + }, []); return ( {message} - ) -} + ); +}; diff --git a/frontend/src/components/Common/WarningNotification/style.tsx b/frontend/src/components/Common/WarningNotification/style.tsx index 0dd6fc6fec..1d5153871a 100644 --- a/frontend/src/components/Common/WarningNotification/style.tsx +++ b/frontend/src/components/Common/WarningNotification/style.tsx @@ -1,21 +1,21 @@ -import { Snackbar } from '@mui/material' -import MuiAlert, { AlertProps } from '@mui/material/Alert' -import { styled } from '@mui/material/styles' -import { forwardRef } from 'react' -import { Z_INDEX } from '@src/constants/commons' +import { Snackbar } from '@mui/material'; +import MuiAlert, { AlertProps } from '@mui/material/Alert'; +import { styled } from '@mui/material/styles'; +import { forwardRef } from 'react'; +import { Z_INDEX } from '@src/constants/commons'; export const WarningBar = styled(Snackbar)({ position: 'relative', display: 'flex', justifyContent: 'center', zIndex: Z_INDEX.SNACKBARS, -}) +}); const Alert = forwardRef(function Alert(props, ref) { - return -}) + return ; +}); export const StyledAlert = styled(Alert)({ position: 'absolute', marginTop: '6rem', -}) +}); diff --git a/frontend/src/components/ErrorContent/index.tsx b/frontend/src/components/ErrorContent/index.tsx index b313a9419d..a32dacf8f9 100644 --- a/frontend/src/components/ErrorContent/index.tsx +++ b/frontend/src/components/ErrorContent/index.tsx @@ -1,6 +1,6 @@ -import { useNavigate } from 'react-router-dom' -import ErrorIcon from '@src/assets/ErrorIcon.svg' -import React from 'react' +import { useNavigate } from 'react-router-dom'; +import ErrorIcon from '@src/assets/ErrorIcon.svg'; +import React from 'react'; import { Container, ErrorImg, @@ -12,16 +12,16 @@ import { Okay, ErrorInfo, RetryButton, -} from '@src/components/ErrorContent/style' -import { ROUTE } from '@src/constants/router' -import { MESSAGE } from '@src/constants/resources' +} from '@src/components/ErrorContent/style'; +import { ROUTE } from '@src/constants/router'; +import { MESSAGE } from '@src/constants/resources'; export const ErrorContent = () => { - const navigate = useNavigate() + const navigate = useNavigate(); const backToHomePage = () => { - navigate(ROUTE.BASE_PAGE) - } + navigate(ROUTE.BASE_PAGE); + }; return ( @@ -39,5 +39,5 @@ export const ErrorContent = () => { Go to homepage - ) -} + ); +}; diff --git a/frontend/src/components/ErrorContent/style.tsx b/frontend/src/components/ErrorContent/style.tsx index 8eb8325d59..4f2fb23ed2 100644 --- a/frontend/src/components/ErrorContent/style.tsx +++ b/frontend/src/components/ErrorContent/style.tsx @@ -1,7 +1,7 @@ -import styled from '@emotion/styled' -import { theme } from '@src/theme' -import { Button } from '@mui/material' -import { basicButtonStyle } from '@src/components/Metrics/MetricsStepper/style' +import styled from '@emotion/styled'; +import { theme } from '@src/theme'; +import { Button } from '@mui/material'; +import { basicButtonStyle } from '@src/components/Metrics/MetricsStepper/style'; export const Container = styled.div({ display: 'flex', @@ -12,23 +12,23 @@ export const Container = styled.div({ padding: '6rem', minHeight: '30rem', minWidth: '60rem', -}) +}); export const ErrorTitle = styled.div({ paddingLeft: '6rem', position: 'relative', -}) +}); export const ErrorImg = styled.img({ height: '7rem', -}) +}); export const Some = styled.text({ fontSize: '2.5rem', fontWeight: '600', fontFamily: 'system-ui', paddingLeft: '1rem', -}) +}); export const Error = styled.text({ paddingLeft: '1rem', @@ -37,20 +37,20 @@ export const Error = styled.text({ fontFamily: 'system-ui', letterSpacing: '-0.5rem', color: 'firebrick', -}) +}); export const ErrorMessage = styled.div({ display: 'flex', flexDirection: 'row', paddingRight: '12rem', -}) +}); export const OhNo = styled.text({ fontSize: '7rem', fontWeight: '750', fontFamily: 'system-ui', letterSpacing: '0.2rem', -}) +}); export const Okay = styled.text({ margin: 'auto 0', @@ -59,14 +59,14 @@ export const Okay = styled.text({ fontWeight: '250', fontFamily: 'system-ui', color: theme.main.backgroundColor, -}) +}); export const ErrorInfo = styled.div({ padding: '2rem', fontSize: '1rem', fontWeight: '250', fontFamily: 'system-ui', -}) +}); export const RetryButton = styled(Button)({ ...basicButtonStyle, @@ -78,4 +78,4 @@ export const RetryButton = styled(Button)({ '&:hover': { background: theme.main.backgroundColor, }, -}) +}); diff --git a/frontend/src/components/ErrorNotification/index.tsx b/frontend/src/components/ErrorNotification/index.tsx index 67119778ee..a318c11fd5 100644 --- a/frontend/src/components/ErrorNotification/index.tsx +++ b/frontend/src/components/ErrorNotification/index.tsx @@ -1,10 +1,10 @@ -import { ErrorBar, StyledAlert } from './style' +import { ErrorBar, StyledAlert } from './style'; export const ErrorNotification = (props: { message: string }) => { - const { message } = props + const { message } = props; return ( {message} - ) -} + ); +}; diff --git a/frontend/src/components/ErrorNotification/style.tsx b/frontend/src/components/ErrorNotification/style.tsx index 08266bad78..730ffce63b 100644 --- a/frontend/src/components/ErrorNotification/style.tsx +++ b/frontend/src/components/ErrorNotification/style.tsx @@ -1,21 +1,21 @@ -import { Snackbar } from '@mui/material' -import { styled } from '@mui/material/styles' -import { forwardRef } from 'react' -import MuiAlert, { AlertProps } from '@mui/material/Alert' -import { Z_INDEX } from '@src/constants/commons' +import { Snackbar } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { forwardRef } from 'react'; +import MuiAlert, { AlertProps } from '@mui/material/Alert'; +import { Z_INDEX } from '@src/constants/commons'; export const ErrorBar = styled(Snackbar)({ position: 'relative', display: 'flex', justifyContent: 'center', zIndex: Z_INDEX.SNACKBARS, -}) +}); const Alert = forwardRef(function Alert(props, ref) { - return -}) + return ; +}); export const StyledAlert = styled(Alert)({ position: 'absolute', marginTop: '6rem', -}) +}); diff --git a/frontend/src/components/HomeGuide/index.tsx b/frontend/src/components/HomeGuide/index.tsx index 872c19f79a..4048239244 100644 --- a/frontend/src/components/HomeGuide/index.tsx +++ b/frontend/src/components/HomeGuide/index.tsx @@ -1,71 +1,71 @@ -import { useNavigate } from 'react-router-dom' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { resetImportedData, updateBasicConfigState, updateProjectCreatedState } from '@src/context/config/configSlice' -import React, { useState } from 'react' -import { updateMetricsImportedData } from '@src/context/Metrics/metricsSlice' -import { resetStep } from '@src/context/stepper/StepperSlice' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import { convertToNewFileConfig, NewFileConfig, OldFileConfig } from '@src/fileConfig/fileConfig' -import { GuideButton, HomeGuideContainer, StyledStack } from '@src/components/HomeGuide/style' -import { MESSAGE } from '@src/constants/resources' -import { ROUTE } from '@src/constants/router' +import { useNavigate } from 'react-router-dom'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { resetImportedData, updateBasicConfigState, updateProjectCreatedState } from '@src/context/config/configSlice'; +import React, { useState } from 'react'; +import { updateMetricsImportedData } from '@src/context/Metrics/metricsSlice'; +import { resetStep } from '@src/context/stepper/StepperSlice'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import { convertToNewFileConfig, NewFileConfig, OldFileConfig } from '@src/fileConfig/fileConfig'; +import { GuideButton, HomeGuideContainer, StyledStack } from '@src/components/HomeGuide/style'; +import { MESSAGE } from '@src/constants/resources'; +import { ROUTE } from '@src/constants/router'; export const HomeGuide = () => { - const navigate = useNavigate() - const dispatch = useAppDispatch() - const [validConfig, setValidConfig] = useState(true) + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const [validConfig, setValidConfig] = useState(true); - const getImportFileElement = () => document.getElementById('importJson') as HTMLInputElement + const getImportFileElement = () => document.getElementById('importJson') as HTMLInputElement; const isValidImportedConfig = (config: NewFileConfig) => { try { const { projectName, metrics, dateRange: { startDate, endDate }, - } = config - return projectName || startDate || endDate || metrics.length > 0 + } = config; + return projectName || startDate || endDate || metrics.length > 0; } catch { - return false + return false; } - } + }; const handleChange = (e: React.ChangeEvent) => { - const input = e.target.files?.[0] - const reader = new FileReader() + const input = e.target.files?.[0]; + const reader = new FileReader(); if (input) { reader.onload = () => { if (reader.result && typeof reader.result === 'string') { - const importedConfig: OldFileConfig | NewFileConfig = JSON.parse(reader.result) - const config: NewFileConfig = convertToNewFileConfig(importedConfig) + const importedConfig: OldFileConfig | NewFileConfig = JSON.parse(reader.result); + const config: NewFileConfig = convertToNewFileConfig(importedConfig); if (isValidImportedConfig(config)) { - dispatch(updateProjectCreatedState(false)) - dispatch(updateBasicConfigState(config)) - dispatch(updateMetricsImportedData(config)) - navigate(ROUTE.METRICS_PAGE) + dispatch(updateProjectCreatedState(false)); + dispatch(updateBasicConfigState(config)); + dispatch(updateMetricsImportedData(config)); + navigate(ROUTE.METRICS_PAGE); } else { - setValidConfig(false) + setValidConfig(false); } } - const fileInput = getImportFileElement() - fileInput.value = '' - } - reader.readAsText(input, 'utf-8') + const fileInput = getImportFileElement(); + fileInput.value = ''; + }; + reader.readAsText(input, 'utf-8'); } - } + }; const openFileImportBox = () => { - setValidConfig(true) - dispatch(resetImportedData()) - dispatch(resetStep()) - const fileInput = getImportFileElement() - fileInput.click() - } + setValidConfig(true); + dispatch(resetImportedData()); + dispatch(resetStep()); + const fileInput = getImportFileElement(); + fileInput.click(); + }; const createNewProject = () => { - dispatch(resetStep()) - dispatch(resetImportedData()) - navigate(ROUTE.METRICS_PAGE) - } + dispatch(resetStep()); + dispatch(resetImportedData()); + navigate(ROUTE.METRICS_PAGE); + }; return ( @@ -76,5 +76,5 @@ export const HomeGuide = () => { Create a new project - ) -} + ); +}; diff --git a/frontend/src/components/HomeGuide/style.tsx b/frontend/src/components/HomeGuide/style.tsx index 4c5deb8e13..d62e97b986 100644 --- a/frontend/src/components/HomeGuide/style.tsx +++ b/frontend/src/components/HomeGuide/style.tsx @@ -1,7 +1,7 @@ -import { theme } from '@src/theme' -import Button, { ButtonProps } from '@mui/material/Button' -import styled from '@emotion/styled' -import Stack from '@mui/material/Stack' +import { theme } from '@src/theme'; +import Button, { ButtonProps } from '@mui/material/Button'; +import styled from '@emotion/styled'; +import Stack from '@mui/material/Stack'; export const basicStyle = { backgroundColor: theme.main.backgroundColor, @@ -14,7 +14,7 @@ export const basicStyle = { width: '80%', maxWidth: '15rem', }, -} +}; export const GuideButton = styled(Button)({ ...basicStyle, '&:hover': { @@ -26,16 +26,16 @@ export const GuideButton = styled(Button)({ '&:focus': { ...basicStyle, }, -}) +}); export const StyledStack = styled(Stack)({ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', -}) +}); export const HomeGuideContainer = styled.div({ height: '44rem', position: 'relative', -}) +}); diff --git a/frontend/src/components/Loading/index.tsx b/frontend/src/components/Loading/index.tsx index fefd9904d7..1b95071d3c 100644 --- a/frontend/src/components/Loading/index.tsx +++ b/frontend/src/components/Loading/index.tsx @@ -1,10 +1,10 @@ -import { CircularProgress } from '@mui/material' -import { LoadingDrop, LoadingTypography } from './style' +import { CircularProgress } from '@mui/material'; +import { LoadingDrop, LoadingTypography } from './style'; export interface LoadingProps { - message?: string - size?: string - backgroundColor?: string + message?: string; + size?: string; + backgroundColor?: string; } export const Loading = ({ message, size = '8rem', backgroundColor }: LoadingProps) => { @@ -13,5 +13,5 @@ export const Loading = ({ message, size = '8rem', backgroundColor }: LoadingProp {message && {message}} - ) -} + ); +}; diff --git a/frontend/src/components/Loading/style.tsx b/frontend/src/components/Loading/style.tsx index 2e76bb00c6..736ad78df8 100644 --- a/frontend/src/components/Loading/style.tsx +++ b/frontend/src/components/Loading/style.tsx @@ -1,7 +1,7 @@ -import { styled } from '@mui/material/styles' -import { Backdrop, Typography } from '@mui/material' -import { theme } from '@src/theme' -import { Z_INDEX } from '@src/constants/commons' +import { styled } from '@mui/material/styles'; +import { Backdrop, Typography } from '@mui/material'; +import { theme } from '@src/theme'; +import { Z_INDEX } from '@src/constants/commons'; export const LoadingDrop = styled(Backdrop)({ position: 'absolute', @@ -12,10 +12,10 @@ export const LoadingDrop = styled(Backdrop)({ flexDirection: 'column', alignItems: 'center', justifyContent: 'center', -}) +}); export const LoadingTypography = styled(Typography)({ fontSize: '1rem', marginTop: '2rem', color: theme.main.secondColor, -}) +}); diff --git a/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/index.tsx b/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/index.tsx index d3e82f9d51..d9155bbd80 100644 --- a/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/index.tsx @@ -1,53 +1,53 @@ -import { Checkbox, FormHelperText, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent } from '@mui/material' -import { SELECTED_VALUE_SEPARATOR } from '@src/constants/commons' -import { REQUIRED_DATA } from '@src/constants/resources' -import { useEffect, useState } from 'react' -import { RequireDataSelections } from '@src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' -import { selectConfig, updateMetrics } from '@src/context/config/configSlice' +import { Checkbox, FormHelperText, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent } from '@mui/material'; +import { SELECTED_VALUE_SEPARATOR } from '@src/constants/commons'; +import { REQUIRED_DATA } from '@src/constants/resources'; +import { useEffect, useState } from 'react'; +import { RequireDataSelections } from '@src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; +import { selectConfig, updateMetrics } from '@src/context/config/configSlice'; export const RequiredMetrics = () => { - const dispatch = useAppDispatch() - const configData = useAppSelector(selectConfig) - const { metrics } = configData.basic - const [isEmptyRequireData, setIsEmptyProjectData] = useState(false) + const dispatch = useAppDispatch(); + const configData = useAppSelector(selectConfig); + const { metrics } = configData.basic; + const [isEmptyRequireData, setIsEmptyProjectData] = useState(false); useEffect(() => { - metrics && dispatch(updateMetrics(metrics)) - }, [metrics, dispatch]) + metrics && dispatch(updateMetrics(metrics)); + }, [metrics, dispatch]); - const [isAllSelected, setIsAllSelected] = useState(false) + const [isAllSelected, setIsAllSelected] = useState(false); const handleSelectOptionsChange = (selectOptions: string | string[]) => { if (selectOptions.includes(REQUIRED_DATA.All) && !isAllSelected) { - setIsAllSelected(true) - selectOptions = Object.values(REQUIRED_DATA) + setIsAllSelected(true); + selectOptions = Object.values(REQUIRED_DATA); } else if (selectOptions.length == Object.values(REQUIRED_DATA).length - 1 && !isAllSelected) { - setIsAllSelected(true) - selectOptions = Object.values(REQUIRED_DATA) + setIsAllSelected(true); + selectOptions = Object.values(REQUIRED_DATA); } else if (selectOptions.includes(REQUIRED_DATA.All)) { - setIsAllSelected(false) - selectOptions = selectOptions.slice(1) + setIsAllSelected(false); + selectOptions = selectOptions.slice(1); } else if (!selectOptions.includes(REQUIRED_DATA.All) && isAllSelected) { - setIsAllSelected(false) - selectOptions = [] + setIsAllSelected(false); + selectOptions = []; } - return selectOptions - } + return selectOptions; + }; const handleRequireDataChange = (event: SelectChangeEvent) => { const { target: { value }, - } = event + } = event; - dispatch(updateMetrics(handleSelectOptionsChange(value))) - handleSelectOptionsChange(value).length === 0 ? setIsEmptyProjectData(true) : setIsEmptyProjectData(false) - } + dispatch(updateMetrics(handleSelectOptionsChange(value))); + handleSelectOptionsChange(value).length === 0 ? setIsEmptyProjectData(true) : setIsEmptyProjectData(false); + }; const handleRenderSelectOptions = (selected: string[]) => { if (selected.includes(REQUIRED_DATA.All)) { - return selected.slice(1).join(SELECTED_VALUE_SEPARATOR) + return selected.slice(1).join(SELECTED_VALUE_SEPARATOR); } - return selected.join(SELECTED_VALUE_SEPARATOR) - } + return selected.join(SELECTED_VALUE_SEPARATOR); + }; return ( <> @@ -70,5 +70,5 @@ export const RequiredMetrics = () => { {isEmptyRequireData && Metrics is required} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style.tsx b/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style.tsx index 1bcb1d6b7e..d4de907ef2 100644 --- a/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style.tsx +++ b/frontend/src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics/style.tsx @@ -1,8 +1,8 @@ -import { styled } from '@mui/material/styles' -import { FormControl } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { FormControl } from '@mui/material'; export const RequireDataSelections = styled(FormControl)({ width: '100%', paddingBottom: '1rem', marginTop: '1.25rem', -}) +}); diff --git a/frontend/src/components/Metrics/ConfigStep/BasicInfo/index.tsx b/frontend/src/components/Metrics/ConfigStep/BasicInfo/index.tsx index 5260507d14..bcf5e13d4e 100644 --- a/frontend/src/components/Metrics/ConfigStep/BasicInfo/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/BasicInfo/index.tsx @@ -1,10 +1,10 @@ -import { Radio, RadioGroup } from '@mui/material' -import { useState } from 'react' -import { CALENDAR } from '@src/constants/resources' -import { DEFAULT_HELPER_TEXT } from '@src/constants/commons' -import { DateRangePicker } from '@src/components/Metrics/ConfigStep/DateRangePicker' -import { CollectionDateLabel, ProjectNameInput, StyledFormControlLabel } from './style' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' +import { Radio, RadioGroup } from '@mui/material'; +import { useState } from 'react'; +import { CALENDAR } from '@src/constants/resources'; +import { DEFAULT_HELPER_TEXT } from '@src/constants/commons'; +import { DateRangePicker } from '@src/components/Metrics/ConfigStep/DateRangePicker'; +import { CollectionDateLabel, ProjectNameInput, StyledFormControlLabel } from './style'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { selectCalendarType, selectProjectName, @@ -14,18 +14,18 @@ import { updatePipelineToolVerifyState, updateProjectName, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import { ConfigSectionContainer } from '@src/components/Common/ConfigForms' -import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style' -import { RequiredMetrics } from '@src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics' +} from '@src/context/config/configSlice'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import { ConfigSectionContainer } from '@src/components/Common/ConfigForms'; +import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; +import { RequiredMetrics } from '@src/components/Metrics/ConfigStep/BasicInfo/RequiredMetrics'; const BasicInfo = () => { - const dispatch = useAppDispatch() - const projectName = useAppSelector(selectProjectName) - const calendarType = useAppSelector(selectCalendarType) - const warningMessage = useAppSelector(selectWarningMessage) - const [isEmptyProjectName, setIsEmptyProjectName] = useState(false) + const dispatch = useAppDispatch(); + const projectName = useAppSelector(selectProjectName); + const calendarType = useAppSelector(selectCalendarType); + const warningMessage = useAppSelector(selectWarningMessage); + const [isEmptyProjectName, setIsEmptyProjectName] = useState(false); return ( <> @@ -38,11 +38,11 @@ const BasicInfo = () => { variant='standard' value={projectName} onFocus={(e) => { - setIsEmptyProjectName(e.target.value === '') + setIsEmptyProjectName(e.target.value === ''); }} onChange={(e) => { - dispatch(updateProjectName(e.target.value)) - setIsEmptyProjectName(e.target.value === '') + dispatch(updateProjectName(e.target.value)); + setIsEmptyProjectName(e.target.value === ''); }} error={isEmptyProjectName} helperText={isEmptyProjectName ? 'Project name is required' : DEFAULT_HELPER_TEXT} @@ -51,10 +51,10 @@ const BasicInfo = () => { { - dispatch(updateBoardVerifyState(false)) - dispatch(updatePipelineToolVerifyState(false)) - dispatch(updateSourceControlVerifyState(false)) - dispatch(updateCalendarType(e.target.value)) + dispatch(updateBoardVerifyState(false)); + dispatch(updatePipelineToolVerifyState(false)); + dispatch(updateSourceControlVerifyState(false)); + dispatch(updateCalendarType(e.target.value)); }} > } label={CALENDAR.REGULAR} /> @@ -64,7 +64,7 @@ const BasicInfo = () => { - ) -} + ); +}; -export default BasicInfo +export default BasicInfo; diff --git a/frontend/src/components/Metrics/ConfigStep/BasicInfo/style.tsx b/frontend/src/components/Metrics/ConfigStep/BasicInfo/style.tsx index 3b76b1df39..d23aa90e08 100644 --- a/frontend/src/components/Metrics/ConfigStep/BasicInfo/style.tsx +++ b/frontend/src/components/Metrics/ConfigStep/BasicInfo/style.tsx @@ -1,10 +1,10 @@ -import { css, styled } from '@mui/material/styles' -import { FormControlLabel, TextField } from '@mui/material' -import { theme } from '@src/theme' +import { css, styled } from '@mui/material/styles'; +import { FormControlLabel, TextField } from '@mui/material'; +import { theme } from '@src/theme'; export const ProjectNameInput = styled(TextField)({ width: '100%', -}) +}); export const StyledFormControlLabel = styled(FormControlLabel)` ${css` @@ -14,7 +14,7 @@ export const StyledFormControlLabel = styled(FormControlLabel)` } } `} -` +`; export const CollectionDateLabel = styled('div')({ width: '100%', @@ -22,4 +22,4 @@ export const CollectionDateLabel = styled('div')({ fontSize: '0.8rem', lineHeight: '2em', boxSizing: 'border-box', -}) +}); diff --git a/frontend/src/components/Metrics/ConfigStep/Board/index.tsx b/frontend/src/components/Metrics/ConfigStep/Board/index.tsx index 56ef9c1117..710a055db5 100644 --- a/frontend/src/components/Metrics/ConfigStep/Board/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/Board/index.tsx @@ -1,9 +1,9 @@ -import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material' -import { REGEX } from '@src/constants/regex' -import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons' -import { BOARD_TYPES, CONFIG_TITLE, EMAIL, BOARD_TOKEN } from '@src/constants/resources' -import { FormEvent, useEffect, useState } from 'react' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' +import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; +import { REGEX } from '@src/constants/regex'; +import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons'; +import { BOARD_TYPES, CONFIG_TITLE, EMAIL, BOARD_TOKEN } from '@src/constants/resources'; +import { FormEvent, useEffect, useState } from 'react'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { selectBoard, selectDateRange, @@ -12,33 +12,33 @@ import { updateBoard, updateBoardVerifyState, updateJiraVerifyResponse, -} from '@src/context/config/configSlice' -import { useVerifyBoardEffect } from '@src/hooks/useVerifyBoardEffect' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { NoCardPop } from '@src/components/Metrics/ConfigStep/NoDoneCardPop' -import { Loading } from '@src/components/Loading' -import { ResetButton, VerifyButton } from '@src/components/Common/Buttons' +} from '@src/context/config/configSlice'; +import { useVerifyBoardEffect } from '@src/hooks/useVerifyBoardEffect'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { NoCardPop } from '@src/components/Metrics/ConfigStep/NoDoneCardPop'; +import { Loading } from '@src/components/Loading'; +import { ResetButton, VerifyButton } from '@src/components/Common/Buttons'; import { ConfigSectionContainer, StyledButtonGroup, StyledForm, StyledTextField, StyledTypeSelections, -} from '@src/components/Common/ConfigForms' -import dayjs from 'dayjs' -import { updateMetricsState, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice' -import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style' -import { findCaseInsensitiveType } from '@src/utils/util' +} from '@src/components/Common/ConfigForms'; +import dayjs from 'dayjs'; +import { updateMetricsState, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice'; +import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; +import { findCaseInsensitiveType } from '@src/utils/util'; export const Board = () => { - const dispatch = useAppDispatch() - const isVerified = useAppSelector(selectIsBoardVerified) - const boardFields = useAppSelector(selectBoard) - const DateRange = useAppSelector(selectDateRange) - const isProjectCreated = useAppSelector(selectIsProjectCreated) - const [isShowNoDoneCard, setIsNoDoneCard] = useState(false) - const { verifyJira, isLoading, errorMessage } = useVerifyBoardEffect() - const type = findCaseInsensitiveType(Object.values(BOARD_TYPES), boardFields.type) + const dispatch = useAppDispatch(); + const isVerified = useAppSelector(selectIsBoardVerified); + const boardFields = useAppSelector(selectBoard); + const DateRange = useAppSelector(selectDateRange); + const isProjectCreated = useAppSelector(selectIsProjectCreated); + const [isShowNoDoneCard, setIsNoDoneCard] = useState(false); + const { verifyJira, isLoading, errorMessage } = useVerifyBoardEffect(); + const type = findCaseInsensitiveType(Object.values(BOARD_TYPES), boardFields.type); const [fields, setFields] = useState([ { key: 'Board', @@ -76,19 +76,19 @@ export const Board = () => { isRequired: true, isValid: true, }, - ]) + ]); const [isDisableVerifyButton, setIsDisableVerifyButton] = useState( !fields.every((field) => field.value && field.isValid) - ) + ); const initBoardFields = () => { const newFields = fields.map((field, index) => { - field.value = !index ? BOARD_TYPES.JIRA : EMPTY_STRING - return field - }) - setFields(newFields) - dispatch(updateBoardVerifyState(false)) - } + field.value = !index ? BOARD_TYPES.JIRA : EMPTY_STRING; + return field; + }); + setFields(newFields); + dispatch(updateBoardVerifyState(false)); + }; const updateFields = ( fields: { key: string; value: string; isRequired: boolean; isValid: boolean }[], @@ -97,33 +97,33 @@ export const Board = () => { ) => { return fields.map((field, fieldIndex) => { if (fieldIndex !== index) { - return field + return field; } - const newValue = value.trim() - const isValueEmpty = !!newValue + const newValue = value.trim(); + const isValueEmpty = !!newValue; const isValueValid = field.key === EMAIL ? REGEX.EMAIL.test(newValue) : field.key === BOARD_TOKEN ? REGEX.BOARD_TOKEN.test(newValue) - : true + : true; return { ...field, value: newValue, isRequired: isValueEmpty, isValid: isValueValid, - } - }) - } + }; + }); + }; useEffect(() => { const isFieldInvalid = (field: { key: string; value: string; isRequired: boolean; isValid: boolean }) => - field.isRequired && field.isValid && !!field.value + field.isRequired && field.isValid && !!field.value; const isAllFieldsValid = (fields: { key: string; value: string; isRequired: boolean; isValid: boolean }[]) => - fields.some((field) => !isFieldInvalid(field)) - setIsDisableVerifyButton(isAllFieldsValid(fields)) - }, [fields]) + fields.some((field) => !isFieldInvalid(field)); + setIsDisableVerifyButton(isAllFieldsValid(fields)); + }, [fields]); const onFormUpdate = (index: number, value: string) => { const newFieldsValue = !index @@ -133,15 +133,15 @@ export const Board = () => { value: !index ? value : EMPTY_STRING, isValid: true, isRequired: true, - } + }; }) - : updateFields(fields, index, value) - setFields(newFieldsValue) - dispatch(updateBoardVerifyState(false)) - } + : updateFields(fields, index, value); + setFields(newFieldsValue); + dispatch(updateBoardVerifyState(false)); + }; const updateBoardFields = (e: FormEvent) => { - e.preventDefault() + e.preventDefault(); dispatch( updateBoard({ type: fields[0].value, @@ -151,14 +151,14 @@ export const Board = () => { site: fields[4].value, token: fields[5].value, }) - ) - } + ); + }; const handleSubmitBoardFields = async (e: FormEvent) => { - dispatch(updateTreatFlagCardAsBlock(true)) - updateBoardFields(e) - const msg = `${fields[2].value}:${fields[5].value}` - const encodeToken = `Basic ${btoa(msg)}` + dispatch(updateTreatFlagCardAsBlock(true)); + updateBoardFields(e); + const msg = `${fields[2].value}:${fields[5].value}`; + const encodeToken = `Basic ${btoa(msg)}`; const params = { type: fields[0].value, boardId: fields[1].value, @@ -167,33 +167,33 @@ export const Board = () => { token: encodeToken, startTime: dayjs(DateRange.startDate).valueOf(), endTime: dayjs(DateRange.endDate).valueOf(), - } + }; await verifyJira(params).then((res) => { if (res) { - dispatch(updateBoardVerifyState(res.isBoardVerify)) - dispatch(updateJiraVerifyResponse(res.response)) - res.isBoardVerify && dispatch(updateMetricsState({ ...res.response, isProjectCreated })) - setIsNoDoneCard(!res.haveDoneCard) + dispatch(updateBoardVerifyState(res.isBoardVerify)); + dispatch(updateJiraVerifyResponse(res.response)); + res.isBoardVerify && dispatch(updateMetricsState({ ...res.response, isProjectCreated })); + setIsNoDoneCard(!res.haveDoneCard); } - }) - } + }); + }; const handleResetBoardFields = () => { - initBoardFields() - setIsDisableVerifyButton(true) - dispatch(updateBoardVerifyState(false)) - } + initBoardFields(); + setIsDisableVerifyButton(true); + dispatch(updateBoardVerifyState(false)); + }; const updateFieldHelpText = (field: { key: string; isRequired: boolean; isValid: boolean }) => { - const { key, isRequired, isValid } = field + const { key, isRequired, isValid } = field; if (!isRequired) { - return `${key} is required` + return `${key} is required`; } if ((key === EMAIL || key === BOARD_TOKEN) && !isValid) { - return `${key} is invalid` + return `${key} is invalid`; } - return DEFAULT_HELPER_TEXT - } + return DEFAULT_HELPER_TEXT; + }; return ( @@ -214,7 +214,7 @@ export const Board = () => { labelId='board-type-checkbox-label' value={field.value} onChange={(e) => { - onFormUpdate(index, e.target.value) + onFormUpdate(index, e.target.value); }} > {Object.values(BOARD_TYPES).map((data) => ( @@ -233,7 +233,7 @@ export const Board = () => { variant='standard' value={field.value} onChange={(e) => { - onFormUpdate(index, e.target.value) + onFormUpdate(index, e.target.value); }} error={!field.isRequired || !field.isValid} type={field.key === 'Token' ? 'password' : 'text'} @@ -254,5 +254,5 @@ export const Board = () => { - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/BranchSelection/index.tsx b/frontend/src/components/Metrics/ConfigStep/BranchSelection/index.tsx index 3772c1360c..183e4bd34d 100644 --- a/frontend/src/components/Metrics/ConfigStep/BranchSelection/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/BranchSelection/index.tsx @@ -1,33 +1,33 @@ -import React, { useMemo } from 'react' -import _ from 'lodash' -import { BranchSelectionWrapper } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style' -import MultiAutoComplete from '@src/components/Common/MultiAutoComplete' -import { selectBranches } from '@src/context/config/configSlice' -import { store } from '@src/store' +import React, { useMemo } from 'react'; +import _ from 'lodash'; +import { BranchSelectionWrapper } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style'; +import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; +import { selectBranches } from '@src/context/config/configSlice'; +import { store } from '@src/store'; export interface BranchSelectionProps { - id: number - organization: string - pipelineName: string - branches: string[] - onUpdatePipeline: (id: number, label: string, value: any) => void + id: number; + organization: string; + pipelineName: string; + branches: string[]; + onUpdatePipeline: (id: number, label: string, value: any) => void; } export const BranchSelection = (props: BranchSelectionProps) => { - const { id, organization, pipelineName, branches, onUpdatePipeline } = props - const branchesOptions: string[] = selectBranches(store.getState(), organization, pipelineName) + const { id, organization, pipelineName, branches, onUpdatePipeline } = props; + const branchesOptions: string[] = selectBranches(store.getState(), organization, pipelineName); const isAllBranchesSelected = useMemo( () => !_.isEmpty(branchesOptions) && _.isEqual(branches.length, branchesOptions.length), [branches, branchesOptions] - ) + ); const handleBranchChange = (event: React.SyntheticEvent, value: string[]) => { - let selectBranches = value + let selectBranches = value; if (_.isEqual(selectBranches[selectBranches.length - 1], 'All')) { /* istanbul ignore next */ - selectBranches = _.isEqual(branchesOptions.length, branches.length) ? [] : branchesOptions + selectBranches = _.isEqual(branchesOptions.length, branches.length) ? [] : branchesOptions; } - onUpdatePipeline(id, 'Branches', selectBranches) - } + onUpdatePipeline(id, 'Branches', selectBranches); + }; return ( @@ -40,5 +40,5 @@ export const BranchSelection = (props: BranchSelectionProps) => { isSelectAll={isAllBranchesSelected} /> - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/DateRangePicker/index.tsx b/frontend/src/components/Metrics/ConfigStep/DateRangePicker/index.tsx index c510c91a96..02692d0da0 100644 --- a/frontend/src/components/Metrics/ConfigStep/DateRangePicker/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/DateRangePicker/index.tsx @@ -1,26 +1,26 @@ -import dayjs from 'dayjs' -import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' +import dayjs from 'dayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { selectDateRange, updateBoardVerifyState, updateDateRange, updatePipelineToolVerifyState, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import { StyledDateRangePicker, StyledDateRangePickerContainer } from './style' -import CalendarTodayIcon from '@mui/icons-material/CalendarToday' -import { Z_INDEX } from '@src/constants/commons' +} from '@src/context/config/configSlice'; +import { StyledDateRangePicker, StyledDateRangePickerContainer } from './style'; +import CalendarTodayIcon from '@mui/icons-material/CalendarToday'; +import { Z_INDEX } from '@src/constants/commons'; export const DateRangePicker = () => { - const dispatch = useAppDispatch() - const { startDate, endDate } = useAppSelector(selectDateRange) + const dispatch = useAppDispatch(); + const { startDate, endDate } = useAppSelector(selectDateRange); const updateVerifyStates = () => { - dispatch(updateBoardVerifyState(false)) - dispatch(updatePipelineToolVerifyState(false)) - dispatch(updateSourceControlVerifyState(false)) - } + dispatch(updateBoardVerifyState(false)); + dispatch(updatePipelineToolVerifyState(false)); + dispatch(updateSourceControlVerifyState(false)); + }; const changeStartDate = (value: any) => { if (value === null) { dispatch( @@ -28,17 +28,17 @@ export const DateRangePicker = () => { startDate: null, endDate: null, }) - ) + ); } else { dispatch( updateDateRange({ startDate: value.startOf('date').format('YYYY-MM-DDTHH:mm:ss.SSSZ'), endDate: value.endOf('date').add(13, 'day').format('YYYY-MM-DDTHH:mm:ss.SSSZ'), }) - ) + ); } - updateVerifyStates() - } + updateVerifyStates(); + }; const changeEndDate = (value: any) => { if (value === null) { @@ -47,14 +47,14 @@ export const DateRangePicker = () => { startDate: startDate, endDate: null, }) - ) + ); } else { dispatch( updateDateRange({ startDate: startDate, endDate: value.endOf('date').format('YYYY-MM-DDTHH:mm:ss.SSSZ') }) - ) + ); } - updateVerifyStates() - } + updateVerifyStates(); + }; return ( @@ -94,5 +94,5 @@ export const DateRangePicker = () => { /> - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/DateRangePicker/style.tsx b/frontend/src/components/Metrics/ConfigStep/DateRangePicker/style.tsx index 6ff55f7bff..4a00422c0c 100644 --- a/frontend/src/components/Metrics/ConfigStep/DateRangePicker/style.tsx +++ b/frontend/src/components/Metrics/ConfigStep/DateRangePicker/style.tsx @@ -1,6 +1,6 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { DatePicker } from '@mui/x-date-pickers' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; +import { DatePicker } from '@mui/x-date-pickers'; export const StyledDateRangePickerContainer = styled('div')({ display: 'flex', width: '100%', @@ -10,7 +10,7 @@ export const StyledDateRangePickerContainer = styled('div')({ [theme.breakpoints.down('sm')]: { flexDirection: 'column', }, -}) +}); export const StyledDateRangePicker = styled(DatePicker)({ width: '50%', @@ -20,4 +20,4 @@ export const StyledDateRangePicker = styled(DatePicker)({ [theme.breakpoints.down('sm')]: { width: '100%', }, -}) +}); diff --git a/frontend/src/components/Metrics/ConfigStep/MetricsTypeCheckbox/index.tsx b/frontend/src/components/Metrics/ConfigStep/MetricsTypeCheckbox/index.tsx index d02e8558b5..4ed86deb48 100644 --- a/frontend/src/components/Metrics/ConfigStep/MetricsTypeCheckbox/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/MetricsTypeCheckbox/index.tsx @@ -1,14 +1,14 @@ -import { Board } from '@src/components/Metrics/ConfigStep/Board' -import { useAppSelector } from '@src/hooks/useAppDispatch' -import { selectConfig } from '@src/context/config/configSlice' -import { PipelineTool } from '@src/components/Metrics/ConfigStep/PipelineTool' -import { SourceControl } from '@src/components/Metrics/ConfigStep/SourceControl' +import { Board } from '@src/components/Metrics/ConfigStep/Board'; +import { useAppSelector } from '@src/hooks/useAppDispatch'; +import { selectConfig } from '@src/context/config/configSlice'; +import { PipelineTool } from '@src/components/Metrics/ConfigStep/PipelineTool'; +import { SourceControl } from '@src/components/Metrics/ConfigStep/SourceControl'; export const MetricsTypeCheckbox = () => { - const configData = useAppSelector(selectConfig) - const { isShow: isShowBoard } = configData.board - const { isShow: isShowPipeline } = configData.pipelineTool - const { isShow: isShowSourceControl } = configData.sourceControl + const configData = useAppSelector(selectConfig); + const { isShow: isShowBoard } = configData.board; + const { isShow: isShowPipeline } = configData.pipelineTool; + const { isShow: isShowSourceControl } = configData.sourceControl; return ( <> @@ -16,5 +16,5 @@ export const MetricsTypeCheckbox = () => { {isShowPipeline && } {isShowSourceControl && } - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/index.tsx b/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/index.tsx index 14d40c2638..3d33002864 100644 --- a/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/index.tsx @@ -1,13 +1,13 @@ -import { DialogContent } from '@mui/material' -import { OkButton, StyledDialog } from '@src/components/Metrics/ConfigStep/NoDoneCardPop/style' +import { DialogContent } from '@mui/material'; +import { OkButton, StyledDialog } from '@src/components/Metrics/ConfigStep/NoDoneCardPop/style'; interface NoDoneCardPopProps { - isOpen: boolean - onClose: () => void + isOpen: boolean; + onClose: () => void; } export const NoCardPop = (props: NoDoneCardPopProps) => { - const { isOpen, onClose } = props + const { isOpen, onClose } = props; return ( @@ -15,5 +15,5 @@ export const NoCardPop = (props: NoDoneCardPopProps) => { Ok - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/style.tsx b/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/style.tsx index eabcc5f634..a9dbd58e25 100644 --- a/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/style.tsx +++ b/frontend/src/components/Metrics/ConfigStep/NoDoneCardPop/style.tsx @@ -1,6 +1,6 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { Dialog } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; +import { Dialog } from '@mui/material'; export const StyledDialog = styled(Dialog)` & .MuiDialog-paper { @@ -10,7 +10,7 @@ export const StyledDialog = styled(Dialog)` align-items: center; font-size: 1rem; } -` +`; export const OkButton = styled('button')({ width: '4rem', @@ -21,4 +21,4 @@ export const OkButton = styled('button')({ borderRadius: '0.3rem', color: 'White', backgroundColor: theme.main.backgroundColor, -}) +}); diff --git a/frontend/src/components/Metrics/ConfigStep/PipelineTool/index.tsx b/frontend/src/components/Metrics/ConfigStep/PipelineTool/index.tsx index 039b6b18f9..01b1c0ab07 100644 --- a/frontend/src/components/Metrics/ConfigStep/PipelineTool/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/PipelineTool/index.tsx @@ -1,15 +1,15 @@ -import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material' -import { DEFAULT_HELPER_TEXT, EMPTY_STRING, ZERO } from '@src/constants/commons' -import { CONFIG_TITLE, PIPELINE_TOOL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources' -import { FormEvent, useEffect, useState } from 'react' +import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; +import { DEFAULT_HELPER_TEXT, EMPTY_STRING, ZERO } from '@src/constants/commons'; +import { CONFIG_TITLE, PIPELINE_TOOL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources'; +import { FormEvent, useEffect, useState } from 'react'; import { ConfigSectionContainer, StyledButtonGroup, StyledForm, StyledTextField, StyledTypeSelections, -} from '@src/components/Common/ConfigForms' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' +} from '@src/components/Common/ConfigForms'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { isPipelineToolVerified, selectDateRange, @@ -18,24 +18,24 @@ import { updatePipelineTool, updatePipelineToolVerifyResponse, updatePipelineToolVerifyState, -} from '@src/context/config/configSlice' -import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { Loading } from '@src/components/Loading' -import { ResetButton, VerifyButton } from '@src/components/Common/Buttons' -import { initDeploymentFrequencySettings, updatePipelineSettings } from '@src/context/Metrics/metricsSlice' -import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style' -import { findCaseInsensitiveType } from '@src/utils/util' -import { REGEX } from '@src/constants/regex' +} from '@src/context/config/configSlice'; +import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { Loading } from '@src/components/Loading'; +import { ResetButton, VerifyButton } from '@src/components/Common/Buttons'; +import { initDeploymentFrequencySettings, updatePipelineSettings } from '@src/context/Metrics/metricsSlice'; +import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; +import { findCaseInsensitiveType } from '@src/utils/util'; +import { REGEX } from '@src/constants/regex'; export const PipelineTool = () => { - const dispatch = useAppDispatch() - const pipelineToolFields = useAppSelector(selectPipelineTool) - const DateRange = useAppSelector(selectDateRange) - const isVerified = useAppSelector(isPipelineToolVerified) - const isProjectCreated = useAppSelector(selectIsProjectCreated) - const { verifyPipelineTool, isLoading, errorMessage } = useVerifyPipelineToolEffect() - const type = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), pipelineToolFields.type) + const dispatch = useAppDispatch(); + const pipelineToolFields = useAppSelector(selectPipelineTool); + const DateRange = useAppSelector(selectDateRange); + const isVerified = useAppSelector(isPipelineToolVerified); + const isProjectCreated = useAppSelector(selectIsProjectCreated); + const { verifyPipelineTool, isLoading, errorMessage } = useVerifyPipelineToolEffect(); + const type = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), pipelineToolFields.type); const [fields, setFields] = useState([ { key: 'PipelineTool', @@ -49,96 +49,96 @@ export const PipelineTool = () => { isValid: true, isRequired: true, }, - ]) - const [isDisableVerifyButton, setIsDisableVerifyButton] = useState(!(fields[1].isValid && fields[1].value)) + ]); + const [isDisableVerifyButton, setIsDisableVerifyButton] = useState(!(fields[1].isValid && fields[1].value)); const initPipeLineFields = () => { const newFields = fields.map((field, index) => { - field.value = !index ? PIPELINE_TOOL_TYPES.BUILD_KITE : EMPTY_STRING - return field - }) - setFields(newFields) - dispatch(updatePipelineToolVerifyState(false)) - } + field.value = !index ? PIPELINE_TOOL_TYPES.BUILD_KITE : EMPTY_STRING; + return field; + }); + setFields(newFields); + dispatch(updatePipelineToolVerifyState(false)); + }; useEffect(() => { const isFieldInvalid = (field: { key: string; value: string; isRequired: boolean; isValid: boolean }) => - field.isRequired && field.isValid && !!field.value + field.isRequired && field.isValid && !!field.value; const isAllFieldsValid = (fields: { key: string; value: string; isRequired: boolean; isValid: boolean }[]) => - fields.some((field) => !isFieldInvalid(field)) - setIsDisableVerifyButton(isAllFieldsValid(fields)) - }, [fields]) + fields.some((field) => !isFieldInvalid(field)); + setIsDisableVerifyButton(isAllFieldsValid(fields)); + }, [fields]); const onFormUpdate = (index: number, value: string) => { const newFieldsValue = fields.map((field, fieldIndex) => { if (!index) { - field.value = !fieldIndex ? value : EMPTY_STRING + field.value = !fieldIndex ? value : EMPTY_STRING; } else if (!!index && !!fieldIndex) { return { ...field, value, isRequired: !!value, isValid: REGEX.BUILDKITE_TOKEN.test(value), - } + }; } - return field - }) - setFields(newFieldsValue) - dispatch(updatePipelineToolVerifyState(false)) + return field; + }); + setFields(newFieldsValue); + dispatch(updatePipelineToolVerifyState(false)); dispatch( updatePipelineTool({ type: fields[0].value, token: fields[1].value, }) - ) - } + ); + }; const updateFieldHelpText = (field: { key: string; isRequired: boolean; isValid: boolean }) => { - const { isRequired, isValid } = field + const { isRequired, isValid } = field; if (!isRequired) { - return TOKEN_HELPER_TEXT.RequiredTokenText + return TOKEN_HELPER_TEXT.RequiredTokenText; } if (!isValid) { - return TOKEN_HELPER_TEXT.InvalidTokenText + return TOKEN_HELPER_TEXT.InvalidTokenText; } - return DEFAULT_HELPER_TEXT - } + return DEFAULT_HELPER_TEXT; + }; const updatePipelineToolFields = (e: FormEvent) => { - e.preventDefault() + e.preventDefault(); dispatch( updatePipelineTool({ type: fields[0].value, token: fields[1].value, }) - ) - } + ); + }; const handleSubmitPipelineToolFields = async (e: FormEvent) => { - updatePipelineToolFields(e) + updatePipelineToolFields(e); const params = { type: fields[0].value, token: fields[1].value, startTime: DateRange.startDate, endTime: DateRange.endDate, - } + }; await verifyPipelineTool(params).then((res) => { if (res) { - dispatch(updatePipelineToolVerifyState(res.isPipelineToolVerified)) - dispatch(updatePipelineToolVerifyResponse(res.response)) - dispatch(initDeploymentFrequencySettings()) - res.isPipelineToolVerified && dispatch(updatePipelineSettings({ ...res.response, isProjectCreated })) + dispatch(updatePipelineToolVerifyState(res.isPipelineToolVerified)); + dispatch(updatePipelineToolVerifyResponse(res.response)); + dispatch(initDeploymentFrequencySettings()); + res.isPipelineToolVerified && dispatch(updatePipelineSettings({ ...res.response, isProjectCreated })); } - }) - } + }); + }; const handleResetPipelineToolFields = () => { - initPipeLineFields() - setIsDisableVerifyButton(true) - dispatch(updatePipelineToolVerifyState(false)) - } + initPipeLineFields(); + setIsDisableVerifyButton(true); + dispatch(updatePipelineToolVerifyState(false)); + }; return ( @@ -188,5 +188,5 @@ export const PipelineTool = () => { - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/SourceControl/index.tsx b/frontend/src/components/Metrics/ConfigStep/SourceControl/index.tsx index 5825ba8a19..1820321a4f 100644 --- a/frontend/src/components/Metrics/ConfigStep/SourceControl/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/SourceControl/index.tsx @@ -1,37 +1,37 @@ -import { FormEvent, useEffect, useState } from 'react' -import { REGEX } from '@src/constants/regex' -import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons' -import { CONFIG_TITLE, SOURCE_CONTROL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources' +import { FormEvent, useEffect, useState } from 'react'; +import { REGEX } from '@src/constants/regex'; +import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons'; +import { CONFIG_TITLE, SOURCE_CONTROL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources'; import { ConfigSectionContainer, StyledButtonGroup, StyledForm, StyledTextField, StyledTypeSelections, -} from '@src/components/Common/ConfigForms' -import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' +} from '@src/components/Common/ConfigForms'; +import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { isSourceControlVerified, selectDateRange, selectSourceControl, updateSourceControl, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import { useVerifySourceControlEffect } from '@src/hooks/useVeritySourceControlEffect' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { Loading } from '@src/components/Loading' -import { VerifyButton, ResetButton } from '@src/components/Common/Buttons' -import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style' -import { findCaseInsensitiveType } from '@src/utils/util' +} from '@src/context/config/configSlice'; +import { useVerifySourceControlEffect } from '@src/hooks/useVeritySourceControlEffect'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { Loading } from '@src/components/Loading'; +import { VerifyButton, ResetButton } from '@src/components/Common/Buttons'; +import { ConfigSelectionTitle } from '@src/components/Metrics/MetricsStep/style'; +import { findCaseInsensitiveType } from '@src/utils/util'; export const SourceControl = () => { - const dispatch = useAppDispatch() - const sourceControlFields = useAppSelector(selectSourceControl) - const DateRange = useAppSelector(selectDateRange) - const isVerified = useAppSelector(isSourceControlVerified) - const { verifyGithub, isLoading, errorMessage } = useVerifySourceControlEffect() - const type = findCaseInsensitiveType(Object.values(SOURCE_CONTROL_TYPES), sourceControlFields.type) + const dispatch = useAppDispatch(); + const sourceControlFields = useAppSelector(selectSourceControl); + const DateRange = useAppSelector(selectDateRange); + const isVerified = useAppSelector(isSourceControlVerified); + const { verifyGithub, isLoading, errorMessage } = useVerifySourceControlEffect(); + const type = findCaseInsensitiveType(Object.values(SOURCE_CONTROL_TYPES), sourceControlFields.type); const [fields, setFields] = useState([ { key: 'SourceControl', @@ -45,83 +45,83 @@ export const SourceControl = () => { isValid: true, isRequired: true, }, - ]) - const [isDisableVerifyButton, setIsDisableVerifyButton] = useState(!(fields[1].isValid && fields[1].value)) - const [sourceControlHelperText, setSourceControlHelperText] = useState('') + ]); + const [isDisableVerifyButton, setIsDisableVerifyButton] = useState(!(fields[1].isValid && fields[1].value)); + const [sourceControlHelperText, setSourceControlHelperText] = useState(''); const initSourceControlFields = () => { const newFields = fields.map((field, index) => { - field.value = index === 1 ? '' : SOURCE_CONTROL_TYPES.GITHUB - return field - }) - setFields(newFields) - dispatch(updateSourceControlVerifyState(false)) - } + field.value = index === 1 ? '' : SOURCE_CONTROL_TYPES.GITHUB; + return field; + }); + setFields(newFields); + dispatch(updateSourceControlVerifyState(false)); + }; useEffect(() => { const isFieldInvalid = (field: { key: string; value: string; isRequired: boolean; isValid: boolean }) => - field.isRequired && field.isValid && !!field.value + field.isRequired && field.isValid && !!field.value; const isAllFieldsValid = (fields: { key: string; value: string; isRequired: boolean; isValid: boolean }[]) => - fields.some((field) => !isFieldInvalid(field)) - setIsDisableVerifyButton(isAllFieldsValid(fields)) - }, [fields]) + fields.some((field) => !isFieldInvalid(field)); + setIsDisableVerifyButton(isAllFieldsValid(fields)); + }, [fields]); const updateSourceControlFields = (e: FormEvent) => { - e.preventDefault() + e.preventDefault(); dispatch( updateSourceControl({ type: fields[0].value, token: fields[1].value, }) - ) - } + ); + }; const handleSubmitSourceControlFields = async (e: FormEvent) => { - updateSourceControlFields(e) + updateSourceControlFields(e); const params = { type: fields[0].value, token: fields[1].value, startTime: DateRange.startDate, endTime: DateRange.endDate, - } + }; await verifyGithub(params).then((res) => { if (res) { - dispatch(updateSourceControlVerifyState(res.isSourceControlVerify)) - dispatch(updateSourceControlVerifyState(res.response)) + dispatch(updateSourceControlVerifyState(res.isSourceControlVerify)); + dispatch(updateSourceControlVerifyState(res.response)); } - }) - } + }); + }; const handleResetSourceControlFields = () => { - initSourceControlFields() - setIsDisableVerifyButton(true) - dispatch(updateSourceControlVerifyState(false)) - } + initSourceControlFields(); + setIsDisableVerifyButton(true); + dispatch(updateSourceControlVerifyState(false)); + }; const checkFieldValid = (value: string): boolean => { - let helperText = DEFAULT_HELPER_TEXT + let helperText = DEFAULT_HELPER_TEXT; if (value === EMPTY_STRING) { - helperText = TOKEN_HELPER_TEXT.RequiredTokenText + helperText = TOKEN_HELPER_TEXT.RequiredTokenText; } else if (!REGEX.GITHUB_TOKEN.test(value)) { - helperText = TOKEN_HELPER_TEXT.InvalidTokenText + helperText = TOKEN_HELPER_TEXT.InvalidTokenText; } - setSourceControlHelperText(helperText) - return helperText === DEFAULT_HELPER_TEXT - } + setSourceControlHelperText(helperText); + return helperText === DEFAULT_HELPER_TEXT; + }; const onFormUpdate = (index: number, value: string) => { const newFieldsValue = fields.map((field, fieldIndex) => { if (fieldIndex === index) { - field.value = value - field.isValid = checkFieldValid(value) + field.value = value; + field.isValid = checkFieldValid(value); } - return field - }) - setFields(newFieldsValue) - dispatch(updateSourceControlVerifyState(false)) - } + return field; + }); + setFields(newFieldsValue); + dispatch(updateSourceControlVerifyState(false)); + }; return ( @@ -169,5 +169,5 @@ export const SourceControl = () => { - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ConfigStep/index.tsx b/frontend/src/components/Metrics/ConfigStep/index.tsx index 5ef3757b32..f16ceaa493 100644 --- a/frontend/src/components/Metrics/ConfigStep/index.tsx +++ b/frontend/src/components/Metrics/ConfigStep/index.tsx @@ -1,20 +1,20 @@ -import { ConfigStepWrapper } from './style' -import { MetricsTypeCheckbox } from '@src/components/Metrics/ConfigStep/MetricsTypeCheckbox' -import BasicInfo from '@src/components/Metrics/ConfigStep/BasicInfo' -import { useLayoutEffect } from 'react' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' +import { ConfigStepWrapper } from './style'; +import { MetricsTypeCheckbox } from '@src/components/Metrics/ConfigStep/MetricsTypeCheckbox'; +import BasicInfo from '@src/components/Metrics/ConfigStep/BasicInfo'; +import { useLayoutEffect } from 'react'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; const ConfigStep = ({ resetProps }: useNotificationLayoutEffectInterface) => { useLayoutEffect(() => { - resetProps?.() - }, []) + resetProps?.(); + }, []); return ( - ) -} + ); +}; -export default ConfigStep +export default ConfigStep; diff --git a/frontend/src/components/Metrics/ConfigStep/style.tsx b/frontend/src/components/Metrics/ConfigStep/style.tsx index e6a2ce734c..c4685c3f91 100644 --- a/frontend/src/components/Metrics/ConfigStep/style.tsx +++ b/frontend/src/components/Metrics/ConfigStep/style.tsx @@ -1,5 +1,5 @@ -import { styled } from '@mui/material/styles' +import { styled } from '@mui/material/styles'; export const ConfigStepWrapper = styled('div')({ width: '100%', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/Classification/index.tsx b/frontend/src/components/Metrics/MetricsStep/Classification/index.tsx index 185ca54321..89ee5d98bc 100644 --- a/frontend/src/components/Metrics/MetricsStep/Classification/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/Classification/index.tsx @@ -1,25 +1,25 @@ -import React, { useState } from 'react' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { saveTargetFields, selectClassificationWarningMessage } from '@src/context/Metrics/metricsSlice' -import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle' -import { useAppSelector } from '@src/hooks' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import MultiAutoComplete from '@src/components/Common/MultiAutoComplete' +import React, { useState } from 'react'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { saveTargetFields, selectClassificationWarningMessage } from '@src/context/Metrics/metricsSlice'; +import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle'; +import { useAppSelector } from '@src/hooks'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; interface classificationProps { - title: string - label: string - targetFields: { name: string; key: string; flag: boolean }[] + title: string; + label: string; + targetFields: { name: string; key: string; flag: boolean }[]; } export const Classification = ({ targetFields, title, label }: classificationProps) => { - const dispatch = useAppDispatch() - const classificationWarningMessage = useAppSelector(selectClassificationWarningMessage) + const dispatch = useAppDispatch(); + const classificationWarningMessage = useAppSelector(selectClassificationWarningMessage); const classificationSettings = targetFields .filter((targetField) => targetField.flag) - .map((targetField) => targetField.name) - const [selectedClassification, setSelectedClassification] = useState(classificationSettings) - const isAllSelected = selectedClassification.length > 0 && selectedClassification.length === targetFields.length + .map((targetField) => targetField.name); + const [selectedClassification, setSelectedClassification] = useState(classificationSettings); + const isAllSelected = selectedClassification.length > 0 && selectedClassification.length === targetFields.length; const handleChange = (event: React.SyntheticEvent, value: string[]) => { const newClassificationSettings = @@ -27,14 +27,14 @@ export const Classification = ({ targetFields, title, label }: classificationPro ? isAllSelected ? [] : targetFields.map((targetField) => targetField.name) - : [...value] + : [...value]; const updatedTargetFields = targetFields.map((targetField) => ({ ...targetField, flag: newClassificationSettings.includes(targetField.name), - })) - setSelectedClassification(newClassificationSettings) - dispatch(saveTargetFields(updatedTargetFields)) - } + })); + setSelectedClassification(newClassificationSettings); + dispatch(saveTargetFields(updatedTargetFields)); + }; return ( <> @@ -49,5 +49,5 @@ export const Classification = ({ targetFields, title, label }: classificationPro isSelectAll={isAllSelected} /> - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/Crews/AssigneeFilter.tsx b/frontend/src/components/Metrics/MetricsStep/Crews/AssigneeFilter.tsx index 3833ba6efd..5f446fe5fb 100644 --- a/frontend/src/components/Metrics/MetricsStep/Crews/AssigneeFilter.tsx +++ b/frontend/src/components/Metrics/MetricsStep/Crews/AssigneeFilter.tsx @@ -1,17 +1,17 @@ -import { FormControlLabel, RadioGroup, Radio } from '@mui/material' -import React from 'react' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { useAppSelector } from '@src/hooks' -import { selectAssigneeFilter, updateAssigneeFilter } from '@src/context/Metrics/metricsSlice' -import { AssigneeFilterContainer } from '@src/components/Metrics/MetricsStep/Crews/style' +import { FormControlLabel, RadioGroup, Radio } from '@mui/material'; +import React from 'react'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { useAppSelector } from '@src/hooks'; +import { selectAssigneeFilter, updateAssigneeFilter } from '@src/context/Metrics/metricsSlice'; +import { AssigneeFilterContainer } from '@src/components/Metrics/MetricsStep/Crews/style'; export const AssigneeFilter = () => { - const dispatch = useAppDispatch() - const assigneeFilter = useAppSelector(selectAssigneeFilter) + const dispatch = useAppDispatch(); + const assigneeFilter = useAppSelector(selectAssigneeFilter); const handleChange = (event: React.ChangeEvent) => { - dispatch(updateAssigneeFilter(event.target.value)) - } + dispatch(updateAssigneeFilter(event.target.value)); + }; return ( @@ -26,5 +26,5 @@ export const AssigneeFilter = () => { } label='Historical assignee' /> - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/Crews/index.tsx b/frontend/src/components/Metrics/MetricsStep/Crews/index.tsx index e4ad1ffa30..d7505183f9 100644 --- a/frontend/src/components/Metrics/MetricsStep/Crews/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/Crews/index.tsx @@ -1,43 +1,43 @@ -import { FormHelperText } from '@mui/material' -import React, { useEffect, useState } from 'react' -import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { saveUsers, selectMetricsContent, savePipelineCrews } from '@src/context/Metrics/metricsSlice' -import { useAppSelector } from '@src/hooks' -import { AssigneeFilter } from '@src/components/Metrics/MetricsStep/Crews/AssigneeFilter' -import MultiAutoComplete from '@src/components/Common/MultiAutoComplete' -import { WarningMessage } from '@src/components/Metrics/MetricsStep/Crews/style' +import { FormHelperText } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { saveUsers, selectMetricsContent, savePipelineCrews } from '@src/context/Metrics/metricsSlice'; +import { useAppSelector } from '@src/hooks'; +import { AssigneeFilter } from '@src/components/Metrics/MetricsStep/Crews/AssigneeFilter'; +import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; +import { WarningMessage } from '@src/components/Metrics/MetricsStep/Crews/style'; interface crewsProps { - options: string[] - title: string - label: string - type?: string + options: string[]; + title: string; + label: string; + type?: string; } export const Crews = ({ options, title, label, type = 'board' }: crewsProps) => { - const isBoardCrews = type === 'board' - const dispatch = useAppDispatch() - const [isEmptyCrewData, setIsEmptyCrewData] = useState(false) - const { users, pipelineCrews } = useAppSelector(selectMetricsContent) - const [selectedCrews, setSelectedCrews] = useState(isBoardCrews ? users : pipelineCrews) - const isAllSelected = options.length > 0 && selectedCrews.length === options.length + const isBoardCrews = type === 'board'; + const dispatch = useAppDispatch(); + const [isEmptyCrewData, setIsEmptyCrewData] = useState(false); + const { users, pipelineCrews } = useAppSelector(selectMetricsContent); + const [selectedCrews, setSelectedCrews] = useState(isBoardCrews ? users : pipelineCrews); + const isAllSelected = options.length > 0 && selectedCrews.length === options.length; useEffect(() => { - setIsEmptyCrewData(selectedCrews.length === 0) - }, [selectedCrews]) + setIsEmptyCrewData(selectedCrews.length === 0); + }, [selectedCrews]); const handleCrewChange = (event: React.SyntheticEvent, value: string[]) => { if (value[value.length - 1] === 'All') { - setSelectedCrews(selectedCrews.length === options.length ? [] : options) - return + setSelectedCrews(selectedCrews.length === options.length ? [] : options); + return; } - setSelectedCrews([...value]) - } + setSelectedCrews([...value]); + }; useEffect(() => { - dispatch(isBoardCrews ? saveUsers(selectedCrews) : savePipelineCrews(selectedCrews)) - }, [selectedCrews, dispatch]) + dispatch(isBoardCrews ? saveUsers(selectedCrews) : savePipelineCrews(selectedCrews)); + }, [selectedCrews, dispatch]); return ( <> @@ -60,5 +60,5 @@ export const Crews = ({ options, title, label, type = 'board' }: crewsProps) => )} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/Crews/style.tsx b/frontend/src/components/Metrics/MetricsStep/Crews/style.tsx index 54fc7f234f..70237504f2 100644 --- a/frontend/src/components/Metrics/MetricsStep/Crews/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/Crews/style.tsx @@ -1,10 +1,10 @@ -import { styled } from '@mui/material/styles' +import { styled } from '@mui/material/styles'; export const AssigneeFilterContainer = styled('div')({ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', paddingTop: '1rem', -}) +}); export const WarningMessage = styled('span')({ color: 'red', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/FlagCard.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/FlagCard.tsx index 1754e11821..c05c955fc0 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/FlagCard.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/FlagCard.tsx @@ -1,23 +1,23 @@ -import React from 'react' -import { FlagCardItem, ItemCheckbox, ItemText } from '@src/components/Metrics/MetricsStep/CycleTime/style' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { selectTreatFlagCardAsBlock, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice' -import { useAppSelector } from '@src/hooks' +import React from 'react'; +import { FlagCardItem, ItemCheckbox, ItemText } from '@src/components/Metrics/MetricsStep/CycleTime/style'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { selectTreatFlagCardAsBlock, updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice'; +import { useAppSelector } from '@src/hooks'; const FlagCard = () => { - const dispatch = useAppDispatch() - const flagCardAsBlock = useAppSelector(selectTreatFlagCardAsBlock) + const dispatch = useAppDispatch(); + const flagCardAsBlock = useAppSelector(selectTreatFlagCardAsBlock); const handleFlagCardAsBlock = () => { - dispatch(updateTreatFlagCardAsBlock(!flagCardAsBlock)) - } + dispatch(updateTreatFlagCardAsBlock(!flagCardAsBlock)); + }; return ( Consider the "Flag" as "Block" - ) -} + ); +}; -export default FlagCard +export default FlagCard; diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete.tsx index be884d69f5..48d80187fd 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete.tsx @@ -1,35 +1,35 @@ -import React, { useState, useCallback, SyntheticEvent } from 'react' -import { Autocomplete } from '@mui/material' -import { StyledTextField } from '@src/components/Metrics/MetricsStep/CycleTime/Table/style' -import { CYCLE_TIME_LIST } from '@src/constants/resources' -import { Z_INDEX } from '@src/constants/commons' +import React, { useState, useCallback, SyntheticEvent } from 'react'; +import { Autocomplete } from '@mui/material'; +import { StyledTextField } from '@src/components/Metrics/MetricsStep/CycleTime/Table/style'; +import { CYCLE_TIME_LIST } from '@src/constants/resources'; +import { Z_INDEX } from '@src/constants/commons'; interface ICellAutoCompleteProps { - name: string - defaultSelected: string - onSelect: (name: string, value: string) => void - customRenderInput?: React.FC + name: string; + defaultSelected: string; + onSelect: (name: string, value: string) => void; + customRenderInput?: React.FC; } const CellAutoComplete = ({ name, defaultSelected, onSelect, customRenderInput }: ICellAutoCompleteProps) => { - const [selectedCycleTime, setSelectedCycleTime] = useState(defaultSelected) - const [inputValue, setInputValue] = useState('') + const [selectedCycleTime, setSelectedCycleTime] = useState(defaultSelected); + const [inputValue, setInputValue] = useState(''); const handleInputOnChange = useCallback( (event: SyntheticEvent, newInputValue: string) => { - setInputValue(newInputValue) + setInputValue(newInputValue); }, [setInputValue] - ) + ); const handleSelectOnChange = useCallback( (event: SyntheticEvent, value: string) => { - onSelect(name, value) - setSelectedCycleTime(value) + onSelect(name, value); + setSelectedCycleTime(value); }, [name, onSelect, setSelectedCycleTime] - ) + ); - const renderInput = customRenderInput || ((params) => ) + const renderInput = customRenderInput || ((params) => ); return ( - ) -} + ); +}; -export default CellAutoComplete +export default CellAutoComplete; diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/index.tsx index d0123161c9..46003a8c9b 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/index.tsx @@ -1,19 +1,19 @@ -import React, { useCallback } from 'react' -import Table from '@mui/material/Table' -import TableBody from '@mui/material/TableBody' -import TableContainer from '@mui/material/TableContainer' -import TableHead from '@mui/material/TableHead' -import TableRow from '@mui/material/TableRow' -import Tooltip from '@mui/material/Tooltip' -import { useAppSelector } from '@src/hooks' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { saveCycleTimeSettings, saveDoneColumn, selectMetricsContent } from '@src/context/Metrics/metricsSlice' -import { selectJiraColumns } from '@src/context/config/configSlice' -import { DONE, METRICS_CYCLE_SETTING_TABLE_HEADER } from '@src/constants/resources' -import { theme } from '@src/theme' -import CellAutoComplete from '@src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete' -import { StyledTableHeaderCell, StyledTableRowCell } from '@src/components/Metrics/MetricsStep/CycleTime/Table/style' -import EllipsisText from '@src/components/Common/EllipsisText' +import React, { useCallback } from 'react'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Tooltip from '@mui/material/Tooltip'; +import { useAppSelector } from '@src/hooks'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { saveCycleTimeSettings, saveDoneColumn, selectMetricsContent } from '@src/context/Metrics/metricsSlice'; +import { selectJiraColumns } from '@src/context/config/configSlice'; +import { DONE, METRICS_CYCLE_SETTING_TABLE_HEADER } from '@src/constants/resources'; +import { theme } from '@src/theme'; +import CellAutoComplete from '@src/components/Metrics/MetricsStep/CycleTime/Table/CellAutoComplete'; +import { StyledTableHeaderCell, StyledTableRowCell } from '@src/components/Metrics/MetricsStep/CycleTime/Table/style'; +import EllipsisText from '@src/components/Common/EllipsisText'; export const columns = METRICS_CYCLE_SETTING_TABLE_HEADER.map( (config) => @@ -25,36 +25,36 @@ export const columns = METRICS_CYCLE_SETTING_TABLE_HEADER.map( ) : ( config.text - ) + ); } -) +); const CycleTimeTable = () => { - const dispatch = useAppDispatch() - const { cycleTimeSettings } = useAppSelector(selectMetricsContent) - const jiraColumns = useAppSelector(selectJiraColumns) + const dispatch = useAppDispatch(); + const { cycleTimeSettings } = useAppSelector(selectMetricsContent); + const jiraColumns = useAppSelector(selectJiraColumns); const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value - ) + ); const rows = cycleTimeSettings.map((setting) => ({ ...setting, statuses: jiraColumnsWithValue.find((columnWithValue) => columnWithValue.name === setting.name)?.statuses, - })) + })); const resetRealDoneColumn = useCallback( (name: string, value: string) => { - const optionNamesWithDone = cycleTimeSettings.filter((item) => item.value === DONE).map((item) => item.name) + const optionNamesWithDone = cycleTimeSettings.filter((item) => item.value === DONE).map((item) => item.name); if (value === DONE) { - dispatch(saveDoneColumn([])) + dispatch(saveDoneColumn([])); } if (optionNamesWithDone.includes(name)) { - dispatch(saveDoneColumn([])) + dispatch(saveDoneColumn([])); } }, [cycleTimeSettings, dispatch] - ) + ); const saveCycleTimeOptions = useCallback( (name: string, value: string) => { const newCycleTimeSettings = cycleTimeSettings.map((item) => @@ -64,13 +64,13 @@ const CycleTimeTable = () => { value, } : item - ) + ); - resetRealDoneColumn(name, value) - dispatch(saveCycleTimeSettings(newCycleTimeSettings)) + resetRealDoneColumn(name, value); + dispatch(saveCycleTimeSettings(newCycleTimeSettings)); }, [cycleTimeSettings, dispatch, resetRealDoneColumn] - ) + ); return ( @@ -86,8 +86,8 @@ const CycleTimeTable = () => { {rows.map((row, index) => { - const row1Content = row.name - const row2Content = row.statuses?.join(', ') + const row1Content = row.name; + const row2Content = row.statuses?.join(', '); return ( {row1Content} @@ -100,12 +100,12 @@ const CycleTimeTable = () => { - ) + ); })} - ) -} + ); +}; -export default CycleTimeTable +export default CycleTimeTable; diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/style.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/style.tsx index d9f87389f3..730b2c2bff 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/Table/style.tsx @@ -1,7 +1,7 @@ -import { styled } from '@mui/material/styles' -import TableCell from '@mui/material/TableCell' -import TextField from '@mui/material/TextField' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import TableCell from '@mui/material/TableCell'; +import TextField from '@mui/material/TextField'; +import { theme } from '@src/theme'; export const StyledTableHeaderCell = styled(TableCell)({ padding: 0, @@ -16,7 +16,7 @@ export const StyledTableHeaderCell = styled(TableCell)({ paddingLeft: '1.5rem', }, borderBottom: 0, -}) +}); export const StyledTableRowCell = styled(TableCell)({ padding: 0, @@ -27,7 +27,7 @@ export const StyledTableRowCell = styled(TableCell)({ paddingLeft: '1rem', paddingRight: '1rem', borderBottom: `0.1rem solid ${theme.palette.secondary.dark}`, -}) +}); export const StyledTextField = styled(TextField)({ borderBottom: 0, @@ -58,4 +58,4 @@ export const StyledTextField = styled(TextField)({ '& .MuiOutlinedInput-notchedOutline': { borderColor: 'transparent', }, -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/index.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/index.tsx index 82788d3e4f..60c199fbde 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/index.tsx @@ -1,20 +1,20 @@ -import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle' -import FlagCard from '@src/components/Metrics/MetricsStep/CycleTime/FlagCard' -import { selectCycleTimeWarningMessage } from '@src/context/Metrics/metricsSlice' -import { useAppSelector } from '@src/hooks' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import { TIPS } from '@src/constants/resources' +import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle'; +import FlagCard from '@src/components/Metrics/MetricsStep/CycleTime/FlagCard'; +import { selectCycleTimeWarningMessage } from '@src/context/Metrics/metricsSlice'; +import { useAppSelector } from '@src/hooks'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import { TIPS } from '@src/constants/resources'; import { StyledTooltip, TitleAndTooltipContainer, TooltipContainer, -} from '@src/components/Metrics/MetricsStep/CycleTime/style' -import { IconButton } from '@mui/material' -import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined' -import CycleTimeTable from '@src/components/Metrics/MetricsStep/CycleTime/Table' +} from '@src/components/Metrics/MetricsStep/CycleTime/style'; +import { IconButton } from '@mui/material'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import CycleTimeTable from '@src/components/Metrics/MetricsStep/CycleTime/Table'; export const CycleTime = () => { - const warningMessage = useAppSelector(selectCycleTimeWarningMessage) + const warningMessage = useAppSelector(selectCycleTimeWarningMessage); return (
    @@ -32,5 +32,5 @@ export const CycleTime = () => {
    - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/CycleTime/style.tsx b/frontend/src/components/Metrics/MetricsStep/CycleTime/style.tsx index 9f934886bb..723c9d95a5 100644 --- a/frontend/src/components/Metrics/MetricsStep/CycleTime/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/CycleTime/style.tsx @@ -1,36 +1,36 @@ -import { styled } from '@mui/material/styles' -import { Checkbox, Tooltip } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { Checkbox, Tooltip } from '@mui/material'; export const FlagCardItem = styled('div')({ display: 'flex', margin: '0.5rem 0', -}) +}); export const ItemText = styled('div')({ padding: '0', fontSize: '1rem', lineHeight: '1.5rem', fontWeight: '400', -}) +}); export const ItemCheckbox = styled(Checkbox)({ padding: 0, marginRight: '0.5rem', -}) +}); export const TitleAndTooltipContainer = styled('div')({ display: 'flex', flexDirection: 'row', alignItems: 'center', -}) +}); export const TooltipContainer = styled('div')({ marginLeft: '0.25rem', -}) +}); export const StyledTooltip = styled(({ className, ...props }: any) => ( ))(` max-width: 31.25rem; margin-top: 0.625rem; -`) +`); diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx index 172b2b1636..e0cdc70160 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx @@ -1,41 +1,41 @@ -import React, { useState } from 'react' -import { SingleSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection' -import { useAppDispatch } from '@src/hooks' -import { ButtonWrapper, PipelineMetricSelectionWrapper, RemoveButton, WarningMessage } from './style' -import { Loading } from '@src/components/Loading' -import { useGetMetricsStepsEffect } from '@src/hooks/useGetMetricsStepsEffect' -import { ErrorNotification } from '@src/components/ErrorNotification' +import React, { useState } from 'react'; +import { SingleSelection } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection'; +import { useAppDispatch } from '@src/hooks'; +import { ButtonWrapper, PipelineMetricSelectionWrapper, RemoveButton, WarningMessage } from './style'; +import { Loading } from '@src/components/Loading'; +import { useGetMetricsStepsEffect } from '@src/hooks/useGetMetricsStepsEffect'; +import { ErrorNotification } from '@src/components/ErrorNotification'; import { selectPipelineNames, selectPipelineOrganizations, selectSteps, selectStepsParams, updatePipelineToolVerifyResponseSteps, -} from '@src/context/config/configSlice' -import { store } from '@src/store' +} from '@src/context/config/configSlice'; +import { store } from '@src/store'; import { selectOrganizationWarningMessage, selectPipelineNameWarningMessage, selectStepWarningMessage, updatePipelineStep, -} from '@src/context/Metrics/metricsSlice' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import { BranchSelection } from '@src/components/Metrics/ConfigStep/BranchSelection' -import { MESSAGE } from '@src/constants/resources' +} from '@src/context/Metrics/metricsSlice'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import { BranchSelection } from '@src/components/Metrics/ConfigStep/BranchSelection'; +import { MESSAGE } from '@src/constants/resources'; interface pipelineMetricSelectionProps { - type: string + type: string; pipelineSetting: { - id: number - organization: string - pipelineName: string - step: string - branches: string[] - } - isShowRemoveButton: boolean - onRemovePipeline: (id: number) => void - onUpdatePipeline: (id: number, label: string, value: any) => void - isDuplicated: boolean + id: number; + organization: string; + pipelineName: string; + step: string; + branches: string[]; + }; + isShowRemoveButton: boolean; + onRemovePipeline: (id: number) => void; + onUpdatePipeline: (id: number, label: string, value: any) => void; + isDuplicated: boolean; } export const PipelineMetricSelection = ({ @@ -46,34 +46,34 @@ export const PipelineMetricSelection = ({ onUpdatePipeline, isDuplicated, }: pipelineMetricSelectionProps) => { - const { id, organization, pipelineName, step } = pipelineSetting - const dispatch = useAppDispatch() - const { isLoading, errorMessage, getSteps } = useGetMetricsStepsEffect() - const organizationNameOptions = selectPipelineOrganizations(store.getState()) - const pipelineNameOptions = selectPipelineNames(store.getState(), organization) - const stepsOptions = selectSteps(store.getState(), organization, pipelineName) - const organizationWarningMessage = selectOrganizationWarningMessage(store.getState(), id, type) - const pipelineNameWarningMessage = selectPipelineNameWarningMessage(store.getState(), id, type) - const stepWarningMessage = selectStepWarningMessage(store.getState(), id, type) - const [isShowNoStepWarning, setIsShowNoStepWarning] = useState(false) + const { id, organization, pipelineName, step } = pipelineSetting; + const dispatch = useAppDispatch(); + const { isLoading, errorMessage, getSteps } = useGetMetricsStepsEffect(); + const organizationNameOptions = selectPipelineOrganizations(store.getState()); + const pipelineNameOptions = selectPipelineNames(store.getState(), organization); + const stepsOptions = selectSteps(store.getState(), organization, pipelineName); + const organizationWarningMessage = selectOrganizationWarningMessage(store.getState(), id, type); + const pipelineNameWarningMessage = selectPipelineNameWarningMessage(store.getState(), id, type); + const stepWarningMessage = selectStepWarningMessage(store.getState(), id, type); + const [isShowNoStepWarning, setIsShowNoStepWarning] = useState(false); const handleRemoveClick = () => { - onRemovePipeline(id) - } + onRemovePipeline(id); + }; const handleGetPipelineData = (_pipelineName: string) => { const { params, buildId, organizationId, pipelineType, token } = selectStepsParams( store.getState(), organization, _pipelineName - ) + ); getSteps(params, organizationId, buildId, pipelineType, token).then((res) => { if (res && !res.haveStep) { - isShowRemoveButton && handleRemoveClick() + isShowRemoveButton && handleRemoveClick(); } else { - const steps = res?.response ?? [] - const branches = res?.branches ?? [] - const pipelineCrews = res?.pipelineCrews ?? [] + const steps = res?.response ?? []; + const branches = res?.branches ?? []; + const pipelineCrews = res?.pipelineCrews ?? []; dispatch( updatePipelineToolVerifyResponseSteps({ organization, @@ -82,12 +82,12 @@ export const PipelineMetricSelection = ({ branches, pipelineCrews, }) - ) - res?.haveStep && dispatch(updatePipelineStep({ steps, id, type, branches, pipelineCrews })) + ); + res?.haveStep && dispatch(updatePipelineStep({ steps, id, type, branches, pipelineCrews })); } - res && setIsShowNoStepWarning(!res.haveStep) - }) - } + res && setIsShowNoStepWarning(!res.haveStep); + }); + }; return ( @@ -134,5 +134,5 @@ export const PipelineMetricSelection = ({ )} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style.tsx index 4c2389cc84..beb9694ae0 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/style.tsx @@ -1,6 +1,6 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { BasicButton } from '@src/components/Common/Buttons' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; +import { BasicButton } from '@src/components/Common/Buttons'; export const PipelineMetricSelectionWrapper = styled('div')` position: relative; @@ -11,27 +11,27 @@ export const PipelineMetricSelectionWrapper = styled('div')` margin-bottom: 1rem; line-height: '2rem'; boarder-radius: 0.25rem; -` +`; export const ButtonWrapper = styled('div')({ width: '100%', display: 'flex', justifyContent: 'flex-end', -}) +}); export const RemoveButton = styled(BasicButton)({ fontFamily: theme.main.font.secondary, marginRight: '2rem', -}) +}); export const WarningMessage = styled('p')({ fontFamily: theme.typography.fontFamily, color: '#d32f2f', margin: '0 0 0 2.5%', width: '95%', -}) +}); export const BranchSelectionWrapper = styled('div')({ margin: '0 0 1.5rem 2.5%', width: '95%', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/index.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/index.tsx index 4c98247e78..2e3178c25a 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/index.tsx @@ -1,45 +1,45 @@ -import { Autocomplete, Box, ListItemText, TextField } from '@mui/material' -import React, { useEffect, useState } from 'react' -import { FormControlWrapper } from './style' -import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji' -import { EmojiWrap, StyledAvatar } from '@src/emojis/style' -import { Z_INDEX } from '@src/constants/commons' +import { Autocomplete, Box, ListItemText, TextField } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { FormControlWrapper } from './style'; +import { getEmojiUrls, removeExtraEmojiName } from '@src/emojis/emoji'; +import { EmojiWrap, StyledAvatar } from '@src/emojis/style'; +import { Z_INDEX } from '@src/constants/commons'; interface Props { - options: string[] - label: string - value: string - id: number - onGetSteps?: (pipelineName: string) => void - step?: string - onUpDatePipeline: (id: number, label: string, value: string) => void + options: string[]; + label: string; + value: string; + id: number; + onGetSteps?: (pipelineName: string) => void; + step?: string; + onUpDatePipeline: (id: number, label: string, value: string) => void; } /* istanbul ignore next */ export const SingleSelection = ({ options, label, value, id, onGetSteps, step, onUpDatePipeline }: Props) => { - const labelId = `single-selection-${label.toLowerCase().replace(' ', '-')}` - const [selectedOptions, setSelectedOptions] = useState(value) - const [inputValue, setInputValue] = useState(value) + const labelId = `single-selection-${label.toLowerCase().replace(' ', '-')}`; + const [selectedOptions, setSelectedOptions] = useState(value); + const [inputValue, setInputValue] = useState(value); const handleSelectedOptionsChange = (value: string) => { - setSelectedOptions(value) + setSelectedOptions(value); if (onGetSteps) { - onUpDatePipeline(id, 'Step', '') - onGetSteps(value) + onUpDatePipeline(id, 'Step', ''); + onGetSteps(value); } - onUpDatePipeline(id, label, value) - } + onUpDatePipeline(id, label, value); + }; useEffect(() => { if (onGetSteps && !!selectedOptions && !step) { - onGetSteps(selectedOptions) + onGetSteps(selectedOptions); } - }, []) + }, []); const emojiView = (pipelineStepName: string) => { - const emojiUrls: string[] = getEmojiUrls(pipelineStepName) - return emojiUrls.map((url) => ) - } + const emojiUrls: string[] = getEmojiUrls(pipelineStepName); + return emojiUrls.map((url) => ); + }; return ( <> @@ -59,11 +59,11 @@ export const SingleSelection = ({ options, label, value, id, onGetSteps, step, o )} value={value} onChange={(event, newValue: string) => { - handleSelectedOptionsChange(newValue) + handleSelectedOptionsChange(newValue); }} inputValue={inputValue} onInputChange={(event, newInputValue) => { - setInputValue(newInputValue) + setInputValue(newInputValue); }} renderInput={(params) => } slotProps={{ @@ -76,5 +76,5 @@ export const SingleSelection = ({ options, label, value, id, onGetSteps, step, o /> - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/style.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/style.tsx index 861175932a..8a87c82d85 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/SingleSelection/style.tsx @@ -1,8 +1,8 @@ -import { styled } from '@mui/material/styles' -import { FormControl } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { FormControl } from '@mui/material'; export const FormControlWrapper = styled(FormControl)({ margin: '0 0 2rem 2.5%', width: '95%', height: '2.5rem', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/index.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/index.tsx index b39bec8a2a..8f3442cc6d 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/index.tsx @@ -1,37 +1,37 @@ -import React from 'react' -import { PipelineMetricSelection } from './PipelineMetricSelection' -import { useAppDispatch, useAppSelector } from '@src/hooks' -import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle' +import React from 'react'; +import { PipelineMetricSelection } from './PipelineMetricSelection'; +import { useAppDispatch, useAppSelector } from '@src/hooks'; +import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle'; import { addADeploymentFrequencySetting, deleteADeploymentFrequencySetting, selectDeploymentFrequencySettings, updateDeploymentFrequencySettings, -} from '@src/context/Metrics/metricsSlice' -import { useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext' -import { MetricsSettingAddButton } from '@src/components/Common/MetricsSettingButton' -import { PIPELINE_SETTING_TYPES } from '@src/constants/resources' -import { selectPipelineCrews } from '@src/context/config/configSlice' -import { Crews } from '@src/components/Metrics/MetricsStep/Crews' -import _ from 'lodash' +} from '@src/context/Metrics/metricsSlice'; +import { useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext'; +import { MetricsSettingAddButton } from '@src/components/Common/MetricsSettingButton'; +import { PIPELINE_SETTING_TYPES } from '@src/constants/resources'; +import { selectPipelineCrews } from '@src/context/config/configSlice'; +import { Crews } from '@src/components/Metrics/MetricsStep/Crews'; +import _ from 'lodash'; export const DeploymentFrequencySettings = () => { - const dispatch = useAppDispatch() - const deploymentFrequencySettings = useAppSelector(selectDeploymentFrequencySettings) - const { getDuplicatedPipeLineIds } = useMetricsStepValidationCheckContext() - const pipelineCrews = useAppSelector(selectPipelineCrews) + const dispatch = useAppDispatch(); + const deploymentFrequencySettings = useAppSelector(selectDeploymentFrequencySettings); + const { getDuplicatedPipeLineIds } = useMetricsStepValidationCheckContext(); + const pipelineCrews = useAppSelector(selectPipelineCrews); const handleAddPipeline = () => { - dispatch(addADeploymentFrequencySetting()) - } + dispatch(addADeploymentFrequencySetting()); + }; const handleRemovePipeline = (id: number) => { - dispatch(deleteADeploymentFrequencySetting(id)) - } + dispatch(deleteADeploymentFrequencySetting(id)); + }; const handleUpdatePipeline = (id: number, label: string, value: string) => { - dispatch(updateDeploymentFrequencySettings({ updateId: id, label, value })) - } + dispatch(updateDeploymentFrequencySettings({ updateId: id, label, value })); + }; return ( <> @@ -52,5 +52,5 @@ export const DeploymentFrequencySettings = () => { )} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/RealDone/index.tsx b/frontend/src/components/Metrics/MetricsStep/RealDone/index.tsx index 9c47b52e04..053c31800b 100644 --- a/frontend/src/components/Metrics/MetricsStep/RealDone/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/RealDone/index.tsx @@ -1,71 +1,71 @@ -import { FormHelperText } from '@mui/material' -import React, { useEffect, useState } from 'react' +import { FormHelperText } from '@mui/material'; +import React, { useEffect, useState } from 'react'; import { saveDoneColumn, selectCycleTimeSettings, selectMetricsContent, selectRealDoneWarningMessage, -} from '@src/context/Metrics/metricsSlice' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle' -import { METRICS_CONSTANTS } from '@src/constants/resources' -import { DEFAULT_HELPER_TEXT } from '@src/constants/commons' -import { useAppSelector } from '@src/hooks' -import { WarningNotification } from '@src/components/Common/WarningNotification' -import MultiAutoComplete from '@src/components/Common/MultiAutoComplete' -import { WarningMessage } from '@src/components/Metrics/MetricsStep/Crews/style' +} from '@src/context/Metrics/metricsSlice'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { MetricsSettingTitle } from '@src/components/Common/MetricsSettingTitle'; +import { METRICS_CONSTANTS } from '@src/constants/resources'; +import { DEFAULT_HELPER_TEXT } from '@src/constants/commons'; +import { useAppSelector } from '@src/hooks'; +import { WarningNotification } from '@src/components/Common/WarningNotification'; +import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; +import { WarningMessage } from '@src/components/Metrics/MetricsStep/Crews/style'; interface realDoneProps { - columns: { key: string; value: { name: string; statuses: string[] } }[] - title: string - label: string + columns: { key: string; value: { name: string; statuses: string[] } }[]; + title: string; + label: string; } const getSelectedDoneColumns = (selectedBoardColumns: { name: string; value: string }[]) => - selectedBoardColumns.filter(({ value }) => value === METRICS_CONSTANTS.doneValue).map(({ name }) => name) + selectedBoardColumns.filter(({ value }) => value === METRICS_CONSTANTS.doneValue).map(({ name }) => name); const getFilteredStatus = ( columns: { key: string; value: { name: string; statuses: string[] } }[], selectedDoneColumns: string[] ): string[] => - columns.filter(({ value }) => selectedDoneColumns.includes(value.name)).flatMap(({ value }) => value.statuses) + columns.filter(({ value }) => selectedDoneColumns.includes(value.name)).flatMap(({ value }) => value.statuses); const getDoneStatus = (columns: { key: string; value: { name: string; statuses: string[] } }[]) => - columns.find((column) => column.key === METRICS_CONSTANTS.doneKeyFromBackend)?.value.statuses ?? [] + columns.find((column) => column.key === METRICS_CONSTANTS.doneKeyFromBackend)?.value.statuses ?? []; export const RealDone = ({ columns, title, label }: realDoneProps) => { - const dispatch = useAppDispatch() - const selectedCycleTimeSettings = useAppSelector(selectCycleTimeSettings) - const savedDoneColumns = useAppSelector(selectMetricsContent).doneColumn - const realDoneWarningMessage = useAppSelector(selectRealDoneWarningMessage) - const doneStatus = getDoneStatus(columns) - const selectedDoneColumns = getSelectedDoneColumns(selectedCycleTimeSettings) - const filteredStatus = getFilteredStatus(columns, selectedDoneColumns) - const status = selectedDoneColumns.length < 1 ? doneStatus : filteredStatus - const [selectedDoneStatus, setSelectedDoneStatus] = useState([]) - const isAllSelected = savedDoneColumns.length === status.length - const [useDefaultValue, setUseDefaultValue] = useState(false) + const dispatch = useAppDispatch(); + const selectedCycleTimeSettings = useAppSelector(selectCycleTimeSettings); + const savedDoneColumns = useAppSelector(selectMetricsContent).doneColumn; + const realDoneWarningMessage = useAppSelector(selectRealDoneWarningMessage); + const doneStatus = getDoneStatus(columns); + const selectedDoneColumns = getSelectedDoneColumns(selectedCycleTimeSettings); + const filteredStatus = getFilteredStatus(columns, selectedDoneColumns); + const status = selectedDoneColumns.length < 1 ? doneStatus : filteredStatus; + const [selectedDoneStatus, setSelectedDoneStatus] = useState([]); + const isAllSelected = savedDoneColumns.length === status.length; + const [useDefaultValue, setUseDefaultValue] = useState(false); const handleRealDoneChange = (event: React.SyntheticEvent, value: string[]) => { if (value[value.length - 1] === 'All') { - setSelectedDoneStatus(selectedDoneStatus.length === status.length ? [] : status) - dispatch(saveDoneColumn(selectedDoneStatus.length === status.length ? [] : status)) - return + setSelectedDoneStatus(selectedDoneStatus.length === status.length ? [] : status); + dispatch(saveDoneColumn(selectedDoneStatus.length === status.length ? [] : status)); + return; } - setSelectedDoneStatus([...value]) - dispatch(saveDoneColumn([...value])) - } + setSelectedDoneStatus([...value]); + dispatch(saveDoneColumn([...value])); + }; useEffect(() => { if (status.length === 1) { if (savedDoneColumns.length !== 1) { - dispatch(saveDoneColumn(status)) + dispatch(saveDoneColumn(status)); } - setUseDefaultValue(true) + setUseDefaultValue(true); } else { - setUseDefaultValue(false) + setUseDefaultValue(false); } - }, [status.length, useDefaultValue]) + }, [status.length, useDefaultValue]); return useDefaultValue ? ( <> @@ -91,5 +91,5 @@ export const RealDone = ({ columns, title, label }: realDoneProps) => { )} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStep/RealDone/style.tsx b/frontend/src/components/Metrics/MetricsStep/RealDone/style.tsx index 40518a3600..0b0285b6f0 100644 --- a/frontend/src/components/Metrics/MetricsStep/RealDone/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/RealDone/style.tsx @@ -1,6 +1,6 @@ -import { styled } from '@mui/material/styles' -import { FormControl } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { FormControl } from '@mui/material'; export const FormControlWrapper = styled(FormControl)({ height: '2.5rem', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/index.tsx b/frontend/src/components/Metrics/MetricsStep/index.tsx index 3f1c46708a..e088062be9 100644 --- a/frontend/src/components/Metrics/MetricsStep/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/index.tsx @@ -1,37 +1,37 @@ -import { Crews } from '@src/components/Metrics/MetricsStep/Crews' -import { useAppSelector } from '@src/hooks' -import { RealDone } from '@src/components/Metrics/MetricsStep/RealDone' -import { CycleTime } from '@src/components/Metrics/MetricsStep/CycleTime' -import { Classification } from '@src/components/Metrics/MetricsStep/Classification' -import { selectDateRange, selectJiraColumns, selectMetrics, selectUsers } from '@src/context/config/configSlice' -import { REQUIRED_DATA, DONE } from '@src/constants/resources' -import { selectCycleTimeSettings, selectMetricsContent } from '@src/context/Metrics/metricsSlice' -import { DeploymentFrequencySettings } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings' -import DateRangeViewer from '@src/components/Common/DateRangeViewer' +import { Crews } from '@src/components/Metrics/MetricsStep/Crews'; +import { useAppSelector } from '@src/hooks'; +import { RealDone } from '@src/components/Metrics/MetricsStep/RealDone'; +import { CycleTime } from '@src/components/Metrics/MetricsStep/CycleTime'; +import { Classification } from '@src/components/Metrics/MetricsStep/Classification'; +import { selectDateRange, selectJiraColumns, selectMetrics, selectUsers } from '@src/context/config/configSlice'; +import { REQUIRED_DATA, DONE } from '@src/constants/resources'; +import { selectCycleTimeSettings, selectMetricsContent } from '@src/context/Metrics/metricsSlice'; +import { DeploymentFrequencySettings } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings'; +import DateRangeViewer from '@src/components/Common/DateRangeViewer'; import { MetricSelectionWrapper, MetricsSelectionTitle, MetricSelectionHeader, -} from '@src/components/Metrics/MetricsStep/style' -import { useLayoutEffect } from 'react' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' +} from '@src/components/Metrics/MetricsStep/style'; +import { useLayoutEffect } from 'react'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; const MetricsStep = ({ resetProps }: useNotificationLayoutEffectInterface) => { - const requiredData = useAppSelector(selectMetrics) - const users = useAppSelector(selectUsers) - const jiraColumns = useAppSelector(selectJiraColumns) - const targetFields = useAppSelector(selectMetricsContent).targetFields - const cycleTimeSettings = useAppSelector(selectCycleTimeSettings) - const { startDate, endDate } = useAppSelector(selectDateRange) + const requiredData = useAppSelector(selectMetrics); + const users = useAppSelector(selectUsers); + const jiraColumns = useAppSelector(selectJiraColumns); + const targetFields = useAppSelector(selectMetricsContent).targetFields; + const cycleTimeSettings = useAppSelector(selectCycleTimeSettings); + const { startDate, endDate } = useAppSelector(selectDateRange); const isShowCrewsAndRealDone = requiredData.includes(REQUIRED_DATA.VELOCITY) || requiredData.includes(REQUIRED_DATA.CYCLE_TIME) || - requiredData.includes(REQUIRED_DATA.CLASSIFICATION) - const isShowRealDone = cycleTimeSettings.some((e) => e.value === DONE) + requiredData.includes(REQUIRED_DATA.CLASSIFICATION); + const isShowRealDone = cycleTimeSettings.some((e) => e.value === DONE); useLayoutEffect(() => { - resetProps?.() - }, []) + resetProps?.(); + }, []); return ( <> @@ -66,7 +66,7 @@ const MetricsStep = ({ resetProps }: useNotificationLayoutEffectInterface) => { )} - ) -} + ); +}; -export default MetricsStep +export default MetricsStep; diff --git a/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/index.tsx b/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/index.tsx index 7af02b3a14..a2362f89f0 100644 --- a/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/index.tsx +++ b/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/index.tsx @@ -1,14 +1,14 @@ -import React from 'react' -import { Dialog } from '@mui/material' +import React from 'react'; +import { Dialog } from '@mui/material'; import { CancelButton, ConformationDialog, DialogButtonGroup, YesButton, -} from '@src/components/Metrics/MetricsStepper/ConfirmDialog/style' +} from '@src/components/Metrics/MetricsStepper/ConfirmDialog/style'; export const ConfirmDialog = (props: { onConfirm: () => void; onClose: () => void; isDialogShowing: boolean }) => { - const { onConfirm, onClose, isDialogShowing } = props + const { onConfirm, onClose, isDialogShowing } = props; return ( All the filled data will be cleared. Continue to Home page? @@ -17,5 +17,5 @@ export const ConfirmDialog = (props: { onConfirm: () => void; onClose: () => voi Cancel - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/style.tsx b/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/style.tsx index 115500fb2f..6dbf948467 100644 --- a/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/style.tsx +++ b/frontend/src/components/Metrics/MetricsStepper/ConfirmDialog/style.tsx @@ -1,17 +1,17 @@ -import { theme } from '@src/theme' -import { styled } from '@mui/material/styles' -import Button from '@mui/material/Button' -import { DialogContent } from '@mui/material' +import { theme } from '@src/theme'; +import { styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import { DialogContent } from '@mui/material'; export const ConformationDialog = styled(DialogContent)({ margin: '1rem 0 0 0', -}) +}); export const DialogButtonGroup = styled('div')({ display: 'flex', justifyContent: 'center', margin: '1rem 0', -}) +}); export const YesButton = styled(Button)({ boxShadow: theme.main.boxShadow, @@ -22,8 +22,8 @@ export const YesButton = styled(Button)({ '&:hover': { backgroundColor: theme.main.backgroundColor, }, -}) +}); export const CancelButton = styled(Button)({ color: theme.main.secondColor, -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStepper/index.tsx b/frontend/src/components/Metrics/MetricsStepper/index.tsx index ef0d9ccc17..b6c261a543 100644 --- a/frontend/src/components/Metrics/MetricsStepper/index.tsx +++ b/frontend/src/components/Metrics/MetricsStepper/index.tsx @@ -1,4 +1,4 @@ -import React, { lazy, Suspense, useEffect, useState } from 'react' +import React, { lazy, Suspense, useEffect, useState } from 'react'; import { BackButton, ButtonContainer, @@ -8,9 +8,9 @@ import { StyledStep, StyledStepLabel, StyledStepper, -} from './style' -import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch' -import { backStep, nextStep, selectStepNumber, updateTimeStamp } from '@src/context/stepper/StepperSlice' +} from './style'; +import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; +import { backStep, nextStep, selectStepNumber, updateTimeStamp } from '@src/context/stepper/StepperSlice'; import { BOARD_TYPES, DONE, @@ -20,10 +20,10 @@ import { REQUIRED_DATA, SOURCE_CONTROL_TYPES, TIPS, -} from '@src/constants/resources' -import { COMMON_BUTTONS, METRICS_STEPS, STEPS } from '@src/constants/commons' -import { ConfirmDialog } from '@src/components/Metrics/MetricsStepper/ConfirmDialog' -import { useNavigate } from 'react-router-dom' +} from '@src/constants/resources'; +import { COMMON_BUTTONS, METRICS_STEPS, STEPS } from '@src/constants/commons'; +import { ConfirmDialog } from '@src/components/Metrics/MetricsStepper/ConfirmDialog'; +import { useNavigate } from 'react-router-dom'; import { selectConfig, selectMetrics, @@ -33,70 +33,70 @@ import { updatePipelineToolVerifyState, updateSourceControl, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import { useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext' -import { Tooltip } from '@mui/material' -import { exportToJsonFile } from '@src/utils/util' +} from '@src/context/config/configSlice'; +import { useMetricsStepValidationCheckContext } from '@src/hooks/useMetricsStepValidationCheckContext'; +import { Tooltip } from '@mui/material'; +import { exportToJsonFile } from '@src/utils/util'; import { savedMetricsSettingState, selectCycleTimeSettings, selectMetricsContent, -} from '@src/context/Metrics/metricsSlice' -import _ from 'lodash' -import SaveAltIcon from '@mui/icons-material/SaveAlt' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -import { ROUTE } from '@src/constants/router' +} from '@src/context/Metrics/metricsSlice'; +import _ from 'lodash'; +import SaveAltIcon from '@mui/icons-material/SaveAlt'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; +import { ROUTE } from '@src/constants/router'; -const ConfigStep = lazy(() => import('@src/components/Metrics/ConfigStep')) -const MetricsStep = lazy(() => import('@src/components/Metrics/MetricsStep')) -const ReportStep = lazy(() => import('@src/components/Metrics/ReportStep')) +const ConfigStep = lazy(() => import('@src/components/Metrics/ConfigStep')); +const MetricsStep = lazy(() => import('@src/components/Metrics/MetricsStep')); +const ReportStep = lazy(() => import('@src/components/Metrics/ReportStep')); /* istanbul ignore next */ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { - const navigate = useNavigate() - const dispatch = useAppDispatch() - const activeStep = useAppSelector(selectStepNumber) - const [isDialogShowing, setIsDialogShowing] = useState(false) - const requiredData = useAppSelector(selectMetrics) - const config = useAppSelector(selectConfig) - const metricsConfig = useAppSelector(selectMetricsContent) - const [isDisableNextButton, setIsDisableNextButton] = useState(true) - const { getDuplicatedPipeLineIds } = useMetricsStepValidationCheckContext() - const cycleTimeSettings = useAppSelector(selectCycleTimeSettings) + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const activeStep = useAppSelector(selectStepNumber); + const [isDialogShowing, setIsDialogShowing] = useState(false); + const requiredData = useAppSelector(selectMetrics); + const config = useAppSelector(selectConfig); + const metricsConfig = useAppSelector(selectMetricsContent); + const [isDisableNextButton, setIsDisableNextButton] = useState(true); + const { getDuplicatedPipeLineIds } = useMetricsStepValidationCheckContext(); + const cycleTimeSettings = useAppSelector(selectCycleTimeSettings); - const { isShow: isShowBoard, isVerified: isBoardVerified } = config.board - const { isShow: isShowPipeline, isVerified: isPipelineToolVerified } = config.pipelineTool - const { isShow: isShowSourceControl, isVerified: isSourceControlVerified } = config.sourceControl - const isShowCycleTimeSettings = requiredData.includes(REQUIRED_DATA.CYCLE_TIME) - const isCycleTimeSettingsVerified = cycleTimeSettings.some((e) => e.value === DONE) - const isShowClassificationSetting = requiredData.includes(REQUIRED_DATA.CLASSIFICATION) - const isClassificationSettingVerified = metricsConfig.targetFields.some((item) => item.flag) + const { isShow: isShowBoard, isVerified: isBoardVerified } = config.board; + const { isShow: isShowPipeline, isVerified: isPipelineToolVerified } = config.pipelineTool; + const { isShow: isShowSourceControl, isVerified: isSourceControlVerified } = config.sourceControl; + const isShowCycleTimeSettings = requiredData.includes(REQUIRED_DATA.CYCLE_TIME); + const isCycleTimeSettingsVerified = cycleTimeSettings.some((e) => e.value === DONE); + const isShowClassificationSetting = requiredData.includes(REQUIRED_DATA.CLASSIFICATION); + const isClassificationSettingVerified = metricsConfig.targetFields.some((item) => item.flag); - const { metrics, projectName, dateRange } = config.basic + const { metrics, projectName, dateRange } = config.basic; - const selectedBoardColumns = useAppSelector(selectCycleTimeSettings) + const selectedBoardColumns = useAppSelector(selectCycleTimeSettings); const verifyPipeline = (type: string) => { const pipelines = type === PIPELINE_SETTING_TYPES.LEAD_TIME_FOR_CHANGES_TYPE ? metricsConfig.leadTimeForChanges - : metricsConfig.deploymentFrequencySettings + : metricsConfig.deploymentFrequencySettings; return ( pipelines.every(({ step }) => step !== '') && pipelines.every(({ branches }) => !_.isEmpty(branches)) && getDuplicatedPipeLineIds(pipelines).length === 0 - ) - } + ); + }; - const isShowCrewsSetting = isShowBoard + const isShowCrewsSetting = isShowBoard; const isShowRealDone = - isShowBoard && selectedBoardColumns.filter((column) => column.value === METRICS_CONSTANTS.doneValue).length > 0 + isShowBoard && selectedBoardColumns.filter((column) => column.value === METRICS_CONSTANTS.doneValue).length > 0; const isShowDeploymentFrequency = requiredData.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) || requiredData.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) || - requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) - const isCrewsSettingValid = metricsConfig.users.length > 0 - const isRealDoneValid = metricsConfig.doneColumn.length > 0 - const isDeploymentFrequencyValid = verifyPipeline(PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + requiredData.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY); + const isCrewsSettingValid = metricsConfig.users.length > 0; + const isRealDoneValid = metricsConfig.doneColumn.length > 0; + const isDeploymentFrequencyValid = verifyPipeline(PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE); useEffect(() => { if (!activeStep) { @@ -104,11 +104,11 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { { isShow: isShowBoard, isValid: isBoardVerified }, { isShow: isShowPipeline, isValid: isPipelineToolVerified }, { isShow: isShowSourceControl, isValid: isSourceControlVerified }, - ] - const activeNextButtonValidityOptions = nextButtonValidityOptions.filter(({ isShow }) => isShow) + ]; + const activeNextButtonValidityOptions = nextButtonValidityOptions.filter(({ isShow }) => isShow); projectName && dateRange.startDate && dateRange.endDate && metrics.length ? setIsDisableNextButton(!activeNextButtonValidityOptions.every(({ isValid }) => isValid)) - : setIsDisableNextButton(true) + : setIsDisableNextButton(true); } else if (activeStep === METRICS_STEPS.METRICS) { const nextButtonValidityOptions = [ { isShow: isShowCrewsSetting, isValid: isCrewsSettingValid }, @@ -116,11 +116,11 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { { isShow: isShowDeploymentFrequency, isValid: isDeploymentFrequencyValid }, { isShow: isShowCycleTimeSettings, isValid: isCycleTimeSettingsVerified }, { isShow: isShowClassificationSetting, isValid: isClassificationSettingVerified }, - ] - const activeNextButtonValidityOptions = nextButtonValidityOptions.filter(({ isShow }) => isShow) + ]; + const activeNextButtonValidityOptions = nextButtonValidityOptions.filter(({ isShow }) => isShow); activeNextButtonValidityOptions.every(({ isValid }) => isValid) ? setIsDisableNextButton(false) - : setIsDisableNextButton(true) + : setIsDisableNextButton(true); } }, [ activeStep, @@ -135,7 +135,7 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { dateRange, selectedBoardColumns, metricsConfig, - ]) + ]); const filterMetricsConfig = (metricsConfig: savedMetricsSettingState) => { return Object.fromEntries( @@ -146,17 +146,17 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { !value.every((item) => item.organization === '') && !value.every((item) => item.flag === false) && value.length > 0 - ) + ); } else { - return true + return true; } }) - ) - } + ); + }; /* istanbul ignore next */ const handleSave = () => { - const { projectName, dateRange, calendarType, metrics } = config.basic + const { projectName, dateRange, calendarType, metrics } = config.basic; const configData = { projectName, dateRange, @@ -168,7 +168,7 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { pipelineTool: isShowPipeline ? config.pipelineTool.config : undefined, /* istanbul ignore next */ sourceControl: isShowSourceControl ? config.sourceControl.config : undefined, - } + }; const { leadTimeForChanges, @@ -180,7 +180,7 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { cycleTimeSettings, treatFlagCardAsBlock, assigneeFilter, - } = filterMetricsConfig(metricsConfig) + } = filterMetricsConfig(metricsConfig); /* istanbul ignore next */ const metricsData = { @@ -203,51 +203,51 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { ?.map((item: { name: string; key: string; flag: boolean }) => item.key), deployment: deploymentFrequencySettings, leadTime: leadTimeForChanges, - } - const jsonData = activeStep === METRICS_STEPS.CONFIG ? configData : { ...configData, ...metricsData } - exportToJsonFile('config', jsonData) - } + }; + const jsonData = activeStep === METRICS_STEPS.CONFIG ? configData : { ...configData, ...metricsData }; + exportToJsonFile('config', jsonData); + }; const handleNext = () => { if (activeStep === METRICS_STEPS.METRICS) { - dispatch(updateTimeStamp(new Date().getTime())) + dispatch(updateTimeStamp(new Date().getTime())); } if (activeStep === METRICS_STEPS.CONFIG) { - cleanBoardState() - cleanPipelineToolConfiguration() - cleanSourceControlState() + cleanBoardState(); + cleanPipelineToolConfiguration(); + cleanSourceControlState(); } - dispatch(nextStep()) - } + dispatch(nextStep()); + }; const handleBack = () => { - setIsDialogShowing(!activeStep) - dispatch(backStep()) - } + setIsDialogShowing(!activeStep); + dispatch(backStep()); + }; const backToHomePage = () => { - navigate(ROUTE.BASE_PAGE) - setIsDialogShowing(false) - window.location.reload() - } + navigate(ROUTE.BASE_PAGE); + setIsDialogShowing(false); + window.location.reload(); + }; const CancelDialog = () => { - setIsDialogShowing(false) - } + setIsDialogShowing(false); + }; const cleanPipelineToolConfiguration = () => { - !isShowPipeline && dispatch(updatePipelineTool({ type: PIPELINE_TOOL_TYPES.BUILD_KITE, token: '' })) + !isShowPipeline && dispatch(updatePipelineTool({ type: PIPELINE_TOOL_TYPES.BUILD_KITE, token: '' })); isShowPipeline ? dispatch(updatePipelineToolVerifyState(isPipelineToolVerified)) - : dispatch(updatePipelineToolVerifyState(false)) - } + : dispatch(updatePipelineToolVerifyState(false)); + }; const cleanSourceControlState = () => { - !isShowSourceControl && dispatch(updateSourceControl({ type: SOURCE_CONTROL_TYPES.GITHUB, token: '' })) + !isShowSourceControl && dispatch(updateSourceControl({ type: SOURCE_CONTROL_TYPES.GITHUB, token: '' })); isShowSourceControl ? dispatch(updateSourceControlVerifyState(isSourceControlVerified)) - : dispatch(updateSourceControlVerifyState(false)) - } + : dispatch(updateSourceControlVerifyState(false)); + }; const cleanBoardState = () => { !isShowBoard && @@ -260,9 +260,9 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { site: '', token: '', }) - ) - isShowBoard ? dispatch(updateBoardVerifyState(isBoardVerified)) : dispatch(updateBoardVerifyState(false)) - } + ); + isShowBoard ? dispatch(updateBoardVerifyState(isBoardVerified)) : dispatch(updateBoardVerifyState(false)); + }; return ( <> @@ -303,7 +303,7 @@ const MetricsStepper = (props: useNotificationLayoutEffectInterface) => { )} - ) -} + ); +}; -export default MetricsStepper +export default MetricsStepper; diff --git a/frontend/src/components/Metrics/MetricsStepper/style.tsx b/frontend/src/components/Metrics/MetricsStepper/style.tsx index a353e8bcb3..9d037bc585 100644 --- a/frontend/src/components/Metrics/MetricsStepper/style.tsx +++ b/frontend/src/components/Metrics/MetricsStepper/style.tsx @@ -1,6 +1,6 @@ -import { styled } from '@mui/material/styles' -import { Button, Step, StepLabel, Stepper } from '@mui/material' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import { Button, Step, StepLabel, Stepper } from '@mui/material'; +import { theme } from '@src/theme'; export const StyledStepper = styled(Stepper)({ display: 'flex', @@ -12,7 +12,7 @@ export const StyledStepper = styled(Stepper)({ flexDirection: 'column', fontSize: '0.5rem', }, -}) +}); export const StyledStep = styled(Step)({ svg: { @@ -22,7 +22,7 @@ export const StyledStep = styled(Step)({ [theme.breakpoints.down('md')]: { padding: '0.25rem 0', }, -}) +}); export const StyledStepLabel = styled(StepLabel)({ width: '5rem', @@ -34,7 +34,7 @@ export const StyledStepLabel = styled(StepLabel)({ [theme.breakpoints.down('sm')]: { fontSize: '0.5rem', }, -}) +}); export const MetricsStepperContent = styled('div')({ display: 'flex', @@ -45,7 +45,7 @@ export const MetricsStepperContent = styled('div')({ [theme.breakpoints.down('md')]: { width: '80%', }, -}) +}); export const basicButtonStyle = { height: '2.5rem', @@ -53,7 +53,7 @@ export const basicButtonStyle = { fontSize: '1rem', fontWeight: '500', textTransform: theme.typography.button.textTransform, -} +}; export const SaveButton = styled(Button)({ ...basicButtonStyle, @@ -62,7 +62,7 @@ export const SaveButton = styled(Button)({ [theme.breakpoints.down('lg')]: { fontSize: '0.8rem', }, -}) +}); export const BackButton = styled(Button)({ ...basicButtonStyle, @@ -71,7 +71,7 @@ export const BackButton = styled(Button)({ [theme.breakpoints.down('lg')]: { fontSize: '0.8rem', }, -}) +}); export const NextButton = styled(Button)({ ...basicButtonStyle, @@ -92,7 +92,7 @@ export const NextButton = styled(Button)({ [theme.breakpoints.down('lg')]: { fontSize: '0.8rem', }, -}) +}); export const ButtonContainer = styled('div')({ display: 'flex', @@ -101,4 +101,4 @@ export const ButtonContainer = styled('div')({ margin: '0 auto', padding: '0 0 2rem 0', width: '70%', -}) +}); diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 2317274fbc..8fe156da45 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -44,19 +44,19 @@ export const ReportButtonGroup = ({ const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() useEffect(() => { - setErrorMessage(errorMessage) - }, [errorMessage]) + setErrorMessage(errorMessage); + }, [errorMessage]); const exportCSV = (dataType: DOWNLOAD_TYPES, startDate: string, endDate: string): CSVReportRequestDTO => ({ dataType: dataType, csvTimeStamp: csvTimeStamp, startDate: startDate, endDate: endDate, - }) + }); const handleDownload = (dataType: DOWNLOAD_TYPES, startDate: string, endDate: string) => { - fetchExportData(exportCSV(dataType, startDate, endDate)) - } + fetchExportData(exportCSV(dataType, startDate, endDate)); + }; return ( <> @@ -104,5 +104,5 @@ export const ReportButtonGroup = ({ {} - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index a383e9fceb..9fe0ae0642 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react' -import { useAppSelector } from '@src/hooks' -import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice' +import React, { useEffect } from 'react'; +import { useAppSelector } from '@src/hooks'; +import { selectConfig, selectJiraColumns } from '@src/context/config/configSlice'; import { BOARD_METRICS, CALENDAR, @@ -43,7 +43,7 @@ const BoardMetrics = ({ startDate, endDate, }: BoardMetricsProps) => { - const configData = useAppSelector(selectConfig) + const configData = useAppSelector(selectConfig); const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = useAppSelector(selectMetricsContent) const jiraColumns = useAppSelector(selectJiraColumns) @@ -54,8 +54,8 @@ const BoardMetrics = ({ const jiraToken = getJiraBoardToken(token, email) const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value - ) - const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)) + ); + const boardMetrics = metrics.filter((metric) => BOARD_METRICS.includes(metric)); const getBoardReportRequestBody = (): BoardReportRequestDTO => ({ metrics: boardMetrics, @@ -79,8 +79,8 @@ const BoardMetrics = ({ }) const getBoardItems = () => { - const velocity = boardReport?.velocity - const cycleTime = boardReport?.cycleTime + const velocity = boardReport?.velocity; + const cycleTime = boardReport?.cycleTime; const velocityItems = boardMetrics.includes(REQUIRED_DATA.VELOCITY) ? [ { @@ -99,7 +99,7 @@ const BoardMetrics = ({ ], }, ] - : [] + : []; const cycleTimeItems = boardMetrics.includes(REQUIRED_DATA.CYCLE_TIME) ? [ @@ -117,10 +117,10 @@ const BoardMetrics = ({ ], }, ] - : [] + : []; - return [...velocityItems, ...cycleTimeItems] - } + return [...velocityItems, ...cycleTimeItems]; + }; useEffect(() => { !isBackFromDetail && startToRequestBoardData(getBoardReportRequestBody()) @@ -136,7 +136,7 @@ const BoardMetrics = ({
    - ) -} + ); +}; -export default BoardMetrics +export default BoardMetrics; diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index 3be35e185d..fe0bdd11ea 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -1,8 +1,8 @@ -import { styled } from '@mui/material/styles' +import { styled } from '@mui/material/styles'; export const StyledMetricsSection = styled('div')({ marginTop: '2rem', -}) +}); export const StyledTitleWrapper = styled('div')({ display: 'flex', diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index 3be35e185d..fe0bdd11ea 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -1,8 +1,8 @@ -import { styled } from '@mui/material/styles' +import { styled } from '@mui/material/styles'; export const StyledMetricsSection = styled('div')({ marginTop: '2rem', -}) +}); export const StyledTitleWrapper = styled('div')({ display: 'flex', diff --git a/frontend/src/components/Metrics/ReportStep/ExpiredDialog/index.tsx b/frontend/src/components/Metrics/ReportStep/ExpiredDialog/index.tsx index e479e54036..9d89bd7c66 100644 --- a/frontend/src/components/Metrics/ReportStep/ExpiredDialog/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/ExpiredDialog/index.tsx @@ -1,27 +1,27 @@ -import * as React from 'react' -import { Fragment, useEffect } from 'react' -import Button from '@mui/material/Button' -import Dialog from '@mui/material/Dialog' -import DialogContent from '@mui/material/DialogContent' -import DialogContentText from '@mui/material/DialogContentText' -import ReportGmailerrorredIcon from '@mui/icons-material/ReportGmailerrorred' -import { StyleDialogActions, StyleDialogTitle } from '@src/components/Metrics/ReportStep/ExpiredDialog/style' +import * as React from 'react'; +import { Fragment, useEffect } from 'react'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import ReportGmailerrorredIcon from '@mui/icons-material/ReportGmailerrorred'; +import { StyleDialogActions, StyleDialogTitle } from '@src/components/Metrics/ReportStep/ExpiredDialog/style'; export interface ExpiredDialogInterface { - isExpired: boolean - handleOk: () => void + isExpired: boolean; + handleOk: () => void; } export const ExpiredDialog = ({ isExpired, handleOk }: ExpiredDialogInterface) => { - const [open, setOpen] = React.useState(false) + const [open, setOpen] = React.useState(false); const handleClose = () => { - setOpen(false) - } + setOpen(false); + }; useEffect(() => { - setOpen(isExpired) - }, [isExpired]) + setOpen(isExpired); + }, [isExpired]); return ( @@ -46,5 +46,5 @@ export const ExpiredDialog = ({ isExpired, handleOk }: ExpiredDialogInterface) = - ) -} + ); +}; diff --git a/frontend/src/components/Metrics/ReportStep/ExpiredDialog/style.tsx b/frontend/src/components/Metrics/ReportStep/ExpiredDialog/style.tsx index 04612f3910..5456a44e39 100644 --- a/frontend/src/components/Metrics/ReportStep/ExpiredDialog/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/ExpiredDialog/style.tsx @@ -1,11 +1,11 @@ -import { styled } from '@mui/material/styles' -import { DialogActions, DialogTitle } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { DialogActions, DialogTitle } from '@mui/material'; export const StyleDialogTitle = styled(DialogTitle)({ display: 'flex', alignItems: 'center', -}) +}); export const StyleDialogActions = styled(DialogActions)({ padding: '1rem', -}) +}); diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/style.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/components/Metrics/ReportStep/style.tsx b/frontend/src/components/Metrics/ReportStep/style.tsx index 3d0f2f1624..f0771828dd 100644 --- a/frontend/src/components/Metrics/ReportStep/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/style.tsx @@ -1,14 +1,14 @@ -import { styled } from '@mui/material/styles' -import { Z_INDEX } from '@src/constants/commons' -import { theme } from '@src/theme' +import { styled } from '@mui/material/styles'; +import { Z_INDEX } from '@src/constants/commons'; +import { theme } from '@src/theme'; export const StyledErrorNotification = styled('div')({ zIndex: Z_INDEX.MODAL_BACKDROP, -}) +}); export const StyledSpacing = styled('div')({ height: '1.5rem', -}) +}); export const basicButtonStyle = { height: '2.5rem', @@ -17,4 +17,4 @@ export const basicButtonStyle = { fontSize: '1rem', fontWeight: '500', textTransform: theme.typography.button.textTransform, -} +}; diff --git a/frontend/src/components/ProjectDescription.tsx b/frontend/src/components/ProjectDescription.tsx index 63319a8067..c336c01928 100644 --- a/frontend/src/components/ProjectDescription.tsx +++ b/frontend/src/components/ProjectDescription.tsx @@ -1,16 +1,16 @@ -import styled from '@emotion/styled' -import { theme } from '@src/theme' +import styled from '@emotion/styled'; +import { theme } from '@src/theme'; const DescriptionContainer = styled.p({ padding: '1rem', marginTop: '0', boxShadow: `0 0.8rem 0.7rem -0.5rem ${theme.palette.grey[500]}`, -}) +}); export const ProjectDescription = () => { return ( {`Heartbeat is a tool for tracking project delivery metrics that can help you get a better understanding of delivery performance. This product allows you easily get all aspects of source data faster and more accurate to analyze team delivery performance which enables delivery teams and team leaders focusing on driving continuous improvement and enhancing team productivity and efficiency.`} - ) -} + ); +}; diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index 87cb5760d5..a2c3731949 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -1,4 +1,4 @@ -import { lazy } from 'react' +import { lazy } from 'react'; export const routes = [ { @@ -22,4 +22,4 @@ export const routes = [ component: lazy(() => import('../pages/Home')), name: 'Home', }, -] +]; diff --git a/frontend/src/constants/commons.ts b/frontend/src/constants/commons.ts index e60e748e1c..d9cc36b772 100644 --- a/frontend/src/constants/commons.ts +++ b/frontend/src/constants/commons.ts @@ -1,23 +1,23 @@ -import { ReportDataWithThreeColumns, ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' +import { ReportDataWithThreeColumns, ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; -export const PROJECT_NAME = 'Heartbeat' +export const PROJECT_NAME = 'Heartbeat'; -export const DEFAULT_HELPER_TEXT = ' ' +export const DEFAULT_HELPER_TEXT = ' '; -export const FIVE_HUNDRED = 500 +export const FIVE_HUNDRED = 500; -export const ZERO = 0 +export const ZERO = 0; -export const EMPTY_STRING = '' +export const EMPTY_STRING = ''; -export const STEPS = ['Config', 'Metrics', 'Report'] +export const STEPS = ['Config', 'Metrics', 'Report']; -export const SELECTED_VALUE_SEPARATOR = ', ' +export const SELECTED_VALUE_SEPARATOR = ', '; export const DURATION = { ERROR_MESSAGE_TIME: 4000, NOTIFICATION_TIME: 10000, -} +}; export const INIT_REPORT_DATA_WITH_TWO_COLUMNS: ReportDataWithTwoColumns[] = [ { @@ -25,7 +25,7 @@ export const INIT_REPORT_DATA_WITH_TWO_COLUMNS: ReportDataWithTwoColumns[] = [ name: '', valueList: [{ value: 0, unit: '' }], }, -] +]; export const INIT_REPORT_DATA_WITH_THREE_COLUMNS: ReportDataWithThreeColumns[] = [ { @@ -38,7 +38,7 @@ export const INIT_REPORT_DATA_WITH_THREE_COLUMNS: ReportDataWithThreeColumns[] = }, ], }, -] +]; export const Z_INDEX = { DEFAULT: 0, @@ -53,7 +53,7 @@ export const Z_INDEX = { TOOLTIP: 1050, STICKY: 1060, FIXED: 1070, -} +}; export enum DOWNLOAD_TYPES { METRICS = 'metric', @@ -70,7 +70,7 @@ export const METRICS_STEPS = { CONFIG: 0, METRICS: 1, REPORT: 2, -} +}; export const COMMON_BUTTONS = { SAVE: 'Save', @@ -79,9 +79,9 @@ export const COMMON_BUTTONS = { EXPORT_PIPELINE_DATA: 'Export pipeline data', EXPORT_BOARD_DATA: 'Export board data', EXPORT_METRIC_DATA: 'Export metric data', -} +}; export const GRID_CONFIG = { HALF: { XS: 6, MAX_INDEX: 2, FLEX: 1 }, FULL: { XS: 12, MAX_INDEX: 4, FLEX: 0.25 }, -} +}; diff --git a/frontend/src/constants/regex.ts b/frontend/src/constants/regex.ts index 39ad5bb444..71e7d7549b 100644 --- a/frontend/src/constants/regex.ts +++ b/frontend/src/constants/regex.ts @@ -3,4 +3,4 @@ export const REGEX = { BOARD_TOKEN: /^[a-zA-Z0-9\-=_]{1,500}$/, BUILDKITE_TOKEN: /^(bkua)?_?([a-zA-Z0-9]{40})$/, GITHUB_TOKEN: /^(ghp|gho|ghu|ghs|ghr)+_+([a-zA-Z0-9]{36})$/, -} +}; diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index e700fdce96..b283ed0b8f 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -1,7 +1,7 @@ export const CALENDAR = { REGULAR: 'Regular Calendar(Weekend Considered)', CHINA: 'Calendar with Chinese Holiday', -} +}; export const REPORT_PAGE_TYPE = { SUMMARY: 'Summary', @@ -51,9 +51,9 @@ export const DORA_METRICS: string[] = [ REQUIRED_DATA.DEPLOYMENT_FREQUENCY, REQUIRED_DATA.CHANGE_FAILURE_RATE, REQUIRED_DATA.MEAN_TIME_TO_RECOVERY, -] +]; -export const BOARD_METRICS: string[] = [REQUIRED_DATA.VELOCITY, REQUIRED_DATA.CYCLE_TIME, REQUIRED_DATA.CLASSIFICATION] +export const BOARD_METRICS: string[] = [REQUIRED_DATA.VELOCITY, REQUIRED_DATA.CYCLE_TIME, REQUIRED_DATA.CLASSIFICATION]; export enum CONFIG_TITLE { BOARD = 'Board', @@ -64,16 +64,16 @@ export enum CONFIG_TITLE { export const BOARD_TYPES = { CLASSIC_JIRA: 'Classic Jira', JIRA: 'Jira', -} +}; export const PIPELINE_TOOL_TYPES = { BUILD_KITE: 'BuildKite', GO_CD: 'GoCD', -} +}; export const SOURCE_CONTROL_TYPES = { GITHUB: 'GitHub', -} +}; export enum PIPELINE_SETTING_TYPES { DEPLOYMENT_FREQUENCY_SETTINGS_TYPE = 'DeploymentFrequencySettings', @@ -83,13 +83,13 @@ export enum PIPELINE_SETTING_TYPES { export const ASSIGNEE_FILTER_TYPES = { LAST_ASSIGNEE: 'lastAssignee', HISTORICAL_ASSIGNEE: 'historicalAssignee', -} +}; -export const EMAIL = 'Email' +export const EMAIL = 'Email'; -export const BOARD_TOKEN = 'Token' +export const BOARD_TOKEN = 'Token'; -export const DONE = 'Done' +export const DONE = 'Done'; export const METRICS_CONSTANTS = { cycleTimeEmptyStr: '----', @@ -102,7 +102,7 @@ export const METRICS_CONSTANTS = { waitingValue: 'Waiting for testing', testingValue: 'Testing', reviewValue: 'Review', -} +}; export const CYCLE_TIME_LIST = [ METRICS_CONSTANTS.cycleTimeEmptyStr, @@ -114,18 +114,18 @@ export const CYCLE_TIME_LIST = [ METRICS_CONSTANTS.testingValue, METRICS_CONSTANTS.reviewValue, METRICS_CONSTANTS.doneValue, -] +]; export const TOKEN_HELPER_TEXT = { RequiredTokenText: 'Token is required', InvalidTokenText: 'Token is invalid', -} +}; export const TIPS = { SAVE_CONFIG: 'Note: When you save the settings, some tokens might be saved, please save it safely (e.g. by 1 password, vault), Rotate the tokens regularly. (e.g. every 3 months)', CYCLE_TIME: 'The report page will sum all the status in the column for cycletime calculation', -} +}; export enum VELOCITY_METRICS_NAME { VELOCITY_SP = 'Velocity(Story Point)', @@ -146,17 +146,17 @@ export enum CYCLE_TIME_METRICS_NAME { AVERAGE_TESTING_TIME = 'Average testing time', } -export const DEPLOYMENT_FREQUENCY_NAME = 'Deployment frequency(deployments/day)' +export const DEPLOYMENT_FREQUENCY_NAME = 'Deployment frequency(deployments/day)'; -export const FAILURE_RATE_NAME = 'Failure rate' +export const FAILURE_RATE_NAME = 'Failure rate'; -export const MEAN_TIME_TO_RECOVERY_NAME = 'Mean Time To Recovery' +export const MEAN_TIME_TO_RECOVERY_NAME = 'Mean Time To Recovery'; -export const PIPELINE_STEP = 'Pipeline/step' +export const PIPELINE_STEP = 'Pipeline/step'; -export const NAME = 'Name' +export const NAME = 'Name'; -export const AVERAGE_FIELD = 'Average' +export const AVERAGE_FIELD = 'Average'; export enum REPORT_SUFFIX_UNITS { PER_SP = '(days/SP)', @@ -181,7 +181,7 @@ export const MESSAGE = { NOTIFICATION_FIRST_REPORT: 'The file needs to be exported within %s minutes, otherwise it will expire.', EXPIRE_IN_FIVE_MINUTES: 'The file will expire in 5 minutes, please download it in time.', REPORT_LOADING: 'The report is being generated, please do not refresh the page or all the data will be disappeared.', -} +}; export const METRICS_CYCLE_SETTING_TABLE_HEADER = [ { @@ -196,7 +196,7 @@ export const METRICS_CYCLE_SETTING_TABLE_HEADER = [ text: 'Heartbeat State', emphasis: true, }, -] +]; export const REPORT_PAGE = { BOARD: { @@ -205,4 +205,4 @@ export const REPORT_PAGE = { DORA: { TITLE: 'DORA Metrics', }, -} +}; diff --git a/frontend/src/constants/template.ts b/frontend/src/constants/template.ts index 9cb28f04c6..7e2fef9b28 100644 --- a/frontend/src/constants/template.ts +++ b/frontend/src/constants/template.ts @@ -1 +1 @@ -export const DATE_FORMAT_TEMPLATE = 'YYYY/MM/DD' +export const DATE_FORMAT_TEMPLATE = 'YYYY/MM/DD'; diff --git a/frontend/src/context/Metrics/metricsSlice.tsx b/frontend/src/context/Metrics/metricsSlice.tsx index 1510a71c54..69cafe3f2a 100644 --- a/frontend/src/context/Metrics/metricsSlice.tsx +++ b/frontend/src/context/Metrics/metricsSlice.tsx @@ -1,62 +1,62 @@ /* istanbul ignore file */ -import { createSlice } from '@reduxjs/toolkit' -import camelCase from 'lodash.camelcase' -import { RootState } from '@src/store' -import { ASSIGNEE_FILTER_TYPES, CYCLE_TIME_LIST, MESSAGE, METRICS_CONSTANTS } from '@src/constants/resources' -import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice' -import _ from 'lodash' +import { createSlice } from '@reduxjs/toolkit'; +import camelCase from 'lodash.camelcase'; +import { RootState } from '@src/store'; +import { ASSIGNEE_FILTER_TYPES, CYCLE_TIME_LIST, MESSAGE, METRICS_CONSTANTS } from '@src/constants/resources'; +import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice'; +import _ from 'lodash'; export interface IPipelineConfig { - id: number - organization: string - pipelineName: string - step: string - branches: string[] + id: number; + organization: string; + pipelineName: string; + step: string; + branches: string[]; } export interface IPipelineWarningMessageConfig { - id: number | null - organization: string | null - pipelineName: string | null - step: string | null + id: number | null; + organization: string | null; + pipelineName: string | null; + step: string | null; } export interface ICycleTimeSetting { - name: string - value: string + name: string; + value: string; } export interface IJiraColumnsWithValue { - name: string - statuses: string[] + name: string; + statuses: string[]; } export interface savedMetricsSettingState { - jiraColumns: { key: string; value: { name: string; statuses: string[] } }[] - targetFields: { name: string; key: string; flag: boolean }[] - users: string[] - pipelineCrews: string[] - doneColumn: string[] - cycleTimeSettings: ICycleTimeSetting[] - deploymentFrequencySettings: IPipelineConfig[] - leadTimeForChanges: IPipelineConfig[] - treatFlagCardAsBlock: boolean - assigneeFilter: string + jiraColumns: { key: string; value: { name: string; statuses: string[] } }[]; + targetFields: { name: string; key: string; flag: boolean }[]; + users: string[]; + pipelineCrews: string[]; + doneColumn: string[]; + cycleTimeSettings: ICycleTimeSetting[]; + deploymentFrequencySettings: IPipelineConfig[]; + leadTimeForChanges: IPipelineConfig[]; + treatFlagCardAsBlock: boolean; + assigneeFilter: string; importedData: { - importedCrews: string[] - importedAssigneeFilter: string - importedPipelineCrews: string[] + importedCrews: string[]; + importedAssigneeFilter: string; + importedPipelineCrews: string[]; importedCycleTime: { - importedCycleTimeSettings: { [key: string]: string }[] - importedTreatFlagCardAsBlock: boolean - } - importedDoneStatus: string[] - importedClassification: string[] - importedDeployment: IPipelineConfig[] - } - cycleTimeWarningMessage: string | null - classificationWarningMessage: string | null - realDoneWarningMessage: string | null - deploymentWarningMessage: IPipelineWarningMessageConfig[] + importedCycleTimeSettings: { [key: string]: string }[]; + importedTreatFlagCardAsBlock: boolean; + }; + importedDoneStatus: string[]; + importedClassification: string[]; + importedDeployment: IPipelineConfig[]; + }; + cycleTimeWarningMessage: string | null; + classificationWarningMessage: string | null; + realDoneWarningMessage: string | null; + deploymentWarningMessage: IPipelineWarningMessageConfig[]; } const initialState: savedMetricsSettingState = { @@ -86,49 +86,49 @@ const initialState: savedMetricsSettingState = { classificationWarningMessage: null, realDoneWarningMessage: null, deploymentWarningMessage: [], -} +}; const compareArrays = (arrayA: string[], arrayB: string[]): string | null => { if (arrayA?.length > arrayB?.length) { - const differentValues = arrayA?.filter((value) => !arrayB.includes(value)) - return `The column of ${differentValues} is a deleted column, which means this column existed the time you saved config, but was deleted. Please confirm!` + const differentValues = arrayA?.filter((value) => !arrayB.includes(value)); + return `The column of ${differentValues} is a deleted column, which means this column existed the time you saved config, but was deleted. Please confirm!`; } else { - const differentValues = arrayB?.filter((value) => !arrayA.includes(value)) + const differentValues = arrayB?.filter((value) => !arrayA.includes(value)); return differentValues?.length > 0 ? `The column of ${differentValues} is a new column. Please select a value for it!` - : null + : null; } -} +}; const findDifferentValues = (arrayA: string[], arrayB: string[]): string[] | null => { - const diffInArrayA = arrayA?.filter((value) => !arrayB.includes(value)) + const diffInArrayA = arrayA?.filter((value) => !arrayB.includes(value)); if (diffInArrayA?.length === 0) { - return null + return null; } else { - return diffInArrayA + return diffInArrayA; } -} +}; const findKeyByValues = (arrayA: { [key: string]: string }[], arrayB: string[]): string | null => { - const matchingKeys: string[] = [] + const matchingKeys: string[] = []; for (const setting of arrayA) { - const key = Object.keys(setting)[0] - const value = setting[key] + const key = Object.keys(setting)[0]; + const value = setting[key]; if (arrayB.includes(value)) { - matchingKeys.push(key) + matchingKeys.push(key); } } - return `The value of ${matchingKeys} in imported json is not in dropdown list now. Please select a value for it!` -} + return `The value of ${matchingKeys} in imported json is not in dropdown list now. Please select a value for it!`; +}; const setSelectUsers = (users: string[], importedCrews: string[]) => - users.filter((item: string) => importedCrews?.includes(item)) + users.filter((item: string) => importedCrews?.includes(item)); const setPipelineCrews = (pipelineCrews: string[], importedPipelineCrews: string[]) => { if (_.isEmpty(pipelineCrews)) { - return [] + return []; } - return pipelineCrews.filter((item: string) => importedPipelineCrews?.includes(item)) -} + return pipelineCrews.filter((item: string) => importedPipelineCrews?.includes(item)); +}; const setSelectTargetFields = ( targetFields: { name: string; key: string; flag: boolean }[], @@ -137,22 +137,22 @@ const setSelectTargetFields = ( targetFields.map((item: { name: string; key: string; flag: boolean }) => ({ ...item, flag: importedClassification?.includes(item.key), - })) + })); const setCycleTimeSettings = ( jiraColumns: { key: string; value: { name: string; statuses: string[] } }[], importedCycleTimeSettings: { [key: string]: string }[] ) => { return jiraColumns?.map((item: { key: string; value: { name: string; statuses: string[] } }) => { - const controlName = item.value.name - let defaultOptionValue = METRICS_CONSTANTS.cycleTimeEmptyStr - const validImportValue = importedCycleTimeSettings?.find((i) => Object.keys(i)[0] === controlName) + const controlName = item.value.name; + let defaultOptionValue = METRICS_CONSTANTS.cycleTimeEmptyStr; + const validImportValue = importedCycleTimeSettings?.find((i) => Object.keys(i)[0] === controlName); if (validImportValue && CYCLE_TIME_LIST.includes(Object.values(validImportValue)[0])) { - defaultOptionValue = Object.values(validImportValue)[0] + defaultOptionValue = Object.values(validImportValue)[0]; } - return { name: controlName, value: defaultOptionValue } - }) -} + return { name: controlName, value: defaultOptionValue }; + }); +}; const setSelectDoneColumns = ( jiraColumns: { key: string; value: { name: string; statuses: string[] } }[], @@ -160,49 +160,49 @@ const setSelectDoneColumns = ( importedDoneStatus: string[] ) => { const doneStatus = - jiraColumns?.find((item) => item.key === METRICS_CONSTANTS.doneKeyFromBackend)?.value.statuses ?? [] + jiraColumns?.find((item) => item.key === METRICS_CONSTANTS.doneKeyFromBackend)?.value.statuses ?? []; const selectedDoneColumns = cycleTimeSettings ?.filter(({ value }) => value === METRICS_CONSTANTS.doneValue) - .map(({ name }) => name) + .map(({ name }) => name); const filteredStatus = jiraColumns ?.filter(({ value }) => selectedDoneColumns.includes(value.name)) - .flatMap(({ value }) => value.statuses) - const status = selectedDoneColumns?.length < 1 ? doneStatus : filteredStatus - return status.filter((item: string) => importedDoneStatus?.includes(item)) -} + .flatMap(({ value }) => value.statuses); + const status = selectedDoneColumns?.length < 1 ? doneStatus : filteredStatus; + return status.filter((item: string) => importedDoneStatus?.includes(item)); +}; export const metricsSlice = createSlice({ name: 'metrics', initialState, reducers: { saveTargetFields: (state, action) => { - state.targetFields = action.payload + state.targetFields = action.payload; }, saveDoneColumn: (state, action) => { - state.doneColumn = action.payload + state.doneColumn = action.payload; }, saveUsers: (state, action) => { - state.users = action.payload + state.users = action.payload; }, savePipelineCrews: (state, action) => { - state.pipelineCrews = action.payload + state.pipelineCrews = action.payload; }, saveCycleTimeSettings: (state, action) => { - state.cycleTimeSettings = action.payload + state.cycleTimeSettings = action.payload; }, addADeploymentFrequencySetting: (state) => { const newId = state.deploymentFrequencySettings.length >= 1 ? state.deploymentFrequencySettings[state.deploymentFrequencySettings.length - 1].id + 1 - : 0 + : 0; state.deploymentFrequencySettings = [ ...state.deploymentFrequencySettings, { id: newId, organization: '', pipelineName: '', step: '', branches: [] }, - ] + ]; }, updateDeploymentFrequencySettings: (state, action) => { - const { updateId, label, value } = action.payload + const { updateId, label, value } = action.payload; state.deploymentFrequencySettings = state.deploymentFrequencySettings.map((deploymentFrequencySetting) => { return deploymentFrequencySetting.id === updateId @@ -210,107 +210,112 @@ export const metricsSlice = createSlice({ ...deploymentFrequencySetting, [label === 'Steps' ? 'step' : camelCase(label)]: value, } - : deploymentFrequencySetting - }) + : deploymentFrequencySetting; + }); }, updateMetricsImportedData: (state, action) => { const { crews, cycleTime, doneStatus, classification, deployment, leadTime, assigneeFilter, pipelineCrews } = - action.payload - state.importedData.importedCrews = crews || state.importedData.importedCrews - state.importedData.importedPipelineCrews = pipelineCrews || state.importedData.importedPipelineCrews + action.payload; + state.importedData.importedCrews = crews || state.importedData.importedCrews; + state.importedData.importedPipelineCrews = pipelineCrews || state.importedData.importedPipelineCrews; state.importedData.importedCycleTime.importedCycleTimeSettings = - cycleTime?.jiraColumns || state.importedData.importedCycleTime.importedCycleTimeSettings + cycleTime?.jiraColumns || state.importedData.importedCycleTime.importedCycleTimeSettings; state.importedData.importedCycleTime.importedTreatFlagCardAsBlock = - cycleTime?.treatFlagCardAsBlock && state.importedData.importedCycleTime.importedTreatFlagCardAsBlock - state.importedData.importedAssigneeFilter = assigneeFilter || state.importedData.importedAssigneeFilter - state.importedData.importedDoneStatus = doneStatus || state.importedData.importedDoneStatus - state.importedData.importedClassification = classification || state.importedData.importedClassification - state.importedData.importedDeployment = deployment || leadTime || state.importedData.importedDeployment + cycleTime?.treatFlagCardAsBlock && state.importedData.importedCycleTime.importedTreatFlagCardAsBlock; + state.importedData.importedAssigneeFilter = assigneeFilter || state.importedData.importedAssigneeFilter; + state.importedData.importedDoneStatus = doneStatus || state.importedData.importedDoneStatus; + state.importedData.importedClassification = classification || state.importedData.importedClassification; + state.importedData.importedDeployment = deployment || leadTime || state.importedData.importedDeployment; }, updateMetricsState: (state, action) => { - const { targetFields, users, jiraColumns, isProjectCreated, ignoredTargetFields } = action.payload + const { targetFields, users, jiraColumns, isProjectCreated, ignoredTargetFields } = action.payload; const { importedCrews, importedClassification, importedCycleTime, importedDoneStatus, importedAssigneeFilter } = - state.importedData - state.users = isProjectCreated ? users : setSelectUsers(users, importedCrews) - state.targetFields = isProjectCreated ? targetFields : setSelectTargetFields(targetFields, importedClassification) + state.importedData; + state.users = isProjectCreated ? users : setSelectUsers(users, importedCrews); + state.targetFields = isProjectCreated + ? targetFields + : setSelectTargetFields(targetFields, importedClassification); if (!isProjectCreated && importedCycleTime?.importedCycleTimeSettings?.length > 0) { const importedCycleTimeSettingsKeys = importedCycleTime.importedCycleTimeSettings.flatMap((obj) => Object.keys(obj) - ) + ); const importedCycleTimeSettingsValues = importedCycleTime.importedCycleTimeSettings.flatMap((obj) => Object.values(obj) - ) + ); const jiraColumnsNames = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value.name - ) - const metricsContainsValues = Object.values(METRICS_CONSTANTS) - const importedKeyMismatchWarning = compareArrays(importedCycleTimeSettingsKeys, jiraColumnsNames) - const importedValueMismatchWarning = findDifferentValues(importedCycleTimeSettingsValues, metricsContainsValues) + ); + const metricsContainsValues = Object.values(METRICS_CONSTANTS); + const importedKeyMismatchWarning = compareArrays(importedCycleTimeSettingsKeys, jiraColumnsNames); + const importedValueMismatchWarning = findDifferentValues( + importedCycleTimeSettingsValues, + metricsContainsValues + ); const getWarningMessage = (): string | null => { if (importedKeyMismatchWarning?.length) { - return compareArrays(importedCycleTimeSettingsKeys, jiraColumnsNames) + return compareArrays(importedCycleTimeSettingsKeys, jiraColumnsNames); } if (importedValueMismatchWarning?.length) { - return findKeyByValues(importedCycleTime.importedCycleTimeSettings, importedValueMismatchWarning) + return findKeyByValues(importedCycleTime.importedCycleTimeSettings, importedValueMismatchWarning); } - return null - } - state.cycleTimeWarningMessage = getWarningMessage() + return null; + }; + state.cycleTimeWarningMessage = getWarningMessage(); } else { - state.cycleTimeWarningMessage = null + state.cycleTimeWarningMessage = null; } if (!isProjectCreated && importedClassification?.length > 0) { - const keyArray = targetFields?.map((field: { key: string; name: string; flag: boolean }) => field.key) + const keyArray = targetFields?.map((field: { key: string; name: string; flag: boolean }) => field.key); const ignoredKeyArray = ignoredTargetFields?.map( (field: { key: string; name: string; flag: boolean }) => field.key - ) - const filteredImportedClassification = importedClassification.filter((item) => !ignoredKeyArray.includes(item)) + ); + const filteredImportedClassification = importedClassification.filter((item) => !ignoredKeyArray.includes(item)); if (filteredImportedClassification.every((item) => keyArray.includes(item))) { - state.classificationWarningMessage = null + state.classificationWarningMessage = null; } else { - state.classificationWarningMessage = MESSAGE.CLASSIFICATION_WARNING + state.classificationWarningMessage = MESSAGE.CLASSIFICATION_WARNING; } } else { - state.classificationWarningMessage = null + state.classificationWarningMessage = null; } - state.cycleTimeSettings = setCycleTimeSettings(jiraColumns, importedCycleTime.importedCycleTimeSettings) + state.cycleTimeSettings = setCycleTimeSettings(jiraColumns, importedCycleTime.importedCycleTimeSettings); if (!isProjectCreated && !!importedDoneStatus.length) { setSelectDoneColumns(jiraColumns, state.cycleTimeSettings, importedDoneStatus).length < importedDoneStatus.length ? (state.realDoneWarningMessage = MESSAGE.REAL_DONE_WARNING) - : (state.realDoneWarningMessage = null) + : (state.realDoneWarningMessage = null); } state.doneColumn = isProjectCreated ? [] - : setSelectDoneColumns(jiraColumns, state.cycleTimeSettings, importedDoneStatus) + : setSelectDoneColumns(jiraColumns, state.cycleTimeSettings, importedDoneStatus); state.assigneeFilter = importedAssigneeFilter === ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE || importedAssigneeFilter === ASSIGNEE_FILTER_TYPES.HISTORICAL_ASSIGNEE ? importedAssigneeFilter - : ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE + : ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE; state.treatFlagCardAsBlock = typeof importedCycleTime.importedTreatFlagCardAsBlock === 'boolean' ? importedCycleTime.importedTreatFlagCardAsBlock - : true + : true; }, updatePipelineSettings: (state, action) => { - const { pipelineList, isProjectCreated, pipelineCrews } = action.payload - const { importedDeployment, importedPipelineCrews } = state.importedData - state.pipelineCrews = isProjectCreated ? pipelineCrews : setPipelineCrews(pipelineCrews, importedPipelineCrews) - const orgNames: Array = _.uniq(pipelineList.map((item: pipeline) => item.orgName)) + const { pipelineList, isProjectCreated, pipelineCrews } = action.payload; + const { importedDeployment, importedPipelineCrews } = state.importedData; + state.pipelineCrews = isProjectCreated ? pipelineCrews : setPipelineCrews(pipelineCrews, importedPipelineCrews); + const orgNames: Array = _.uniq(pipelineList.map((item: pipeline) => item.orgName)); const filteredPipelineNames = (organization: string) => pipelineList .filter((pipeline: pipeline) => pipeline.orgName.toLowerCase() === organization.toLowerCase()) - .map((item: pipeline) => item.name) + .map((item: pipeline) => item.name); const getValidPipelines = (pipelines: IPipelineConfig[]) => !pipelines.length || isProjectCreated ? [{ id: 0, organization: '', pipelineName: '', step: '', branches: [] }] @@ -320,48 +325,48 @@ export const metricsSlice = createSlice({ pipelineName: filteredPipelineNames(organization).includes(pipelineName) ? pipelineName : '', step: '', branches: [], - })) + })); const createPipelineWarning = ({ id, organization, pipelineName }: IPipelineConfig) => { const orgWarning = orgNames.some((i) => (i as string).toLowerCase() === organization.toLowerCase()) ? null - : MESSAGE.ORGANIZATION_WARNING + : MESSAGE.ORGANIZATION_WARNING; const pipelineNameWarning = orgWarning || filteredPipelineNames(organization).includes(pipelineName) ? null - : MESSAGE.PIPELINE_NAME_WARNING + : MESSAGE.PIPELINE_NAME_WARNING; return { id, organization: orgWarning, pipelineName: pipelineNameWarning, step: null, - } - } + }; + }; const getPipelinesWarningMessage = (pipelines: IPipelineConfig[]) => { if (!pipelines.length || isProjectCreated) { - return [] + return []; } - return pipelines.map((pipeline) => createPipelineWarning(pipeline)) - } + return pipelines.map((pipeline) => createPipelineWarning(pipeline)); + }; - state.deploymentFrequencySettings = getValidPipelines(importedDeployment) - state.deploymentWarningMessage = getPipelinesWarningMessage(importedDeployment) + state.deploymentFrequencySettings = getValidPipelines(importedDeployment); + state.deploymentWarningMessage = getPipelinesWarningMessage(importedDeployment); }, updatePipelineStep: (state, action) => { - const { steps, id, branches, pipelineCrews } = action.payload - const { importedDeployment, importedPipelineCrews } = state.importedData - const updatedImportedPipeline = importedDeployment - const updatedImportedPipelineStep = updatedImportedPipeline.find((pipeline) => pipeline.id === id)?.step ?? '' + const { steps, id, branches, pipelineCrews } = action.payload; + const { importedDeployment, importedPipelineCrews } = state.importedData; + const updatedImportedPipeline = importedDeployment; + const updatedImportedPipelineStep = updatedImportedPipeline.find((pipeline) => pipeline.id === id)?.step ?? ''; const updatedImportedPipelineBranches = - updatedImportedPipeline.find((pipeline) => pipeline.id === id)?.branches ?? [] - const validStep = steps.includes(updatedImportedPipelineStep) ? updatedImportedPipelineStep : '' - const validBranches = _.filter(branches, (branch) => updatedImportedPipelineBranches.includes(branch)) - const validPipelineCrews = _.filter(pipelineCrews, (crew) => importedPipelineCrews.includes(crew)) - state.pipelineCrews = validPipelineCrews - const stepWarningMessage = steps.includes(updatedImportedPipelineStep) ? null : MESSAGE.STEP_WARNING + updatedImportedPipeline.find((pipeline) => pipeline.id === id)?.branches ?? []; + const validStep = steps.includes(updatedImportedPipelineStep) ? updatedImportedPipelineStep : ''; + const validBranches = _.filter(branches, (branch) => updatedImportedPipelineBranches.includes(branch)); + const validPipelineCrews = _.filter(pipelineCrews, (crew) => importedPipelineCrews.includes(crew)); + state.pipelineCrews = validPipelineCrews; + const stepWarningMessage = steps.includes(updatedImportedPipelineStep) ? null : MESSAGE.STEP_WARNING; const getPipelineSettings = (pipelines: IPipelineConfig[]) => pipelines.map((pipeline) => @@ -372,7 +377,7 @@ export const metricsSlice = createSlice({ branches: validBranches, } : pipeline - ) + ); const getStepWarningMessage = (pipelines: IPipelineWarningMessageConfig[]) => { return pipelines.map((pipeline) => @@ -382,31 +387,31 @@ export const metricsSlice = createSlice({ step: stepWarningMessage, } : pipeline - ) - } + ); + }; - state.deploymentFrequencySettings = getPipelineSettings(state.deploymentFrequencySettings) - state.deploymentWarningMessage = getStepWarningMessage(state.deploymentWarningMessage) + state.deploymentFrequencySettings = getPipelineSettings(state.deploymentFrequencySettings); + state.deploymentWarningMessage = getStepWarningMessage(state.deploymentWarningMessage); }, deleteADeploymentFrequencySetting: (state, action) => { - const deleteId = action.payload - state.deploymentFrequencySettings = [...state.deploymentFrequencySettings.filter(({ id }) => id !== deleteId)] + const deleteId = action.payload; + state.deploymentFrequencySettings = [...state.deploymentFrequencySettings.filter(({ id }) => id !== deleteId)]; }, initDeploymentFrequencySettings: (state) => { - state.deploymentFrequencySettings = initialState.deploymentFrequencySettings + state.deploymentFrequencySettings = initialState.deploymentFrequencySettings; }, updateTreatFlagCardAsBlock: (state, action) => { - state.treatFlagCardAsBlock = action.payload + state.treatFlagCardAsBlock = action.payload; }, updateAssigneeFilter: (state, action) => { - state.assigneeFilter = action.payload + state.assigneeFilter = action.payload; }, }, -}) +}); export const { saveTargetFields, @@ -424,35 +429,35 @@ export const { updateMetricsState, updatePipelineSettings, updatePipelineStep, -} = metricsSlice.actions +} = metricsSlice.actions; -export const selectDeploymentFrequencySettings = (state: RootState) => state.metrics.deploymentFrequencySettings -export const selectLeadTimeForChanges = (state: RootState) => state.metrics.leadTimeForChanges +export const selectDeploymentFrequencySettings = (state: RootState) => state.metrics.deploymentFrequencySettings; +export const selectLeadTimeForChanges = (state: RootState) => state.metrics.leadTimeForChanges; -export const selectCycleTimeSettings = (state: RootState) => state.metrics.cycleTimeSettings -export const selectMetricsContent = (state: RootState) => state.metrics -export const selectTreatFlagCardAsBlock = (state: RootState) => state.metrics.treatFlagCardAsBlock -export const selectAssigneeFilter = (state: RootState) => state.metrics.assigneeFilter -export const selectCycleTimeWarningMessage = (state: RootState) => state.metrics.cycleTimeWarningMessage -export const selectClassificationWarningMessage = (state: RootState) => state.metrics.classificationWarningMessage -export const selectRealDoneWarningMessage = (state: RootState) => state.metrics.realDoneWarningMessage +export const selectCycleTimeSettings = (state: RootState) => state.metrics.cycleTimeSettings; +export const selectMetricsContent = (state: RootState) => state.metrics; +export const selectTreatFlagCardAsBlock = (state: RootState) => state.metrics.treatFlagCardAsBlock; +export const selectAssigneeFilter = (state: RootState) => state.metrics.assigneeFilter; +export const selectCycleTimeWarningMessage = (state: RootState) => state.metrics.cycleTimeWarningMessage; +export const selectClassificationWarningMessage = (state: RootState) => state.metrics.classificationWarningMessage; +export const selectRealDoneWarningMessage = (state: RootState) => state.metrics.realDoneWarningMessage; export const selectOrganizationWarningMessage = (state: RootState, id: number, type: string) => { - const { deploymentWarningMessage } = state.metrics - const warningMessage = deploymentWarningMessage - return warningMessage.find((item) => item.id === id)?.organization -} + const { deploymentWarningMessage } = state.metrics; + const warningMessage = deploymentWarningMessage; + return warningMessage.find((item) => item.id === id)?.organization; +}; export const selectPipelineNameWarningMessage = (state: RootState, id: number, type: string) => { - const { deploymentWarningMessage } = state.metrics - const warningMessage = deploymentWarningMessage - return warningMessage.find((item) => item.id === id)?.pipelineName -} + const { deploymentWarningMessage } = state.metrics; + const warningMessage = deploymentWarningMessage; + return warningMessage.find((item) => item.id === id)?.pipelineName; +}; export const selectStepWarningMessage = (state: RootState, id: number, type: string) => { - const { deploymentWarningMessage } = state.metrics - const warningMessage = deploymentWarningMessage - return warningMessage.find((item) => item.id === id)?.step -} + const { deploymentWarningMessage } = state.metrics; + const warningMessage = deploymentWarningMessage; + return warningMessage.find((item) => item.id === id)?.step; +}; -export default metricsSlice.reducer +export default metricsSlice.reducer; diff --git a/frontend/src/context/config/board/boardSlice.ts b/frontend/src/context/config/board/boardSlice.ts index 5e7896b022..81ff80e4ef 100644 --- a/frontend/src/context/config/board/boardSlice.ts +++ b/frontend/src/context/config/board/boardSlice.ts @@ -1,26 +1,26 @@ -import { BOARD_TYPES } from '@src/constants/resources' +import { BOARD_TYPES } from '@src/constants/resources'; export interface IBoardVerifyResponseState { - jiraColumns: { key: string; value: { name: string; statuses: string[] } }[] - targetFields: { name: string; key: string; flag: boolean }[] - users: string[] + jiraColumns: { key: string; value: { name: string; statuses: string[] } }[]; + targetFields: { name: string; key: string; flag: boolean }[]; + users: string[]; } export interface IBoardState { - config: { type: string; boardId: string; email: string; projectKey: string; site: string; token: string } - isVerified: boolean - isShow: boolean - verifiedResponse: IBoardVerifyResponseState + config: { type: string; boardId: string; email: string; projectKey: string; site: string; token: string }; + isVerified: boolean; + isShow: boolean; + verifiedResponse: IBoardVerifyResponseState; } export const initialVerifiedBoardState: IBoardVerifyResponseState = { jiraColumns: [], targetFields: [], users: [], -} +}; export const initialBoardState: IBoardState = { config: { type: BOARD_TYPES.JIRA, boardId: '', email: '', projectKey: '', site: '', token: '' }, isVerified: false, isShow: false, verifiedResponse: initialVerifiedBoardState, -} +}; diff --git a/frontend/src/context/config/configSlice.ts b/frontend/src/context/config/configSlice.ts index eac7b16ce8..90d4c06a2a 100644 --- a/frontend/src/context/config/configSlice.ts +++ b/frontend/src/context/config/configSlice.ts @@ -1,29 +1,29 @@ -import { createSlice } from '@reduxjs/toolkit' -import type { RootState } from '@src/store' -import { BOARD_METRICS, CALENDAR, DORA_METRICS, MESSAGE } from '@src/constants/resources' -import { REQUIRED_DATA } from '@src/constants/resources' -import { IBoardState, initialBoardState } from '@src/context/config/board/boardSlice' -import { initialPipelineToolState, IPipelineToolState } from '@src/context/config/pipelineTool/pipelineToolSlice' -import { initialSourceControlState, ISourceControl } from '@src/context/config/sourceControl/sourceControlSlice' -import dayjs from 'dayjs' -import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice' -import _ from 'lodash' +import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from '@src/store'; +import { BOARD_METRICS, CALENDAR, DORA_METRICS, MESSAGE } from '@src/constants/resources'; +import { REQUIRED_DATA } from '@src/constants/resources'; +import { IBoardState, initialBoardState } from '@src/context/config/board/boardSlice'; +import { initialPipelineToolState, IPipelineToolState } from '@src/context/config/pipelineTool/pipelineToolSlice'; +import { initialSourceControlState, ISourceControl } from '@src/context/config/sourceControl/sourceControlSlice'; +import dayjs from 'dayjs'; +import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice'; +import _ from 'lodash'; export interface BasicConfigState { - isProjectCreated: boolean + isProjectCreated: boolean; basic: { - projectName: string - calendarType: string + projectName: string; + calendarType: string; dateRange: { - startDate: string | null - endDate: string | null - } - metrics: string[] - } - board: IBoardState - pipelineTool: IPipelineToolState - sourceControl: ISourceControl - warningMessage: string | null + startDate: string | null; + endDate: string | null; + }; + metrics: string[]; + }; + board: IBoardState; + pipelineTool: IPipelineToolState; + sourceControl: ISourceControl; + warningMessage: string | null; } export const initialBasicConfigState: BasicConfigState = { @@ -41,7 +41,7 @@ export const initialBasicConfigState: BasicConfigState = { pipelineTool: initialPipelineToolState, sourceControl: initialSourceControlState, warningMessage: null, -} +}; export const configSlice = createSlice({ name: 'config', @@ -53,14 +53,14 @@ export const configSlice = createSlice({ }, reducers: { updateProjectName: (state, action) => { - state.basic.projectName = action.payload + state.basic.projectName = action.payload; }, updateCalendarType: (state, action) => { - state.basic.calendarType = action.payload + state.basic.calendarType = action.payload; }, updateDateRange: (state, action) => { - const { startDate, endDate } = action.payload - state.basic.dateRange = { startDate, endDate } + const { startDate, endDate } = action.payload; + state.basic.dateRange = { startDate, endDate }; }, updateMetrics: (state, action) => { const { @@ -71,65 +71,67 @@ export const configSlice = createSlice({ DEPLOYMENT_FREQUENCY, CHANGE_FAILURE_RATE, MEAN_TIME_TO_RECOVERY, - } = REQUIRED_DATA + } = REQUIRED_DATA; - state.basic.metrics = action.payload + state.basic.metrics = action.payload; - state.board.isShow = [VELOCITY, CYCLE_TIME, CLASSIFICATION].some((metric) => state.basic.metrics.includes(metric)) + state.board.isShow = [VELOCITY, CYCLE_TIME, CLASSIFICATION].some((metric) => + state.basic.metrics.includes(metric) + ); state.pipelineTool.isShow = [ LEAD_TIME_FOR_CHANGES, DEPLOYMENT_FREQUENCY, CHANGE_FAILURE_RATE, MEAN_TIME_TO_RECOVERY, - ].some((metric) => state.basic.metrics.includes(metric)) - state.sourceControl.isShow = [LEAD_TIME_FOR_CHANGES].some((metric) => state.basic.metrics.includes(metric)) - state.basic.metrics = action.payload + ].some((metric) => state.basic.metrics.includes(metric)); + state.sourceControl.isShow = [LEAD_TIME_FOR_CHANGES].some((metric) => state.basic.metrics.includes(metric)); + state.basic.metrics = action.payload; }, updateBasicConfigState: (state, action) => { - state.basic = action.payload - const { projectName, dateRange, metrics } = state.basic + state.basic = action.payload; + const { projectName, dateRange, metrics } = state.basic; if (!state.isProjectCreated) { state.warningMessage = projectName && dateRange.startDate && dateRange.endDate && metrics.length > 0 ? null - : MESSAGE.CONFIG_PAGE_VERIFY_IMPORT_ERROR + : MESSAGE.CONFIG_PAGE_VERIFY_IMPORT_ERROR; } - state.board.config = action.payload.board || state.board.config - state.pipelineTool.config = action.payload.pipelineTool || state.pipelineTool.config - state.sourceControl.config = action.payload.sourceControl || state.sourceControl.config + state.board.config = action.payload.board || state.board.config; + state.pipelineTool.config = action.payload.pipelineTool || state.pipelineTool.config; + state.sourceControl.config = action.payload.sourceControl || state.sourceControl.config; }, updateProjectCreatedState: (state, action) => { - state.isProjectCreated = action.payload + state.isProjectCreated = action.payload; }, updateBoardVerifyState: (state, action) => { - state.board.isVerified = action.payload + state.board.isVerified = action.payload; }, updateBoard: (state, action) => { - state.board.config = action.payload + state.board.config = action.payload; }, updateJiraVerifyResponse: (state, action) => { - const { jiraColumns, targetFields, users } = action.payload - state.board.verifiedResponse.jiraColumns = jiraColumns - state.board.verifiedResponse.targetFields = targetFields - state.board.verifiedResponse.users = users + const { jiraColumns, targetFields, users } = action.payload; + state.board.verifiedResponse.jiraColumns = jiraColumns; + state.board.verifiedResponse.targetFields = targetFields; + state.board.verifiedResponse.users = users; }, updatePipelineToolVerifyState: (state, action) => { - state.pipelineTool.isVerified = action.payload + state.pipelineTool.isVerified = action.payload; }, updatePipelineTool: (state, action) => { - state.pipelineTool.config = action.payload + state.pipelineTool.config = action.payload; }, updatePipelineToolVerifyResponse: (state, action) => { - const { pipelineList } = action.payload + const { pipelineList } = action.payload; state.pipelineTool.verifiedResponse.pipelineList = pipelineList.map((pipeline: pipeline) => ({ ...pipeline, steps: [], - })) + })); }, updatePipelineToolVerifyResponseSteps: (state, action) => { - const { organization, pipelineName, steps, branches, pipelineCrews } = action.payload + const { organization, pipelineName, steps, branches, pipelineCrews } = action.payload; state.pipelineTool.verifiedResponse.pipelineList = state.pipelineTool.verifiedResponse.pipelineList.map( (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organization @@ -139,26 +141,26 @@ export const configSlice = createSlice({ steps: steps, } : pipeline - ) + ); state.pipelineTool.verifiedResponse.pipelineCrews = _.union( state.pipelineTool.verifiedResponse.pipelineCrews, pipelineCrews - ) + ); }, updateSourceControlVerifyState: (state, action) => { - state.sourceControl.isVerified = action.payload + state.sourceControl.isVerified = action.payload; }, updateSourceControl: (state, action) => { - state.sourceControl.config = action.payload + state.sourceControl.config = action.payload; }, updateSourceControlVerifiedResponse: (state, action) => { - const { githubRepos } = action.payload - state.sourceControl.verifiedResponse.repoList = githubRepos + const { githubRepos } = action.payload; + state.sourceControl.verifiedResponse.repoList = githubRepos; }, resetImportedData: () => initialBasicConfigState, }, -}) +}); export const { updateProjectCreatedState, updateProjectName, @@ -177,7 +179,7 @@ export const { updateSourceControlVerifiedResponse, updatePipelineToolVerifyResponseSteps, resetImportedData, -} = configSlice.actions +} = configSlice.actions; export const selectProjectName = (state: RootState) => state.config.basic.projectName export const selectCalendarType = (state: RootState) => state.config.basic.calendarType @@ -194,29 +196,29 @@ export const isSourceControlVerified = (state: RootState) => state.config.source export const selectSourceControl = (state: RootState) => state.config.sourceControl.config export const selectWarningMessage = (state: RootState) => state.config.warningMessage -export const selectConfig = (state: RootState) => state.config +export const selectConfig = (state: RootState) => state.config; -export const selectIsBoardVerified = (state: RootState) => state.config.board.isVerified -export const selectUsers = (state: RootState) => state.config.board.verifiedResponse.users -export const selectJiraColumns = (state: RootState) => state.config.board.verifiedResponse.jiraColumns -export const selectIsProjectCreated = (state: RootState) => state.config.isProjectCreated +export const selectIsBoardVerified = (state: RootState) => state.config.board.isVerified; +export const selectUsers = (state: RootState) => state.config.board.verifiedResponse.users; +export const selectJiraColumns = (state: RootState) => state.config.board.verifiedResponse.jiraColumns; +export const selectIsProjectCreated = (state: RootState) => state.config.isProjectCreated; export const selectPipelineOrganizations = (state: RootState) => [ ...new Set(state.config.pipelineTool.verifiedResponse.pipelineList.map((item) => item.orgName)), -] +]; export const selectPipelineNames = (state: RootState, organization: string) => state.config.pipelineTool.verifiedResponse.pipelineList .filter((pipeline) => pipeline.orgName === organization) .map((item) => item.name) - .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })) + .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })); export const selectStepsParams = (state: RootState, organizationName: string, pipelineName: string) => { const pipeline = state.config.pipelineTool.verifiedResponse.pipelineList.find( (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organizationName - ) - const { startDate, endDate } = state.config.basic.dateRange - const pipelineType = state.config.pipelineTool.config.type - const token = state.config.pipelineTool.config.token + ); + const { startDate, endDate } = state.config.basic.dateRange; + const pipelineType = state.config.pipelineTool.config.type; + const token = state.config.pipelineTool.config.token; return { params: { @@ -230,20 +232,20 @@ export const selectStepsParams = (state: RootState, organizationName: string, pi organizationId: pipeline?.orgId ?? '', pipelineType, token, - } -} + }; +}; export const selectSteps = (state: RootState, organizationName: string, pipelineName: string) => state.config.pipelineTool.verifiedResponse.pipelineList.find( (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organizationName - )?.steps ?? [] + )?.steps ?? []; export const selectBranches = (state: RootState, organizationName: string, pipelineName: string) => state.config.pipelineTool.verifiedResponse.pipelineList.find( /* istanbul ignore next */ (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organizationName - )?.branches ?? [] + )?.branches ?? []; -export const selectPipelineCrews = (state: RootState) => state.config.pipelineTool.verifiedResponse.pipelineCrews +export const selectPipelineCrews = (state: RootState) => state.config.pipelineTool.verifiedResponse.pipelineCrews; -export default configSlice.reducer +export default configSlice.reducer; diff --git a/frontend/src/context/config/pipelineTool/pipelineToolSlice.ts b/frontend/src/context/config/pipelineTool/pipelineToolSlice.ts index 7185e7337f..0ccfd27a73 100644 --- a/frontend/src/context/config/pipelineTool/pipelineToolSlice.ts +++ b/frontend/src/context/config/pipelineTool/pipelineToolSlice.ts @@ -1,11 +1,11 @@ -import { PIPELINE_TOOL_TYPES } from '@src/constants/resources' -import { initialPipelineToolVerifiedResponseState, IPipelineToolVerifyResponse } from './verifyResponseSlice' +import { PIPELINE_TOOL_TYPES } from '@src/constants/resources'; +import { initialPipelineToolVerifiedResponseState, IPipelineToolVerifyResponse } from './verifyResponseSlice'; export interface IPipelineToolState { - config: { type: string; token: string } - isVerified: boolean - isShow: boolean - verifiedResponse: IPipelineToolVerifyResponse + config: { type: string; token: string }; + isVerified: boolean; + isShow: boolean; + verifiedResponse: IPipelineToolVerifyResponse; } export const initialPipelineToolState: IPipelineToolState = { @@ -16,4 +16,4 @@ export const initialPipelineToolState: IPipelineToolState = { isVerified: false, isShow: false, verifiedResponse: initialPipelineToolVerifiedResponseState, -} +}; diff --git a/frontend/src/context/config/pipelineTool/verifyResponseSlice.ts b/frontend/src/context/config/pipelineTool/verifyResponseSlice.ts index 86423b118e..f8ac16642a 100644 --- a/frontend/src/context/config/pipelineTool/verifyResponseSlice.ts +++ b/frontend/src/context/config/pipelineTool/verifyResponseSlice.ts @@ -1,19 +1,19 @@ export interface IPipelineToolVerifyResponse { - pipelineList: pipeline[] - pipelineCrews: string[] + pipelineList: pipeline[]; + pipelineCrews: string[]; } export interface pipeline { - id: string - name: string - orgId: string - orgName: string - repository: string - steps: string[] - branches: string[] + id: string; + name: string; + orgId: string; + orgName: string; + repository: string; + steps: string[]; + branches: string[]; } export const initialPipelineToolVerifiedResponseState: IPipelineToolVerifyResponse = { pipelineList: [], pipelineCrews: [], -} +}; diff --git a/frontend/src/context/config/sourceControl/sourceControlSlice.ts b/frontend/src/context/config/sourceControl/sourceControlSlice.ts index 9f652b5908..da6ddfc817 100644 --- a/frontend/src/context/config/sourceControl/sourceControlSlice.ts +++ b/frontend/src/context/config/sourceControl/sourceControlSlice.ts @@ -1,11 +1,11 @@ -import { SOURCE_CONTROL_TYPES } from '@src/constants/resources' -import { initSourceControlVerifyResponseState, ISourceControlVerifyResponse } from './verifyResponseSlice' +import { SOURCE_CONTROL_TYPES } from '@src/constants/resources'; +import { initSourceControlVerifyResponseState, ISourceControlVerifyResponse } from './verifyResponseSlice'; export interface ISourceControl { - config: { type: string; token: string } - isVerified: boolean - isShow: boolean - verifiedResponse: ISourceControlVerifyResponse + config: { type: string; token: string }; + isVerified: boolean; + isShow: boolean; + verifiedResponse: ISourceControlVerifyResponse; } export const initialSourceControlState: ISourceControl = { @@ -16,4 +16,4 @@ export const initialSourceControlState: ISourceControl = { isVerified: false, isShow: false, verifiedResponse: initSourceControlVerifyResponseState, -} +}; diff --git a/frontend/src/context/config/sourceControl/verifyResponseSlice.ts b/frontend/src/context/config/sourceControl/verifyResponseSlice.ts index 248f710766..d3865be633 100644 --- a/frontend/src/context/config/sourceControl/verifyResponseSlice.ts +++ b/frontend/src/context/config/sourceControl/verifyResponseSlice.ts @@ -1,7 +1,7 @@ export interface ISourceControlVerifyResponse { - repoList: string[] + repoList: string[]; } export const initSourceControlVerifyResponseState: ISourceControlVerifyResponse = { repoList: [], -} +}; diff --git a/frontend/src/context/header/headerSlice.tsx b/frontend/src/context/header/headerSlice.tsx index 6bfecdf662..56de3bdd45 100644 --- a/frontend/src/context/header/headerSlice.tsx +++ b/frontend/src/context/header/headerSlice.tsx @@ -1,26 +1,26 @@ -import { createSlice } from '@reduxjs/toolkit' -import { RootState } from '@src/store' +import { createSlice } from '@reduxjs/toolkit'; +import { RootState } from '@src/store'; export interface headerState { - version: string + version: string; } const initialState: headerState = { version: '', -} +}; export const headerSlice = createSlice({ name: 'header', initialState, reducers: { saveVersion: (state, action) => { - state.version = action.payload + state.version = action.payload; }, }, -}) +}); -export const { saveVersion } = headerSlice.actions +export const { saveVersion } = headerSlice.actions; -export const getVersion = (state: RootState) => state.header.version +export const getVersion = (state: RootState) => state.header.version; -export default headerSlice.reducer +export default headerSlice.reducer; diff --git a/frontend/src/context/interface/index.tsx b/frontend/src/context/interface/index.tsx index 99fa232efb..4c9e832e23 100644 --- a/frontend/src/context/interface/index.tsx +++ b/frontend/src/context/interface/index.tsx @@ -1,7 +1,7 @@ export interface PipelineSetting { - id: number - organization: string - pipelineName: string - step: string - branches: string[] + id: number; + organization: string; + pipelineName: string; + step: string; + branches: string[]; } diff --git a/frontend/src/context/stepper/StepperSlice.tsx b/frontend/src/context/stepper/StepperSlice.tsx index c4f0615035..1d325e0752 100644 --- a/frontend/src/context/stepper/StepperSlice.tsx +++ b/frontend/src/context/stepper/StepperSlice.tsx @@ -1,40 +1,40 @@ -import { createSlice } from '@reduxjs/toolkit' -import type { RootState } from '@src/store' -import { ZERO } from '@src/constants/commons' +import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from '@src/store'; +import { ZERO } from '@src/constants/commons'; export interface StepState { - stepNumber: number - timeStamp: number + stepNumber: number; + timeStamp: number; } const initialState: StepState = { stepNumber: 0, timeStamp: 0, -} +}; export const stepperSlice = createSlice({ name: 'stepper', initialState, reducers: { resetStep: (state) => { - state.stepNumber = initialState.stepNumber - state.timeStamp = initialState.timeStamp + state.stepNumber = initialState.stepNumber; + state.timeStamp = initialState.timeStamp; }, nextStep: (state) => { - state.stepNumber += 1 + state.stepNumber += 1; }, backStep: (state) => { - state.stepNumber = state.stepNumber === ZERO ? ZERO : state.stepNumber - 1 + state.stepNumber = state.stepNumber === ZERO ? ZERO : state.stepNumber - 1; }, updateTimeStamp: (state, action) => { - state.timeStamp = action.payload + state.timeStamp = action.payload; }, }, -}) +}); -export const { resetStep, nextStep, backStep, updateTimeStamp } = stepperSlice.actions +export const { resetStep, nextStep, backStep, updateTimeStamp } = stepperSlice.actions; -export const selectStepNumber = (state: RootState) => state.stepper.stepNumber -export const selectTimeStamp = (state: RootState) => state.stepper.timeStamp +export const selectStepNumber = (state: RootState) => state.stepper.stepNumber; +export const selectTimeStamp = (state: RootState) => state.stepper.timeStamp; -export default stepperSlice.reducer +export default stepperSlice.reducer; diff --git a/frontend/src/emojis/emoji.ts b/frontend/src/emojis/emoji.ts index 129bff2e98..754ba54e55 100644 --- a/frontend/src/emojis/emoji.ts +++ b/frontend/src/emojis/emoji.ts @@ -1,43 +1,43 @@ -import { transformToCleanedBuildKiteEmoji } from '@src/utils/util' -import buildKiteEmojis from '@src/assets/buildkiteEmojis.json' -import appleEmojis from '@src/assets/appleEmojis.json' +import { transformToCleanedBuildKiteEmoji } from '@src/utils/util'; +import buildKiteEmojis from '@src/assets/buildkiteEmojis.json'; +import appleEmojis from '@src/assets/appleEmojis.json'; export interface OriginBuildKiteEmoji { - name: string - image: string - aliases: string[] + name: string; + image: string; + aliases: string[]; } export interface CleanedBuildKiteEmoji { - image: string - aliases: string[] + image: string; + aliases: string[]; } -const EMOJI_URL_PREFIX = 'https://buildkiteassets.com/emojis/' +const EMOJI_URL_PREFIX = 'https://buildkiteassets.com/emojis/'; -const DEFAULT_EMOJI = 'img-buildkite-64/buildkite.png' +const DEFAULT_EMOJI = 'img-buildkite-64/buildkite.png'; const cleanedEmojis: CleanedBuildKiteEmoji[] = (() => - transformToCleanedBuildKiteEmoji([...buildKiteEmojis, ...appleEmojis]))() + transformToCleanedBuildKiteEmoji([...buildKiteEmojis, ...appleEmojis]))(); const getEmojiNames = (input: string): string[] => { - const regex = /:([\w+-]+):/g - const matches = input.match(regex) || [] - return matches.map((match) => match.replaceAll(':', '')) -} + const regex = /:([\w+-]+):/g; + const matches = input.match(regex) || []; + return matches.map((match) => match.replaceAll(':', '')); +}; export const getEmojiUrls = (pipelineStepName: string): string[] => { - const emojiNames = getEmojiNames(pipelineStepName) + const emojiNames = getEmojiNames(pipelineStepName); return emojiNames.flatMap((name) => { - const emojiImage: string | undefined = cleanedEmojis.find(({ aliases }) => aliases.includes(name))?.image - return emojiImage ? `${EMOJI_URL_PREFIX}${emojiImage}` : `${EMOJI_URL_PREFIX}${DEFAULT_EMOJI}` - }) -} + const emojiImage: string | undefined = cleanedEmojis.find(({ aliases }) => aliases.includes(name))?.image; + return emojiImage ? `${EMOJI_URL_PREFIX}${emojiImage}` : `${EMOJI_URL_PREFIX}${DEFAULT_EMOJI}`; + }); +}; export const removeExtraEmojiName = (pipelineStepName: string): string => { - const emojiNames = getEmojiNames(pipelineStepName) + const emojiNames = getEmojiNames(pipelineStepName); emojiNames.map((name) => { - pipelineStepName = pipelineStepName.replaceAll(name, '') - }) - return pipelineStepName.replaceAll(':', '') -} + pipelineStepName = pipelineStepName.replaceAll(name, ''); + }); + return pipelineStepName.replaceAll(':', ''); +}; diff --git a/frontend/src/emojis/style.tsx b/frontend/src/emojis/style.tsx index 012139ff57..508c6ff225 100644 --- a/frontend/src/emojis/style.tsx +++ b/frontend/src/emojis/style.tsx @@ -1,17 +1,17 @@ -import { styled } from '@mui/material/styles' -import { Avatar, Typography } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { Avatar, Typography } from '@mui/material'; export const StyledAvatar = styled(Avatar)({ width: '1.25rem', height: '1.25rem', marginRight: '0.25rem', -}) +}); export const EmojiWrap = styled('div')({ display: 'flex', alignItems: 'center', -}) +}); export const StyledTypography = styled(Typography)({ fontSize: '0.88rem', -}) +}); diff --git a/frontend/src/exceptions/BadRequestException.ts b/frontend/src/exceptions/BadRequestException.ts index aaf7ae0237..9570d6d51b 100644 --- a/frontend/src/exceptions/BadRequestException.ts +++ b/frontend/src/exceptions/BadRequestException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class BadRequestException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/ExceptionType.ts b/frontend/src/exceptions/ExceptionType.ts index 9b90d52236..6f06324a15 100644 --- a/frontend/src/exceptions/ExceptionType.ts +++ b/frontend/src/exceptions/ExceptionType.ts @@ -1,4 +1,4 @@ export interface IHeartBeatException { - code?: number - message: string + code?: number; + message: string; } diff --git a/frontend/src/exceptions/ForbiddenException.ts b/frontend/src/exceptions/ForbiddenException.ts index a66db5de61..2538154bc1 100644 --- a/frontend/src/exceptions/ForbiddenException.ts +++ b/frontend/src/exceptions/ForbiddenException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class ForbiddenException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/InternalServerException.ts b/frontend/src/exceptions/InternalServerException.ts index 9a7e55b3b4..728a0f67ef 100644 --- a/frontend/src/exceptions/InternalServerException.ts +++ b/frontend/src/exceptions/InternalServerException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class InternalServerException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/NotFoundException.ts b/frontend/src/exceptions/NotFoundException.ts index db9766a26d..2e6aecfc12 100644 --- a/frontend/src/exceptions/NotFoundException.ts +++ b/frontend/src/exceptions/NotFoundException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class NotFoundException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/TimeoutException.ts b/frontend/src/exceptions/TimeoutException.ts index 079885e90a..1a5c5f3c63 100644 --- a/frontend/src/exceptions/TimeoutException.ts +++ b/frontend/src/exceptions/TimeoutException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class TimeoutException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/UnauthorizedException.ts b/frontend/src/exceptions/UnauthorizedException.ts index 42a93c78b7..792e95a1b8 100644 --- a/frontend/src/exceptions/UnauthorizedException.ts +++ b/frontend/src/exceptions/UnauthorizedException.ts @@ -1,9 +1,9 @@ -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class UnauthorizedException extends Error implements IHeartBeatException { - code: number + code: number; constructor(message: string, status: number) { - super(message) - this.code = status + super(message); + this.code = status; } } diff --git a/frontend/src/exceptions/UnkonwException.ts b/frontend/src/exceptions/UnkonwException.ts index 0fe5d58209..ffba8dff0d 100644 --- a/frontend/src/exceptions/UnkonwException.ts +++ b/frontend/src/exceptions/UnkonwException.ts @@ -1,8 +1,8 @@ -import { MESSAGE } from '@src/constants/resources' -import { IHeartBeatException } from '@src/exceptions/ExceptionType' +import { MESSAGE } from '@src/constants/resources'; +import { IHeartBeatException } from '@src/exceptions/ExceptionType'; export class UnknownException extends Error implements IHeartBeatException { constructor() { - super(MESSAGE.UNKNOWN_ERROR) + super(MESSAGE.UNKNOWN_ERROR); } } diff --git a/frontend/src/exceptions/index.ts b/frontend/src/exceptions/index.ts index 1f7cb18a5c..b922f34672 100644 --- a/frontend/src/exceptions/index.ts +++ b/frontend/src/exceptions/index.ts @@ -1,10 +1,10 @@ -import { BadRequestException } from '@src/exceptions/BadRequestException' -import { UnauthorizedException } from '@src/exceptions/UnauthorizedException' -import { ForbiddenException } from '@src/exceptions/ForbiddenException' -import { NotFoundException } from '@src/exceptions/NotFoundException' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { TimeoutException } from '@src/exceptions/TimeoutException' -import { UnknownException } from '@src/exceptions/UnkonwException' +import { BadRequestException } from '@src/exceptions/BadRequestException'; +import { UnauthorizedException } from '@src/exceptions/UnauthorizedException'; +import { ForbiddenException } from '@src/exceptions/ForbiddenException'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { TimeoutException } from '@src/exceptions/TimeoutException'; +import { UnknownException } from '@src/exceptions/UnkonwException'; export const isHeartBeatException = (o: unknown) => [ @@ -15,4 +15,4 @@ export const isHeartBeatException = (o: unknown) => InternalServerException, TimeoutException, UnknownException, - ].some((excptionClass) => o instanceof excptionClass) + ].some((excptionClass) => o instanceof excptionClass); diff --git a/frontend/src/fileConfig/fileConfig.ts b/frontend/src/fileConfig/fileConfig.ts index 730d988468..745f65a025 100644 --- a/frontend/src/fileConfig/fileConfig.ts +++ b/frontend/src/fileConfig/fileConfig.ts @@ -1,87 +1,87 @@ -import { CALENDAR } from '@src/constants/resources' +import { CALENDAR } from '@src/constants/resources'; export interface OldFileConfig { - projectName: string - metrics: string[] - startDate: string - endDate: string - considerHoliday: boolean + projectName: string; + metrics: string[]; + startDate: string; + endDate: string; + considerHoliday: boolean; board?: { - type?: string - verifyToken?: string - boardId?: string - token?: string - site?: string - email?: string - projectKey?: string - } + type?: string; + verifyToken?: string; + boardId?: string; + token?: string; + site?: string; + email?: string; + projectKey?: string; + }; pipelineTool?: { - type?: string - verifyToken?: string - token?: string - } + type?: string; + verifyToken?: string; + token?: string; + }; sourceControl?: { - type?: string - verifyToken?: string - token?: string - } - crews?: string[] - assigneeFilter?: string - cycleTime?: unknown - doneStatus?: string[] - classifications?: string[] - deployment?: OldConfigSetting[] - leadTime?: OldConfigSetting[] - pipelineCrews?: string[] + type?: string; + verifyToken?: string; + token?: string; + }; + crews?: string[]; + assigneeFilter?: string; + cycleTime?: unknown; + doneStatus?: string[]; + classifications?: string[]; + deployment?: OldConfigSetting[]; + leadTime?: OldConfigSetting[]; + pipelineCrews?: string[]; } interface OldConfigSetting { - pipelineId?: string - step?: string - orgId?: string - branches?: string[] + pipelineId?: string; + step?: string; + orgId?: string; + branches?: string[]; } interface NewConfigSetting { - id: number - organization?: string - pipelineName?: string - step?: string - branch?: string[] + id: number; + organization?: string; + pipelineName?: string; + step?: string; + branch?: string[]; } export interface NewFileConfig { - projectName: string + projectName: string; dateRange: { - startDate: string - endDate: string - } - calendarType: string - metrics: string[] + startDate: string; + endDate: string; + }; + calendarType: string; + metrics: string[]; board?: { - type?: string - boardId?: string - email?: string - projectKey?: string - site?: string - token?: string - } + type?: string; + boardId?: string; + email?: string; + projectKey?: string; + site?: string; + token?: string; + }; pipelineTool?: { - type?: string - token?: string - } + type?: string; + token?: string; + }; sourceControl?: { - type?: string - token?: string - } - crews?: string[] - assigneeFilter?: string - cycleTime?: unknown - doneStatus?: string[] - classification?: string[] - deployment?: NewConfigSetting[] - leadTime?: NewConfigSetting[] - pipelineCrews?: string[] + type?: string; + token?: string; + }; + crews?: string[]; + assigneeFilter?: string; + cycleTime?: unknown; + doneStatus?: string[]; + classification?: string[]; + deployment?: NewConfigSetting[]; + leadTime?: NewConfigSetting[]; + pipelineCrews?: string[]; } export const convertToNewFileConfig = (fileConfig: OldFileConfig | NewFileConfig): NewFileConfig => { if ('considerHoliday' in fileConfig) { @@ -101,7 +101,7 @@ export const convertToNewFileConfig = (fileConfig: OldFileConfig | NewFileConfig classifications, deployment, pipelineCrews, - } = fileConfig + } = fileConfig; return { projectName, dateRange: { startDate, endDate }, @@ -136,7 +136,7 @@ export const convertToNewFileConfig = (fileConfig: OldFileConfig | NewFileConfig step: item?.step, branches: item?.branches, })), - } + }; } - return fileConfig -} + return fileConfig; +}; diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index e51e22dad4..6fdab881ce 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -1,6 +1,6 @@ -import { useDispatch, useSelector } from 'react-redux' -import type { TypedUseSelectorHook } from 'react-redux' -import type { RootState, AppDispatch } from '@src/store' +import { useDispatch, useSelector } from 'react-redux'; +import type { TypedUseSelectorHook } from 'react-redux'; +import type { RootState, AppDispatch } from '@src/store'; -export const useAppDispatch: () => AppDispatch = useDispatch -export const useAppSelector: TypedUseSelectorHook = useSelector +export const useAppDispatch: () => AppDispatch = useDispatch; +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/frontend/src/hooks/reportMapper/changeFailureRate.ts b/frontend/src/hooks/reportMapper/changeFailureRate.ts index c45a93bf3c..165089da11 100644 --- a/frontend/src/hooks/reportMapper/changeFailureRate.ts +++ b/frontend/src/hooks/reportMapper/changeFailureRate.ts @@ -1,12 +1,12 @@ -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { ChangeFailureRateResponse } from '@src/clients/report/dto/response' -import { FAILURE_RATE_NAME } from '@src/constants/resources' +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { ChangeFailureRateResponse } from '@src/clients/report/dto/response'; +import { FAILURE_RATE_NAME } from '@src/constants/resources'; export const changeFailureRateMapper = ({ avgChangeFailureRate, changeFailureRateOfPipelines, }: ChangeFailureRateResponse) => { - const mappedChangeFailureRateValue: ReportDataWithThreeColumns[] = [] + const mappedChangeFailureRateValue: ReportDataWithThreeColumns[] = []; changeFailureRateOfPipelines.map((item, index) => { const deploymentFrequencyValue: ReportDataWithThreeColumns = { @@ -18,9 +18,9 @@ export const changeFailureRateMapper = ({ value: `${(item.failureRate * 100).toFixed(2)}%(${item.failedTimesOfPipeline}/${item.totalTimesOfPipeline})`, }, ], - } - mappedChangeFailureRateValue.push(deploymentFrequencyValue) - }) + }; + mappedChangeFailureRateValue.push(deploymentFrequencyValue); + }); mappedChangeFailureRateValue.push({ id: mappedChangeFailureRateValue.length, name: avgChangeFailureRate.name, @@ -32,7 +32,7 @@ export const changeFailureRateMapper = ({ })`, }, ], - }) + }); - return mappedChangeFailureRateValue -} + return mappedChangeFailureRateValue; +}; diff --git a/frontend/src/hooks/reportMapper/classification.ts b/frontend/src/hooks/reportMapper/classification.ts index 59da6155f4..4935f4aaa1 100644 --- a/frontend/src/hooks/reportMapper/classification.ts +++ b/frontend/src/hooks/reportMapper/classification.ts @@ -1,23 +1,23 @@ -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { ClassificationResponse } from '@src/clients/report/dto/response' +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { ClassificationResponse } from '@src/clients/report/dto/response'; export const classificationMapper = (classification: ClassificationResponse[]) => { - const mappedClassificationValue: ReportDataWithThreeColumns[] = [] + const mappedClassificationValue: ReportDataWithThreeColumns[] = []; classification.map((item, index) => { - const pairsValues: { name: string; value: string }[] = [] + const pairsValues: { name: string; value: string }[] = []; item.pairList.map((pairItem) => { - pairsValues.push({ name: pairItem.name, value: `${(pairItem.value * 100).toFixed(2)}%` }) - }) + pairsValues.push({ name: pairItem.name, value: `${(pairItem.value * 100).toFixed(2)}%` }); + }); const classificationValue: ReportDataWithThreeColumns = { id: index, name: item.fieldName, valuesList: pairsValues, - } - mappedClassificationValue.push(classificationValue) - }) + }; + mappedClassificationValue.push(classificationValue); + }); - return mappedClassificationValue -} + return mappedClassificationValue; +}; diff --git a/frontend/src/hooks/reportMapper/cycleTime.ts b/frontend/src/hooks/reportMapper/cycleTime.ts index f8d2586215..8dd4f3644e 100644 --- a/frontend/src/hooks/reportMapper/cycleTime.ts +++ b/frontend/src/hooks/reportMapper/cycleTime.ts @@ -1,6 +1,6 @@ -import { CYCLE_TIME_METRICS_NAME, METRICS_CONSTANTS, REPORT_SUFFIX_UNITS } from '@src/constants/resources' -import { ReportDataWithTwoColumns, ValueWithUnits } from '@src/hooks/reportMapper/reportUIDataStructure' -import { CycleTimeResponse, Swimlane } from '@src/clients/report/dto/response' +import { CYCLE_TIME_METRICS_NAME, METRICS_CONSTANTS, REPORT_SUFFIX_UNITS } from '@src/constants/resources'; +import { ReportDataWithTwoColumns, ValueWithUnits } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { CycleTimeResponse, Swimlane } from '@src/clients/report/dto/response'; export const cycleTimeMapper = ({ swimlaneList, @@ -8,17 +8,17 @@ export const cycleTimeMapper = ({ averageCycleTimePerSP, averageCycleTimePerCard, }: CycleTimeResponse) => { - const mappedCycleTimeValue: ReportDataWithTwoColumns[] = [] + const mappedCycleTimeValue: ReportDataWithTwoColumns[] = []; const getSwimlaneByItemName = (itemName: string) => { - return swimlaneList.find((item: Swimlane) => item.optionalItemName === itemName) - } + return swimlaneList.find((item: Swimlane) => item.optionalItemName === itemName); + }; const calPerColumnTotalTimeDivTotalTime = (itemName: string): ValueWithUnits[] => { - const swimlane = getSwimlaneByItemName(itemName) - return swimlane ? [{ value: `${parseFloat(((swimlane.totalTime / totalTimeForCards) * 100).toFixed(2))}%` }] : [] - } + const swimlane = getSwimlaneByItemName(itemName); + return swimlane ? [{ value: `${parseFloat(((swimlane.totalTime / totalTimeForCards) * 100).toFixed(2))}%` }] : []; + }; const getAverageTimeForPerColumn = (itemName: string) => { - const swimlane = getSwimlaneByItemName(itemName) + const swimlane = getSwimlaneByItemName(itemName); return swimlane ? [ { value: swimlane.averageTimeForSP.toFixed(2), unit: REPORT_SUFFIX_UNITS.PER_SP }, @@ -27,8 +27,8 @@ export const cycleTimeMapper = ({ unit: REPORT_SUFFIX_UNITS.PER_CARD, }, ] - : [] - } + : []; + }; const cycleTimeValue: { [key: string]: ValueWithUnits[] } = { AVERAGE_CYCLE_TIME: [ @@ -48,13 +48,13 @@ export const cycleTimeMapper = ({ AVERAGE_BLOCK_TIME: getAverageTimeForPerColumn(METRICS_CONSTANTS.blockValue), AVERAGE_REVIEW_TIME: getAverageTimeForPerColumn(METRICS_CONSTANTS.reviewValue), AVERAGE_TESTING_TIME: getAverageTimeForPerColumn(METRICS_CONSTANTS.testingValue), - } + }; Object.entries(CYCLE_TIME_METRICS_NAME).map(([key, cycleName]) => { if (cycleTimeValue[key].length > 0) { - mappedCycleTimeValue.push({ id: mappedCycleTimeValue.length, name: cycleName, valueList: cycleTimeValue[key] }) + mappedCycleTimeValue.push({ id: mappedCycleTimeValue.length, name: cycleName, valueList: cycleTimeValue[key] }); } - }) + }); - return mappedCycleTimeValue -} + return mappedCycleTimeValue; +}; diff --git a/frontend/src/hooks/reportMapper/deploymentFrequency.ts b/frontend/src/hooks/reportMapper/deploymentFrequency.ts index bf55bf74a1..df4689bd44 100644 --- a/frontend/src/hooks/reportMapper/deploymentFrequency.ts +++ b/frontend/src/hooks/reportMapper/deploymentFrequency.ts @@ -1,21 +1,21 @@ -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { DeploymentFrequencyResponse } from '@src/clients/report/dto/response' -import { DEPLOYMENT_FREQUENCY_NAME } from '@src/constants/resources' +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { DeploymentFrequencyResponse } from '@src/clients/report/dto/response'; +import { DEPLOYMENT_FREQUENCY_NAME } from '@src/constants/resources'; export const deploymentFrequencyMapper = ({ avgDeploymentFrequency, deploymentFrequencyOfPipelines, }: DeploymentFrequencyResponse) => { - const mappedDeploymentFrequencyValue: ReportDataWithThreeColumns[] = [] + const mappedDeploymentFrequencyValue: ReportDataWithThreeColumns[] = []; deploymentFrequencyOfPipelines.map((item, index) => { const deploymentFrequencyValue: ReportDataWithThreeColumns = { id: index, name: `${item.name}/${item.step}`, valuesList: [{ name: DEPLOYMENT_FREQUENCY_NAME, value: `${item.deploymentFrequency.toFixed(2)}` }], - } - mappedDeploymentFrequencyValue.push(deploymentFrequencyValue) - }) + }; + mappedDeploymentFrequencyValue.push(deploymentFrequencyValue); + }); mappedDeploymentFrequencyValue.push({ id: mappedDeploymentFrequencyValue.length, name: avgDeploymentFrequency.name, @@ -25,6 +25,6 @@ export const deploymentFrequencyMapper = ({ value: `${avgDeploymentFrequency.deploymentFrequency.toFixed(2)}`, }, ], - }) - return mappedDeploymentFrequencyValue -} + }); + return mappedDeploymentFrequencyValue; +}; diff --git a/frontend/src/hooks/reportMapper/exportValidityTime.ts b/frontend/src/hooks/reportMapper/exportValidityTime.ts index c24d500e4a..2440f055c8 100644 --- a/frontend/src/hooks/reportMapper/exportValidityTime.ts +++ b/frontend/src/hooks/reportMapper/exportValidityTime.ts @@ -1,8 +1,8 @@ -import dayjs from 'dayjs' -import duration from 'dayjs/plugin/duration' +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; export const exportValidityTimeMapper = (exportValidityTime: number | null) => { - dayjs.extend(duration) - const timestamp = exportValidityTime ? exportValidityTime : null - return timestamp ? dayjs.duration(timestamp).asMinutes() : null -} + dayjs.extend(duration); + const timestamp = exportValidityTime ? exportValidityTime : null; + return timestamp ? dayjs.duration(timestamp).asMinutes() : null; +}; diff --git a/frontend/src/hooks/reportMapper/leadTimeForChanges.ts b/frontend/src/hooks/reportMapper/leadTimeForChanges.ts index 55b0870018..28f81de85c 100644 --- a/frontend/src/hooks/reportMapper/leadTimeForChanges.ts +++ b/frontend/src/hooks/reportMapper/leadTimeForChanges.ts @@ -1,18 +1,18 @@ -import { LeadTimeForChangesResponse } from '@src/clients/report/dto/response' +import { LeadTimeForChangesResponse } from '@src/clients/report/dto/response'; export const leadTimeForChangesMapper = ({ leadTimeForChangesOfPipelines, avgLeadTimeForChanges, }: LeadTimeForChangesResponse) => { - const minutesPerHour = 60 + const minutesPerHour = 60; const formatDuration = (duration: number) => { - return (duration / minutesPerHour).toFixed(2) - } + return (duration / minutesPerHour).toFixed(2); + }; const formatNameDisplay = (name: string) => { - if (name == 'pipelineLeadTime') return 'Pipeline Lead Time' - if (name == 'prLeadTime') return 'PR Lead Time' - if (name == 'totalDelayTime') return 'Total Lead Time' - } + if (name == 'pipelineLeadTime') return 'Pipeline Lead Time'; + if (name == 'prLeadTime') return 'PR Lead Time'; + if (name == 'totalDelayTime') return 'Total Lead Time'; + }; const mappedLeadTimeForChangesValue = leadTimeForChangesOfPipelines.map((item, index) => { return { @@ -24,8 +24,8 @@ export const leadTimeForChangesMapper = ({ name: formatNameDisplay(name) as string, value: formatDuration(value), })), - } - }) + }; + }); mappedLeadTimeForChangesValue.push({ id: mappedLeadTimeForChangesValue.length, @@ -36,7 +36,7 @@ export const leadTimeForChangesMapper = ({ name: formatNameDisplay(name) as string, value: formatDuration(value), })), - }) + }); - return mappedLeadTimeForChangesValue -} + return mappedLeadTimeForChangesValue; +}; diff --git a/frontend/src/hooks/reportMapper/meanTimeToRecovery.ts b/frontend/src/hooks/reportMapper/meanTimeToRecovery.ts index 1e35046ed0..08f7fa0a71 100644 --- a/frontend/src/hooks/reportMapper/meanTimeToRecovery.ts +++ b/frontend/src/hooks/reportMapper/meanTimeToRecovery.ts @@ -1,19 +1,19 @@ -import { MeanTimeToRecoveryResponse } from '@src/clients/report/dto/response' -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { MEAN_TIME_TO_RECOVERY_NAME } from '@src/constants/resources' +import { MeanTimeToRecoveryResponse } from '@src/clients/report/dto/response'; +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { MEAN_TIME_TO_RECOVERY_NAME } from '@src/constants/resources'; export const meanTimeToRecoveryMapper = ({ avgMeanTimeToRecovery, meanTimeRecoveryPipelines, }: MeanTimeToRecoveryResponse) => { - const minutesPerHour = 60 - const milliscondMinute = 60000 + const minutesPerHour = 60; + const milliscondMinute = 60000; const formatDuration = (duration: number) => { - const minutesDuration = duration / milliscondMinute - return (minutesDuration / minutesPerHour).toFixed(2) - } + const minutesDuration = duration / milliscondMinute; + return (minutesDuration / minutesPerHour).toFixed(2); + }; - const mappedMeanTimeToRecoveryValue: ReportDataWithThreeColumns[] = [] + const mappedMeanTimeToRecoveryValue: ReportDataWithThreeColumns[] = []; meanTimeRecoveryPipelines.map((item, index) => { const meanTimeToRecoveryValue: ReportDataWithThreeColumns = { @@ -25,9 +25,9 @@ export const meanTimeToRecoveryMapper = ({ value: formatDuration(item.timeToRecovery), }, ], - } - mappedMeanTimeToRecoveryValue.push(meanTimeToRecoveryValue) - }) + }; + mappedMeanTimeToRecoveryValue.push(meanTimeToRecoveryValue); + }); mappedMeanTimeToRecoveryValue.push({ id: mappedMeanTimeToRecoveryValue.length, name: avgMeanTimeToRecovery.name, @@ -37,7 +37,7 @@ export const meanTimeToRecoveryMapper = ({ value: formatDuration(avgMeanTimeToRecovery.timeToRecovery), }, ], - }) + }); - return mappedMeanTimeToRecoveryValue -} + return mappedMeanTimeToRecoveryValue; +}; diff --git a/frontend/src/hooks/reportMapper/report.ts b/frontend/src/hooks/reportMapper/report.ts index 3fe04dfeb9..348c3b0bff 100644 --- a/frontend/src/hooks/reportMapper/report.ts +++ b/frontend/src/hooks/reportMapper/report.ts @@ -1,12 +1,12 @@ -import { ReportResponse, ReportResponseDTO } from '@src/clients/report/dto/response' -import { changeFailureRateMapper } from '@src/hooks/reportMapper/changeFailureRate' -import { velocityMapper } from '@src/hooks/reportMapper/velocity' -import { cycleTimeMapper } from '@src/hooks/reportMapper/cycleTime' -import { classificationMapper } from '@src/hooks/reportMapper/classification' -import { deploymentFrequencyMapper } from '@src/hooks/reportMapper/deploymentFrequency' -import { leadTimeForChangesMapper } from '@src/hooks/reportMapper/leadTimeForChanges' -import { meanTimeToRecoveryMapper } from '@src/hooks/reportMapper/meanTimeToRecovery' -import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' +import { ReportResponse, ReportResponseDTO } from '@src/clients/report/dto/response'; +import { changeFailureRateMapper } from '@src/hooks/reportMapper/changeFailureRate'; +import { velocityMapper } from '@src/hooks/reportMapper/velocity'; +import { cycleTimeMapper } from '@src/hooks/reportMapper/cycleTime'; +import { classificationMapper } from '@src/hooks/reportMapper/classification'; +import { deploymentFrequencyMapper } from '@src/hooks/reportMapper/deploymentFrequency'; +import { leadTimeForChangesMapper } from '@src/hooks/reportMapper/leadTimeForChanges'; +import { meanTimeToRecoveryMapper } from '@src/hooks/reportMapper/meanTimeToRecovery'; +import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime'; export const reportMapper = ({ velocity, @@ -18,21 +18,21 @@ export const reportMapper = ({ changeFailureRate, exportValidityTime, }: ReportResponseDTO): ReportResponse => { - const velocityList = velocity && velocityMapper(velocity) + const velocityList = velocity && velocityMapper(velocity); - const cycleTimeList = cycleTime && cycleTimeMapper(cycleTime) + const cycleTimeList = cycleTime && cycleTimeMapper(cycleTime); - const classification = classificationList && classificationMapper(classificationList) + const classification = classificationList && classificationMapper(classificationList); - const deploymentFrequencyList = deploymentFrequency && deploymentFrequencyMapper(deploymentFrequency) + const deploymentFrequencyList = deploymentFrequency && deploymentFrequencyMapper(deploymentFrequency); - const meanTimeToRecoveryList = meanTimeToRecovery && meanTimeToRecoveryMapper(meanTimeToRecovery) + const meanTimeToRecoveryList = meanTimeToRecovery && meanTimeToRecoveryMapper(meanTimeToRecovery); - const leadTimeForChangesList = leadTimeForChanges && leadTimeForChangesMapper(leadTimeForChanges) + const leadTimeForChangesList = leadTimeForChanges && leadTimeForChangesMapper(leadTimeForChanges); - const changeFailureRateList = changeFailureRate && changeFailureRateMapper(changeFailureRate) + const changeFailureRateList = changeFailureRate && changeFailureRateMapper(changeFailureRate); - const exportValidityTimeMin = exportValidityTimeMapper(exportValidityTime) + const exportValidityTimeMin = exportValidityTimeMapper(exportValidityTime); return { velocityList, @@ -43,5 +43,5 @@ export const reportMapper = ({ leadTimeForChangesList, changeFailureRateList, exportValidityTimeMin, - } -} + }; +}; diff --git a/frontend/src/hooks/reportMapper/reportUIDataStructure.ts b/frontend/src/hooks/reportMapper/reportUIDataStructure.ts index a08f1092eb..d9add8c71d 100644 --- a/frontend/src/hooks/reportMapper/reportUIDataStructure.ts +++ b/frontend/src/hooks/reportMapper/reportUIDataStructure.ts @@ -1,19 +1,19 @@ export interface ReportDataWithTwoColumns { - id: number - name: string - valueList: ValueWithUnits[] + id: number; + name: string; + valueList: ValueWithUnits[]; } export interface ValueWithUnits { - value: number | string - unit?: string + value: number | string; + unit?: string; } export interface ReportDataWithThreeColumns { - id: number - name: string + id: number; + name: string; valuesList: { - name: string - value: string - }[] + name: string; + value: string; + }[]; } diff --git a/frontend/src/hooks/reportMapper/velocity.ts b/frontend/src/hooks/reportMapper/velocity.ts index df4904356f..d4120fdf5d 100644 --- a/frontend/src/hooks/reportMapper/velocity.ts +++ b/frontend/src/hooks/reportMapper/velocity.ts @@ -1,22 +1,22 @@ -import { VELOCITY_METRICS_NAME } from '@src/constants/resources' -import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { VelocityResponse } from '@src/clients/report/dto/response' +import { VELOCITY_METRICS_NAME } from '@src/constants/resources'; +import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { VelocityResponse } from '@src/clients/report/dto/response'; export const velocityMapper = ({ velocityForSP, velocityForCards }: VelocityResponse) => { - const mappedVelocityValue: ReportDataWithTwoColumns[] = [] + const mappedVelocityValue: ReportDataWithTwoColumns[] = []; const velocityValue: { [key: string]: number } = { VELOCITY_SP: velocityForSP, THROUGHPUT_CARDS_COUNT: velocityForCards, - } + }; Object.entries(VELOCITY_METRICS_NAME).map(([key, velocityName], index) => { mappedVelocityValue.push({ id: index, name: velocityName, valueList: [{ value: velocityValue[key] }], - }) - }) + }); + }); - return mappedVelocityValue -} + return mappedVelocityValue; +}; diff --git a/frontend/src/hooks/useAppDispatch.ts b/frontend/src/hooks/useAppDispatch.ts index e51e22dad4..6fdab881ce 100644 --- a/frontend/src/hooks/useAppDispatch.ts +++ b/frontend/src/hooks/useAppDispatch.ts @@ -1,6 +1,6 @@ -import { useDispatch, useSelector } from 'react-redux' -import type { TypedUseSelectorHook } from 'react-redux' -import type { RootState, AppDispatch } from '@src/store' +import { useDispatch, useSelector } from 'react-redux'; +import type { TypedUseSelectorHook } from 'react-redux'; +import type { RootState, AppDispatch } from '@src/store'; -export const useAppDispatch: () => AppDispatch = useDispatch -export const useAppSelector: TypedUseSelectorHook = useSelector +export const useAppDispatch: () => AppDispatch = useDispatch; +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/frontend/src/hooks/useExportCsvEffect.ts b/frontend/src/hooks/useExportCsvEffect.ts index db6dbb989f..5e13605259 100644 --- a/frontend/src/hooks/useExportCsvEffect.ts +++ b/frontend/src/hooks/useExportCsvEffect.ts @@ -1,35 +1,35 @@ -import { useState } from 'react' -import { CSVReportRequestDTO } from '@src/clients/report/dto/request' -import { csvClient } from '@src/clients/report/CSVClient' -import { NotFoundException } from '@src/exceptions/NotFoundException' -import { DURATION } from '@src/constants/commons' +import { useState } from 'react'; +import { CSVReportRequestDTO } from '@src/clients/report/dto/request'; +import { csvClient } from '@src/clients/report/CSVClient'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { DURATION } from '@src/constants/commons'; export interface useExportCsvEffectInterface { - fetchExportData: (params: CSVReportRequestDTO) => void - errorMessage: string - isExpired: boolean + fetchExportData: (params: CSVReportRequestDTO) => void; + errorMessage: string; + isExpired: boolean; } export const useExportCsvEffect = (): useExportCsvEffectInterface => { - const [errorMessage, setErrorMessage] = useState('') - const [isExpired, setIsExpired] = useState(false) + const [errorMessage, setErrorMessage] = useState(''); + const [isExpired, setIsExpired] = useState(false); const fetchExportData = async (params: CSVReportRequestDTO) => { try { - setIsExpired(false) - return await csvClient.exportCSVData(params) + setIsExpired(false); + return await csvClient.exportCSVData(params); } catch (e) { - const err = e as Error + const err = e as Error; if (err instanceof NotFoundException) { - setIsExpired(true) + setIsExpired(true); } else { - setErrorMessage(`failed to export csv: ${err.message}`) + setErrorMessage(`failed to export csv: ${err.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } } - } + }; - return { fetchExportData, errorMessage, isExpired } -} + return { fetchExportData, errorMessage, isExpired }; +}; diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index f117446e23..63bd6252d8 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -1,19 +1,19 @@ -import { useRef, useState } from 'react' -import { reportClient } from '@src/clients/report/ReportClient' -import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' -import { UnknownException } from '@src/exceptions/UnkonwException' -import { InternalServerException } from '@src/exceptions/InternalServerException' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons' -import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime' +import { useRef, useState } from 'react'; +import { reportClient } from '@src/clients/report/ReportClient'; +import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; +import { UnknownException } from '@src/exceptions/UnkonwException'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { DURATION, RETRIEVE_REPORT_TYPES } from '@src/constants/commons'; +import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime'; export interface useGenerateReportEffectInterface { - startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void - startToRequestDoraData: (doraParams: ReportRequestDTO) => void - stopPollingReports: () => void - isServerError: boolean - errorMessage: string - reportData: ReportResponseDTO | undefined + startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void; + startToRequestDoraData: (doraParams: ReportRequestDTO) => void; + stopPollingReports: () => void; + isServerError: boolean; + errorMessage: string; + reportData: ReportResponseDTO | undefined; } export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { @@ -28,68 +28,68 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { reportClient .retrieveReportByUrl(boardParams, `${reportPath}/${RETRIEVE_REPORT_TYPES.BOARD}`) .then((res) => { - if (hasPollingStarted) return - hasPollingStarted = true - pollingReport(res.response.callbackUrl, res.response.interval) + if (hasPollingStarted) return; + hasPollingStarted = true; + pollingReport(res.response.callbackUrl, res.response.interval); }) .catch((e) => { - handleError(e) - stopPollingReports() - }) - } + handleError(e); + stopPollingReports(); + }); + }; const handleError = (error: Error) => { if (error instanceof InternalServerException || error instanceof UnknownException) { - setIsServerError(true) + setIsServerError(true); } else { - setErrorMessage(`generate report: ${error.message}`) + setErrorMessage(`generate report: ${error.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } - } + }; const startToRequestDoraData = (doraParams: ReportRequestDTO) => { reportClient .retrieveReportByUrl(doraParams, `${reportPath}/${RETRIEVE_REPORT_TYPES.DORA}`) .then((res) => { - if (hasPollingStarted) return - hasPollingStarted = true - pollingReport(res.response.callbackUrl, res.response.interval) + if (hasPollingStarted) return; + hasPollingStarted = true; + pollingReport(res.response.callbackUrl, res.response.interval); }) .catch((e) => { - handleError(e) - stopPollingReports() - }) - } + handleError(e); + stopPollingReports(); + }); + }; const pollingReport = (url: string, interval: number) => { reportClient .pollingReport(url) .then((res: { status: number; response: ReportResponseDTO }) => { - const response = res.response - handleAndUpdateData(response) + const response = res.response; + handleAndUpdateData(response); if (response.isAllMetricsReady) { - stopPollingReports() + stopPollingReports(); } else { - timerIdRef.current = window.setTimeout(() => pollingReport(url, interval), interval * 1000) + timerIdRef.current = window.setTimeout(() => pollingReport(url, interval), interval * 1000); } }) .catch((e) => { - handleError(e) - stopPollingReports() - }) - } + handleError(e); + stopPollingReports(); + }); + }; const stopPollingReports = () => { - window.clearTimeout(timerIdRef.current) - hasPollingStarted = false - } + window.clearTimeout(timerIdRef.current); + hasPollingStarted = false; + }; const handleAndUpdateData = (response: ReportResponseDTO) => { - const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime) - setReportData({ ...response, exportValidityTime: exportValidityTime }) - } + const exportValidityTime = exportValidityTimeMapper(response.exportValidityTime); + setReportData({ ...response, exportValidityTime: exportValidityTime }); + }; return { startToRequestBoardData, @@ -98,5 +98,5 @@ export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { reportData, isServerError, errorMessage, - } -} + }; +}; diff --git a/frontend/src/hooks/useGetMetricsStepsEffect.ts b/frontend/src/hooks/useGetMetricsStepsEffect.ts index 739ce1447e..cf504681c2 100644 --- a/frontend/src/hooks/useGetMetricsStepsEffect.ts +++ b/frontend/src/hooks/useGetMetricsStepsEffect.ts @@ -1,7 +1,7 @@ -import { useState } from 'react' -import { getStepsParams, metricsClient } from '@src/clients/MetricsClient' -import { DURATION } from '@src/constants/commons' -import { MESSAGE } from '@src/constants/resources' +import { useState } from 'react'; +import { getStepsParams, metricsClient } from '@src/clients/MetricsClient'; +import { DURATION } from '@src/constants/commons'; +import { MESSAGE } from '@src/constants/resources'; export interface useGetMetricsStepsEffectInterface { getSteps: ( @@ -12,20 +12,20 @@ export interface useGetMetricsStepsEffectInterface { token: string ) => Promise< | { - haveStep: boolean - response: string[] - branches: string[] - pipelineCrews: string[] + haveStep: boolean; + response: string[]; + branches: string[]; + pipelineCrews: string[]; } | undefined - > - isLoading: boolean - errorMessage: string + >; + isLoading: boolean; + errorMessage: string; } export const useGetMetricsStepsEffect = (): useGetMetricsStepsEffectInterface => { - const [isLoading, setIsLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState('') + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); const getSteps = async ( params: getStepsParams, @@ -34,19 +34,19 @@ export const useGetMetricsStepsEffect = (): useGetMetricsStepsEffectInterface => pipelineType: string, token: string ) => { - setIsLoading(true) + setIsLoading(true); try { - return await metricsClient.getSteps(params, organizationId, buildId, pipelineType, token) + return await metricsClient.getSteps(params, organizationId, buildId, pipelineType, token); } catch (e) { - const err = e as Error - setErrorMessage(`${pipelineType} ${MESSAGE.GET_STEPS_FAILED}: ${err.message}`) + const err = e as Error; + setErrorMessage(`${pipelineType} ${MESSAGE.GET_STEPS_FAILED}: ${err.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; - return { isLoading, getSteps, errorMessage } -} + return { isLoading, getSteps, errorMessage }; +}; diff --git a/frontend/src/hooks/useMetricsStepValidationCheckContext.tsx b/frontend/src/hooks/useMetricsStepValidationCheckContext.tsx index 8cb08fc7ee..4d05a4acb7 100644 --- a/frontend/src/hooks/useMetricsStepValidationCheckContext.tsx +++ b/frontend/src/hooks/useMetricsStepValidationCheckContext.tsx @@ -1,41 +1,41 @@ -import React, { createContext, useContext } from 'react' -import { useAppSelector } from '@src/hooks/index' -import { selectDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice' -import { PipelineSetting } from '@src/context/interface' +import React, { createContext, useContext } from 'react'; +import { useAppSelector } from '@src/hooks/index'; +import { selectDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice'; +import { PipelineSetting } from '@src/context/interface'; interface ProviderContextType { - isPipelineValid: (type: string) => boolean - getDuplicatedPipeLineIds: (pipelineSettings: PipelineSetting[]) => number[] + isPipelineValid: (type: string) => boolean; + getDuplicatedPipeLineIds: (pipelineSettings: PipelineSetting[]) => number[]; } interface ContextProviderProps { - children: React.ReactNode + children: React.ReactNode; } export const ValidationContext = createContext({ isPipelineValid: () => false, getDuplicatedPipeLineIds: () => [], -}) +}); const assignErrorMessage = (label: string, value: string, id: number, duplicatedPipeLineIds: number[]) => - !value ? `${label} is required` : duplicatedPipeLineIds.includes(id) ? `duplicated ${label}` : '' + !value ? `${label} is required` : duplicatedPipeLineIds.includes(id) ? `duplicated ${label}` : ''; const getDuplicatedPipeLineIds = (pipelineSettings: PipelineSetting[]) => { - const errors: { [key: string]: number[] } = {} + const errors: { [key: string]: number[] } = {}; pipelineSettings.forEach(({ id, organization, pipelineName, step }) => { if (organization && pipelineName && step) { - const errorString = `${organization}${pipelineName}${step}` - if (errors[errorString]) errors[errorString].push(id) - else errors[errorString] = [id] + const errorString = `${organization}${pipelineName}${step}`; + if (errors[errorString]) errors[errorString].push(id); + else errors[errorString] = [id]; } - }) + }); return Object.values(errors) .filter((ids) => ids.length > 1) - .flat() -} + .flat(); +}; const getErrorMessages = (pipelineSettings: PipelineSetting[]) => { - const duplicatedPipelineIds: number[] = getDuplicatedPipeLineIds(pipelineSettings) + const duplicatedPipelineIds: number[] = getDuplicatedPipeLineIds(pipelineSettings); return pipelineSettings.map(({ id, organization, pipelineName, step }) => ({ id, error: { @@ -43,17 +43,17 @@ const getErrorMessages = (pipelineSettings: PipelineSetting[]) => { pipelineName: assignErrorMessage('pipelineName', pipelineName, id, duplicatedPipelineIds), step: assignErrorMessage('step', step, id, duplicatedPipelineIds), }, - })) -} + })); +}; export const ContextProvider = ({ children }: ContextProviderProps) => { - const deploymentFrequencySettings = useAppSelector(selectDeploymentFrequencySettings) + const deploymentFrequencySettings = useAppSelector(selectDeploymentFrequencySettings); - const isPipelineValid = (type: string) => { - const pipelines = deploymentFrequencySettings - const errorMessages = getErrorMessages(pipelines) - return errorMessages.every(({ error }) => Object.values(error).every((val) => !val)) - } + const isPipelineValid = () => { + const pipelines = deploymentFrequencySettings; + const errorMessages = getErrorMessages(pipelines); + return errorMessages.every(({ error }) => Object.values(error).every((val) => !val)); + }; return ( { > {children} - ) -} + ); +}; -export const useMetricsStepValidationCheckContext = () => useContext(ValidationContext) +export const useMetricsStepValidationCheckContext = () => useContext(ValidationContext); diff --git a/frontend/src/hooks/useNotificationLayoutEffect.ts b/frontend/src/hooks/useNotificationLayoutEffect.ts index eac4d7d510..e8013ef1d8 100644 --- a/frontend/src/hooks/useNotificationLayoutEffect.ts +++ b/frontend/src/hooks/useNotificationLayoutEffect.ts @@ -1,17 +1,17 @@ -import { useEffect, useState } from 'react' -import { DURATION } from '@src/constants/commons' +import { useEffect, useState } from 'react'; +import { DURATION } from '@src/constants/commons'; export interface NotificationTipProps { - title: string - open: boolean - closeAutomatically: boolean - durationTimeout?: number + title: string; + open: boolean; + closeAutomatically: boolean; + durationTimeout?: number; } export interface useNotificationLayoutEffectInterface { - notificationProps?: NotificationTipProps - resetProps?: () => void - updateProps?: (notificationProps: NotificationTipProps) => void + notificationProps?: NotificationTipProps; + resetProps?: () => void; + updateProps?: (notificationProps: NotificationTipProps) => void; } export const useNotificationLayoutEffect = (): useNotificationLayoutEffectInterface => { @@ -20,7 +20,7 @@ export const useNotificationLayoutEffect = (): useNotificationLayoutEffectInterf title: '', closeAutomatically: false, durationTimeout: DURATION.NOTIFICATION_TIME, - }) + }); const resetProps = () => { setNotificationProps(() => ({ @@ -28,25 +28,25 @@ export const useNotificationLayoutEffect = (): useNotificationLayoutEffectInterf title: '', closeAutomatically: false, durationTimeout: DURATION.NOTIFICATION_TIME, - })) - } + })); + }; const updateProps = (notificationProps: NotificationTipProps) => { - setNotificationProps(notificationProps) - } + setNotificationProps(notificationProps); + }; const closeAutomatically = () => { const durationTimeout = notificationProps.durationTimeout ? notificationProps.durationTimeout - : DURATION.NOTIFICATION_TIME + : DURATION.NOTIFICATION_TIME; window.setTimeout(() => { - resetProps() - }, durationTimeout) - } + resetProps(); + }, durationTimeout); + }; useEffect(() => { - notificationProps?.closeAutomatically && closeAutomatically() - }, [notificationProps]) + notificationProps?.closeAutomatically && closeAutomatically(); + }, [notificationProps]); - return { notificationProps, resetProps, updateProps } -} + return { notificationProps, resetProps, updateProps }; +}; diff --git a/frontend/src/hooks/useVerifyBoardEffect.ts b/frontend/src/hooks/useVerifyBoardEffect.ts index b5c3d06df5..9889874932 100644 --- a/frontend/src/hooks/useVerifyBoardEffect.ts +++ b/frontend/src/hooks/useVerifyBoardEffect.ts @@ -1,44 +1,44 @@ -import { useState } from 'react' -import { boardClient } from '@src/clients/board/BoardClient' -import { MESSAGE } from '@src/constants/resources' -import { BoardRequestDTO } from '@src/clients/board/dto/request' -import { DURATION } from '@src/constants/commons' +import { useState } from 'react'; +import { boardClient } from '@src/clients/board/BoardClient'; +import { MESSAGE } from '@src/constants/resources'; +import { BoardRequestDTO } from '@src/clients/board/dto/request'; +import { DURATION } from '@src/constants/commons'; export interface useVerifyBoardStateInterface { verifyJira: (params: BoardRequestDTO) => Promise< | { - isBoardVerify: boolean - haveDoneCard: boolean - response: object + isBoardVerify: boolean; + haveDoneCard: boolean; + response: object; } | undefined - > - isLoading: boolean - errorMessage: string + >; + isLoading: boolean; + errorMessage: string; } export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { - const [isLoading, setIsLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState('') + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); const verifyJira = async (params: BoardRequestDTO) => { - setIsLoading(true) + setIsLoading(true); try { - return await boardClient.getVerifyBoard(params) + return await boardClient.getVerifyBoard(params); } catch (e) { - const err = e as Error - setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`) + const err = e as Error; + setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; return { verifyJira, isLoading, errorMessage, - } -} + }; +}; diff --git a/frontend/src/hooks/useVerifyPipelineToolEffect.ts b/frontend/src/hooks/useVerifyPipelineToolEffect.ts index b42ab1a6a3..7478681980 100644 --- a/frontend/src/hooks/useVerifyPipelineToolEffect.ts +++ b/frontend/src/hooks/useVerifyPipelineToolEffect.ts @@ -1,43 +1,43 @@ -import { useState } from 'react' -import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient' -import { MESSAGE } from '@src/constants/resources' -import { PipelineRequestDTO } from '@src/clients/pipeline/dto/request' -import { DURATION } from '@src/constants/commons' +import { useState } from 'react'; +import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient'; +import { MESSAGE } from '@src/constants/resources'; +import { PipelineRequestDTO } from '@src/clients/pipeline/dto/request'; +import { DURATION } from '@src/constants/commons'; export interface useVerifyPipeLineToolStateInterface { verifyPipelineTool: (params: PipelineRequestDTO) => Promise< | { - isPipelineToolVerified: boolean - response: object + isPipelineToolVerified: boolean; + response: object; } | undefined - > - isLoading: boolean - errorMessage: string + >; + isLoading: boolean; + errorMessage: string; } export const useVerifyPipelineToolEffect = (): useVerifyPipeLineToolStateInterface => { - const [isLoading, setIsLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState('') + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); const verifyPipelineTool = async (params: PipelineRequestDTO) => { - setIsLoading(true) + setIsLoading(true); try { - return await pipelineToolClient.verifyPipelineTool(params) + return await pipelineToolClient.verifyPipelineTool(params); } catch (e) { - const err = e as Error - setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`) + const err = e as Error; + setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; return { verifyPipelineTool, isLoading, errorMessage, - } -} + }; +}; diff --git a/frontend/src/hooks/useVeritySourceControlEffect.ts b/frontend/src/hooks/useVeritySourceControlEffect.ts index a9797f7615..6431bd34a4 100644 --- a/frontend/src/hooks/useVeritySourceControlEffect.ts +++ b/frontend/src/hooks/useVeritySourceControlEffect.ts @@ -1,43 +1,43 @@ -import { useState } from 'react' -import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient' -import { MESSAGE } from '@src/constants/resources' -import { SourceControlRequestDTO } from '@src/clients/sourceControl/dto/request' -import { DURATION } from '@src/constants/commons' +import { useState } from 'react'; +import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; +import { MESSAGE } from '@src/constants/resources'; +import { SourceControlRequestDTO } from '@src/clients/sourceControl/dto/request'; +import { DURATION } from '@src/constants/commons'; export interface useVerifySourceControlStateInterface { verifyGithub: (params: SourceControlRequestDTO) => Promise< | { - isSourceControlVerify: boolean - response: object + isSourceControlVerify: boolean; + response: object; } | undefined - > - isLoading: boolean - errorMessage: string + >; + isLoading: boolean; + errorMessage: string; } export const useVerifySourceControlEffect = (): useVerifySourceControlStateInterface => { - const [isLoading, setIsLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState('') + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); const verifyGithub = async (params: SourceControlRequestDTO) => { - setIsLoading(true) + setIsLoading(true); try { - return await sourceControlClient.getVerifySourceControl(params) + return await sourceControlClient.getVerifySourceControl(params); } catch (e) { - const err = e as Error - setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`) + const err = e as Error; + setErrorMessage(`${params.type} ${MESSAGE.VERIFY_FAILED_ERROR}: ${err.message}`); setTimeout(() => { - setErrorMessage('') - }, DURATION.ERROR_MESSAGE_TIME) + setErrorMessage(''); + }, DURATION.ERROR_MESSAGE_TIME); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; return { verifyGithub, isLoading, errorMessage, - } -} + }; +}; diff --git a/frontend/src/layouts/Header.tsx b/frontend/src/layouts/Header.tsx index a2e9299f50..457bb830b8 100644 --- a/frontend/src/layouts/Header.tsx +++ b/frontend/src/layouts/Header.tsx @@ -1,7 +1,7 @@ -import { useLocation, useNavigate } from 'react-router-dom' -import Logo from '@src/assets/Logo.svg' +import { useLocation, useNavigate } from 'react-router-dom'; +import Logo from '@src/assets/Logo.svg'; -import { PROJECT_NAME } from '@src/constants/commons' +import { PROJECT_NAME } from '@src/constants/commons'; import { HomeIconContainer, HomeIconElement, @@ -13,43 +13,43 @@ import { NotificationIconContainer, StyledHeaderInfo, StyledVersion, -} from '@src/layouts/style' -import { NotificationButton } from '@src/components/Common/NotificationButton' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -import { useEffect } from 'react' -import { headerClient } from '@src/clients/header/HeaderClient' -import { useAppDispatch } from '@src/hooks/useAppDispatch' -import { getVersion, saveVersion } from '@src/context/header/headerSlice' -import { useAppSelector } from '@src/hooks' -import { isEmpty } from 'lodash' -import { resetImportedData } from '@src/context/config/configSlice' +} from '@src/layouts/style'; +import { NotificationButton } from '@src/components/Common/NotificationButton'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; +import { useEffect } from 'react'; +import { headerClient } from '@src/clients/header/HeaderClient'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { getVersion, saveVersion } from '@src/context/header/headerSlice'; +import { useAppSelector } from '@src/hooks'; +import { isEmpty } from 'lodash'; +import { resetImportedData } from '@src/context/config/configSlice'; const Header = (props: useNotificationLayoutEffectInterface) => { - const location = useLocation() - const navigate = useNavigate() - const dispatch = useAppDispatch() - const version = useAppSelector(getVersion) + const location = useLocation(); + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const version = useAppSelector(getVersion); const goHome = () => { - dispatch(resetImportedData()) - navigate('/') - } + dispatch(resetImportedData()); + navigate('/'); + }; const shouldShowHomeIcon = () => { - return ['/metrics', '/error-page'].includes(location.pathname) - } + return ['/metrics', '/error-page'].includes(location.pathname); + }; const shouldShowNotificationIcon = () => { - return ['/metrics'].includes(location.pathname) - } + return ['/metrics'].includes(location.pathname); + }; useEffect(() => { if (isEmpty(version)) { headerClient.getVersion().then((res) => { - dispatch(saveVersion(res)) - }) + dispatch(saveVersion(res)); + }); } - }, []) + }, []); return ( @@ -73,6 +73,6 @@ const Header = (props: useNotificationLayoutEffectInterface) => { )} - ) -} -export default Header + ); +}; +export default Header; diff --git a/frontend/src/layouts/style.tsx b/frontend/src/layouts/style.tsx index e052116de8..f2b94a23b6 100644 --- a/frontend/src/layouts/style.tsx +++ b/frontend/src/layouts/style.tsx @@ -1,7 +1,7 @@ -import styled from '@emotion/styled' -import { theme } from '@src/theme' -import HomeIcon from '@mui/icons-material/Home' -import { Z_INDEX } from '@src/constants/commons' +import styled from '@emotion/styled'; +import { theme } from '@src/theme'; +import HomeIcon from '@mui/icons-material/Home'; +import { Z_INDEX } from '@src/constants/commons'; export const LogoWarp = styled.div({ display: 'flex', @@ -13,18 +13,18 @@ export const LogoWarp = styled.div({ zIndex: Z_INDEX.STICKY, position: 'sticky', top: 0, -}) +}); export const LogoTitle = styled.span({ color: theme.main.color, fontWeight: 'bold', fontSize: '1.5rem', -}) +}); export const LogoImage = styled.img({ height: '4rem', width: '4rem', -}) +}); export const LogoContainer = styled.div({ display: 'flex', @@ -32,39 +32,39 @@ export const LogoContainer = styled.div({ cursor: 'pointer', color: theme.main.color, marginRight: '1rem', -}) +}); export const StyledHeaderInfo = styled.div({ display: 'flex', alignItems: 'center', color: theme.main.color, -}) +}); export const StyledVersion = styled.div({ color: '#ddd', fontSize: '0.8rem', paddingTop: '0.5rem', -}) +}); export const IconContainer = styled.div({ display: 'inherit', color: theme.main.color, -}) +}); export const HomeIconContainer = styled.span` cursor: pointer; -` +`; export const HomeIconElement = styled(HomeIcon)` color: ${theme.main.color}; -` +`; export const NotificationIconContainer = styled.span({ left: 0, -}) +}); export const ellipsisProps: { [key: string]: string | number } = { textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', -} +}; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 406dc51a4b..13943a8814 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App' -import { Provider } from 'react-redux' -import { store } from './store' -import { ThemeProvider } from '@mui/material' -import { theme } from '@src/theme' +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import { Provider } from 'react-redux'; +import { store } from './store'; +import { ThemeProvider } from '@mui/material'; +import { theme } from '@src/theme'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( @@ -14,4 +14,4 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( -) +); diff --git a/frontend/src/pages/ErrorPage.tsx b/frontend/src/pages/ErrorPage.tsx index 1f3f99eabd..7104cc35f3 100644 --- a/frontend/src/pages/ErrorPage.tsx +++ b/frontend/src/pages/ErrorPage.tsx @@ -1,6 +1,6 @@ -import React from 'react' -import Header from '@src/layouts/Header' -import { ErrorContent } from '@src/components/ErrorContent' +import React from 'react'; +import Header from '@src/layouts/Header'; +import { ErrorContent } from '@src/components/ErrorContent'; const ErrorPage = () => { return ( @@ -8,6 +8,6 @@ const ErrorPage = () => {
    - ) -} -export default ErrorPage + ); +}; +export default ErrorPage; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index c4520a3941..30aaf478d5 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,7 +1,7 @@ -import React from 'react' -import Header from '@src/layouts/Header' -import { ProjectDescription } from '@src/components/ProjectDescription' -import { HomeGuide } from '@src/components/HomeGuide' +import React from 'react'; +import Header from '@src/layouts/Header'; +import { ProjectDescription } from '@src/components/ProjectDescription'; +import { HomeGuide } from '@src/components/HomeGuide'; const Home = () => { return ( @@ -10,6 +10,6 @@ const Home = () => { - ) -} -export default Home + ); +}; +export default Home; diff --git a/frontend/src/pages/Metrics.tsx b/frontend/src/pages/Metrics.tsx index 202e239c47..c8a123c1bb 100644 --- a/frontend/src/pages/Metrics.tsx +++ b/frontend/src/pages/Metrics.tsx @@ -1,10 +1,10 @@ -import Header from '@src/layouts/Header' -import MetricsStepper from '@src/components/Metrics/MetricsStepper' -import { ContextProvider } from '@src/hooks/useMetricsStepValidationCheckContext' -import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect' +import Header from '@src/layouts/Header'; +import MetricsStepper from '@src/components/Metrics/MetricsStepper'; +import { ContextProvider } from '@src/hooks/useMetricsStepValidationCheckContext'; +import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect'; const Metrics = () => { - const props = useNotificationLayoutEffect() + const props = useNotificationLayoutEffect(); return ( <> @@ -13,7 +13,7 @@ const Metrics = () => { - ) -} + ); +}; -export default Metrics +export default Metrics; diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 6ba7e22b11..43cc90a3b8 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,17 +1,17 @@ -import { Suspense } from 'react' -import { Route, Routes } from 'react-router-dom' -import { routes } from './config/routes' -import { Loading } from './components/Loading' +import { Suspense } from 'react'; +import { Route, Routes } from 'react-router-dom'; +import { routes } from './config/routes'; +import { Loading } from './components/Loading'; const Router = () => { const appRoutes = routes.map((item) => { - return } /> - }) + return } />; + }); return ( }> {appRoutes} - ) -} + ); +}; -export default Router +export default Router; diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 385fba18a7..48146f68fb 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -1,8 +1,8 @@ -import { configureStore } from '@reduxjs/toolkit' -import stepperReducer from './context/stepper/StepperSlice' -import configReducer from './context/config/configSlice' -import metricsSlice from './context/Metrics/metricsSlice' -import headerSlice from '@src/context/header/headerSlice' +import { configureStore } from '@reduxjs/toolkit'; +import stepperReducer from './context/stepper/StepperSlice'; +import configReducer from './context/config/configSlice'; +import metricsSlice from './context/Metrics/metricsSlice'; +import headerSlice from '@src/context/header/headerSlice'; export const store = configureStore({ reducer: { @@ -11,7 +11,7 @@ export const store = configureStore({ metrics: metricsSlice, header: headerSlice, }, -}) +}); -export type RootState = ReturnType -export type AppDispatch = typeof store.dispatch +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index dc18bbbd9c..c1d3bc05e4 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -1,65 +1,65 @@ -import { createTheme } from '@mui/material/styles' -import { indigo } from '@mui/material/colors' -import { FIVE_HUNDRED } from '@src/constants/commons' -import '@fontsource/roboto' +import { createTheme } from '@mui/material/styles'; +import { indigo } from '@mui/material/colors'; +import { FIVE_HUNDRED } from '@src/constants/commons'; +import '@fontsource/roboto'; declare module '@mui/material/styles' { interface Theme { main: { - backgroundColor: string - color: string - secondColor: string - fontSize: string - boxShadow: string - cardShadow: string - cardBorder: string + backgroundColor: string; + color: string; + secondColor: string; + fontSize: string; + boxShadow: string; + cardShadow: string; + cardBorder: string; font: { - primary: string - secondary: string - } + primary: string; + secondary: string; + }; button: { disabled: { - backgroundColor: string - color: string - } - } - } + backgroundColor: string; + color: string; + }; + }; + }; } // allow configuration using `createTheme` interface ThemeOptions { main: { - backgroundColor: string - color: string - secondColor: string - fontSize: string - boxShadow: string - cardShadow: string - cardBorder: string + backgroundColor: string; + color: string; + secondColor: string; + fontSize: string; + boxShadow: string; + cardShadow: string; + cardBorder: string; font: { - primary: string - secondary: string - } + primary: string; + secondary: string; + }; button: { disabled: { - backgroundColor: string - color: string - } - } - } + backgroundColor: string; + color: string; + }; + }; + }; } interface Components { errorMessage: { - color: string - paddingBottom: string - } + color: string; + paddingBottom: string; + }; waringMessage: { - color: string - } + color: string; + }; tip: { - color: string - } + color: string; + }; } } @@ -125,4 +125,4 @@ export const theme = createTheme({ color: '#ED6D03CC', }, }, -}) +}); diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 851eddc876..0e06502f06 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -1,46 +1,46 @@ -import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/emojis/emoji' -import { ICycleTimeSetting, IJiraColumnsWithValue } from '@src/context/Metrics/metricsSlice' -import dayjs from 'dayjs' -import { DATE_FORMAT_TEMPLATE } from '@src/constants/template' -import duration from 'dayjs/plugin/duration' -dayjs.extend(duration) +import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/emojis/emoji'; +import { ICycleTimeSetting, IJiraColumnsWithValue } from '@src/context/Metrics/metricsSlice'; +import dayjs from 'dayjs'; +import { DATE_FORMAT_TEMPLATE } from '@src/constants/template'; +import duration from 'dayjs/plugin/duration'; +dayjs.extend(duration); export const exportToJsonFile = (filename: string, json: object) => { - const dataStr = JSON.stringify(json, null, 4) - const dataUri = `data:application/json;charset=utf-8,${encodeURIComponent(dataStr)}` - const exportFileDefaultName = `${filename}.json` + const dataStr = JSON.stringify(json, null, 4); + const dataUri = `data:application/json;charset=utf-8,${encodeURIComponent(dataStr)}`; + const exportFileDefaultName = `${filename}.json`; - const linkElement = document.createElement('a') - linkElement.setAttribute('href', dataUri) - linkElement.setAttribute('download', exportFileDefaultName) - linkElement.click() -} + const linkElement = document.createElement('a'); + linkElement.setAttribute('href', dataUri); + linkElement.setAttribute('download', exportFileDefaultName); + linkElement.click(); +}; export const downloadCSV = (filename: string, data: string) => { - const blob = new Blob([data], { type: 'application/octet-stream' }) - const url = window.URL.createObjectURL(blob) - const link = document.createElement('a') - link.href = url - link.setAttribute('download', filename) - document.body.appendChild(link) - link.click() - document.body.removeChild(link) -} + const blob = new Blob([data], { type: 'application/octet-stream' }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', filename); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}; export const transformToCleanedBuildKiteEmoji = (input: OriginBuildKiteEmoji[]): CleanedBuildKiteEmoji[] => input.map(({ name, image, aliases }) => ({ image, aliases: [...new Set([...aliases, name])], - })) + })); export const getJiraBoardToken = (token: string, email: string) => { if (token) { - const encodedMsg = btoa(`${email}:${token}`) - return `Basic ${encodedMsg}` + const encodedMsg = btoa(`${email}:${token}`); + return `Basic ${encodedMsg}`; } else { - return '' + return ''; } -} +}; export const filterAndMapCycleTimeSettings = ( cycleTimeSettings: ICycleTimeSetting[], @@ -49,29 +49,29 @@ export const filterAndMapCycleTimeSettings = ( return cycleTimeSettings .filter((item) => item.value !== '----') .flatMap((cycleTimeSetting) => { - const previousName = cycleTimeSetting.name - const jiraColumnsStatuses = jiraColumnsWithValue.find((item) => item.name === previousName)?.statuses || [] + const previousName = cycleTimeSetting.name; + const jiraColumnsStatuses = jiraColumnsWithValue.find((item) => item.name === previousName)?.statuses || []; return jiraColumnsStatuses.map((item) => ({ name: item, value: cycleTimeSetting.value, - })) - }) -} + })); + }); +}; export const findCaseInsensitiveType = (option: string[], value: string): string => { - const newValue = option.find((item) => value.toLowerCase() === item.toLowerCase()) - return newValue ? newValue : value -} + const newValue = option.find((item) => value.toLowerCase() === item.toLowerCase()); + return newValue ? newValue : value; +}; export const formatDate = (date: Date | string) => { - return dayjs(date).format(DATE_FORMAT_TEMPLATE) -} + return dayjs(date).format(DATE_FORMAT_TEMPLATE); +}; export const formatMinToHours = (duration: number) => { - return dayjs.duration(duration, 'minutes').asHours() -} + return dayjs.duration(duration, 'minutes').asHours(); +}; export const formatMillisecondsToHours = (duration: number) => { - return dayjs.duration(duration, 'milliseconds').asHours() -} + return dayjs.duration(duration, 'milliseconds').asHours(); +}; diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 09d5363f39..666ebff8c7 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -1,7 +1,7 @@ /// declare module '*.svg' { - const content: unknown - export const ReactComponent: unknown - export default content + const content: unknown; + export const ReactComponent: unknown; + export default content; } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 25a4be21bb..09b54b4cc9 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react-swc' -import { resolve } from 'path/posix' -import { VitePWA } from 'vite-plugin-pwa' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import { resolve } from 'path/posix'; +import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ server: { @@ -62,4 +62,4 @@ export default defineConfig({ '@src': resolve(__dirname, './src'), }, }, -}) +}); From d31535494b9f37e3288988c0ffcdcac661176eb3 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 11:00:07 +0800 Subject: [PATCH 82/90] [frontend] fix codacy --- .../Common/DateRangeViewer/DateRangeViewer.test.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx index 37227cb7bb..aaeafd9046 100644 --- a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx +++ b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx @@ -1,11 +1,9 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import DateRangeViewer from '@src/components/Common/DateRangeViewer'; describe('DateRangeVier', () => { it('should show date when render component given startDate and endDate', () => { - const { getByText } = render( - - ); - expect(getByText(/2022\/01\/01/g)).toBeInTheDocument(); - expect(getByText(/2022\/01\/02/g)).toBeInTheDocument(); + render(); + expect(screen.getByText(/2022\/01\/01/)).toBeInTheDocument(); + expect(screen.getByText(/2022\/01\/02/)).toBeInTheDocument(); }); }); From b2b162948c0969fb78f9743e694ff7235982e029 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 13:45:55 +0800 Subject: [PATCH 83/90] [frontend] fix codacy --- .../DateRangeViewer/DateRangeViewer.test.tsx | 1 + .../Common/EllipsisText/EllipsisText.test.tsx | 14 +-- .../Common/ReportGrid/ReportCard.test.tsx | 11 ++- .../DateDisplay/CollectionDuration.test.tsx | 16 +-- .../src/components/Loading/Loading.test.tsx | 1 + .../Metrics/ConfigStep/Board.test.tsx | 97 +++++++++--------- .../ConfigStep/BranchSelection.test.tsx | 17 ++-- .../Metrics/ConfigStep/ConfigStep.test.tsx | 83 ++++++++-------- .../ConfigStep/DateRangePicker.test.tsx | 29 +++--- .../Metrics/ConfigStep/SourceControl.test.tsx | 55 ++++++----- .../MetricsStep/Classification.test.tsx | 61 ++++++------ .../Metrics/MetricsStep/Crews.test.tsx | 69 ++++++------- .../Metrics/MetricsStep/CycleTime.test.tsx | 99 ++++++++++--------- .../PipelineMetricSelection.test.tsx | 8 +- .../MetricsStep/MultiAutoComplete.test.tsx | 16 +-- 15 files changed, 294 insertions(+), 283 deletions(-) diff --git a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx index aaeafd9046..eb15b7b1e9 100644 --- a/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx +++ b/frontend/__tests__/src/components/Common/DateRangeViewer/DateRangeViewer.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { render, screen } from '@testing-library/react'; import DateRangeViewer from '@src/components/Common/DateRangeViewer'; describe('DateRangeVier', () => { diff --git a/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx b/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx index a95157b6c3..215afd381a 100644 --- a/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx +++ b/frontend/__tests__/src/components/Common/EllipsisText/EllipsisText.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import EllipsisText from '@src/components/Common/EllipsisText'; describe('EllipsisText', () => { @@ -7,7 +7,7 @@ describe('EllipsisText', () => { it('should forward ref properly', () => { const ref = React.createRef(); - const { getByLabelText } = render( + render(
    test @@ -15,12 +15,12 @@ describe('EllipsisText', () => { ); - const childDOM = getByLabelText('test-ref'); + const childDOM = screen.getByLabelText('test-ref'); expect(ref.current).toEqual(childDOM); }); it('should apply fit-content as its width when `fitContent` specified', async () => { - const { getByLabelText } = render( + render(
    test
    @@ -28,12 +28,12 @@ describe('EllipsisText', () => {
    ); - const targetElement = getByLabelText('test-ellipsis-text'); + const targetElement = screen.getByLabelText('test-ellipsis-text'); expect(targetElement).toHaveStyle({ width: 'fit-content' }); }); it('should apply fit-content as its width when `fitContent` explicitly set to false', async () => { - const { getByLabelText } = render( + render(
    test
    @@ -41,7 +41,7 @@ describe('EllipsisText', () => {
    ); - const targetElement = getByLabelText('test-ellipsis-text'); + const targetElement = screen.getByLabelText('test-ellipsis-text'); expect(targetElement).toHaveStyle({ width: 'auto' }); }); }); diff --git a/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx b/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx index ce25b4ed95..5f9763446b 100644 --- a/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx +++ b/frontend/__tests__/src/components/Common/ReportGrid/ReportCard.test.tsx @@ -1,4 +1,5 @@ -import { render } from '@testing-library/react'; +import React from 'react'; +import { render, screen } from '@testing-library/react'; import { ReportCard } from '@src/components/Common/ReportGrid/ReportCard'; describe('Report Card', () => { @@ -18,10 +19,10 @@ describe('Report Card', () => { }, ]; - const { getByText, queryByText } = render(); + render(); - expect(getByText('1.00')).toBeInTheDocument(); - expect(getByText('2.00')).toBeInTheDocument(); - expect(queryByText('3.00')).not.toBeInTheDocument(); + expect(screen.getByText('1.00')).toBeInTheDocument(); + expect(screen.getByText('2.00')).toBeInTheDocument(); + expect(screen.queryByText('3.00')).not.toBeInTheDocument(); }); }); diff --git a/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx b/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx index 340bad01a3..74c789cef8 100644 --- a/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx +++ b/frontend/__tests__/src/components/DateDisplay/CollectionDuration.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import CollectionDuration from '@src/components/Common/CollectionDuration'; import React from 'react'; import { IMPORTED_NEW_CONFIG_FIXTURE, TIME_DISPLAY_TITTLE_END, TIME_DISPLAY_TITTLE_START } from '../../fixtures'; @@ -12,16 +12,16 @@ describe('Collection Duration', () => { /> ); it('should render the start and end text correctly', () => { - const { getByText } = setup(); + setup(); - expect(getByText(TIME_DISPLAY_TITTLE_START)).toBeInTheDocument(); - expect(getByText(TIME_DISPLAY_TITTLE_END)).toBeInTheDocument(); + expect(screen.getByText(TIME_DISPLAY_TITTLE_START)).toBeInTheDocument(); + expect(screen.getByText(TIME_DISPLAY_TITTLE_END)).toBeInTheDocument(); }); it('should render the start and end time with correct format', () => { - const { getByText, getAllByText } = setup(); + setup(); - expect(getByText('16')).toBeInTheDocument(); - expect(getAllByText('Mar 23')).toHaveLength(2); - expect(getByText('30')).toBeInTheDocument(); + expect(screen.getByText('16')).toBeInTheDocument(); + expect(screen.getAllByText('Mar 23')).toHaveLength(2); + expect(screen.getByText('30')).toBeInTheDocument(); }); }); diff --git a/frontend/__tests__/src/components/Loading/Loading.test.tsx b/frontend/__tests__/src/components/Loading/Loading.test.tsx index cfb4af602f..60218db399 100644 --- a/frontend/__tests__/src/components/Loading/Loading.test.tsx +++ b/frontend/__tests__/src/components/Loading/Loading.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { render } from '@testing-library/react'; import { Loading } from '@src/components/Loading'; diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx index 10d4bb1231..6733406a3d 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/Board.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; import { Board } from '@src/components/Metrics/ConfigStep/Board'; import { @@ -54,28 +55,28 @@ describe('Board', () => { }); it('should show board title and fields when render board component ', () => { - const { getByLabelText, getAllByText } = setup(); + setup(); BOARD_FIELDS.map((field) => { - expect(getByLabelText(`${field} *`)).toBeInTheDocument(); + expect(screen.getByLabelText(`${field} *`)).toBeInTheDocument(); }); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + expect(screen.getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); }); it('should show default value jira when init board component', () => { - const { getByText, queryByText } = setup(); - const boardType = getByText(BOARD_TYPES.JIRA); + setup(); + const boardType = screen.getByText(BOARD_TYPES.JIRA); expect(boardType).toBeInTheDocument(); - const option = queryByText(BOARD_TYPES.CLASSIC_JIRA); + const option = screen.queryByText(BOARD_TYPES.CLASSIC_JIRA); expect(option).not.toBeTruthy(); }); it('should show detail options when click board field', () => { - const { getByRole } = setup(); - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); - const listBox = within(getByRole('listbox')); + setup(); + fireEvent.mouseDown(screen.getByRole('button', { name: CONFIG_TITLE.BOARD })); + const listBox = within(screen.getByRole('listbox')); const options = listBox.getAllByRole('option'); const optionValue = options.map((li) => li.getAttribute('data-value')); @@ -83,52 +84,52 @@ describe('Board', () => { }); it('should show board type when select board field value ', async () => { - const { getByRole, getByText } = setup(); + setup(); - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); - fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)); + fireEvent.mouseDown(screen.getByRole('button', { name: CONFIG_TITLE.BOARD })); + fireEvent.click(screen.getByText(BOARD_TYPES.CLASSIC_JIRA)); await waitFor(() => { - expect(getByText(BOARD_TYPES.CLASSIC_JIRA)).toBeInTheDocument(); + expect(screen.getByText(BOARD_TYPES.CLASSIC_JIRA)).toBeInTheDocument(); }); }); it('should show error message when input a wrong type or empty email ', async () => { - const { getByTestId, getByText } = setup(); + setup(); const EMAil_INVALID_ERROR_MESSAGE = 'Email is invalid'; - const emailInput = getByTestId('Email').querySelector('input') as HTMLInputElement; + const emailInput = screen.getByTestId('Email').querySelector('input') as HTMLInputElement; fireEvent.change(emailInput, { target: { value: 'wrong type email' } }); - expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toBeVisible(); - expect(getByText(EMAil_INVALID_ERROR_MESSAGE)).toHaveStyle(ERROR_MESSAGE_COLOR); + expect(screen.getByText(EMAil_INVALID_ERROR_MESSAGE)).toBeVisible(); + expect(screen.getByText(EMAil_INVALID_ERROR_MESSAGE)).toHaveStyle(ERROR_MESSAGE_COLOR); fireEvent.change(emailInput, { target: { value: '' } }); const EMAIL_REQUIRE_ERROR_MESSAGE = 'Email is required'; - expect(getByText(EMAIL_REQUIRE_ERROR_MESSAGE)).toBeVisible(); + expect(screen.getByText(EMAIL_REQUIRE_ERROR_MESSAGE)).toBeVisible(); }); it('should clear other fields information when change board field selection', () => { - const { getByRole, getByText } = setup(); - const boardIdInput = getByRole('textbox', { + setup(); + const boardIdInput = screen.getByRole('textbox', { name: 'Board Id', }) as HTMLInputElement; - const emailInput = getByRole('textbox', { + const emailInput = screen.getByRole('textbox', { name: 'Email', }) as HTMLInputElement; fireEvent.change(boardIdInput, { target: { value: 2 } }); fireEvent.change(emailInput, { target: { value: 'mockEmail@qq.com' } }); - fireEvent.mouseDown(getByRole('button', { name: CONFIG_TITLE.BOARD })); - fireEvent.click(getByText(BOARD_TYPES.CLASSIC_JIRA)); + fireEvent.mouseDown(screen.getByRole('button', { name: CONFIG_TITLE.BOARD })); + fireEvent.click(screen.getByText(BOARD_TYPES.CLASSIC_JIRA)); expect(emailInput.value).toEqual(''); expect(boardIdInput.value).toEqual(''); }); it('should clear all fields information when click reset button', async () => { - const { getByRole, getByText, queryByRole } = setup(); + setup(); const fieldInputs = BOARD_FIELDS.slice(1, 5).map( (label) => screen.getByRole('textbox', { @@ -138,22 +139,22 @@ describe('Board', () => { ); fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)); + fireEvent.click(screen.getByText(VERIFY)); await waitFor(() => { - fireEvent.click(getByRole('button', { name: RESET })); + fireEvent.click(screen.getByRole('button', { name: RESET })); }); fieldInputs.map((input) => { expect(input.value).toEqual(''); }); - expect(getByText(BOARD_TYPES.JIRA)).toBeInTheDocument(); - expect(queryByRole('button', { name: RESET })).not.toBeTruthy(); - expect(queryByRole('button', { name: VERIFY })).toBeDisabled(); + expect(screen.getByText(BOARD_TYPES.JIRA)).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: RESET })).not.toBeTruthy(); + expect(screen.queryByRole('button', { name: VERIFY })).toBeDisabled(); }); it('should enabled verify button when all fields checked correctly given disable verify button', () => { - const { getByRole } = setup(); - const verifyButton = getByRole('button', { name: VERIFY }); + setup(); + const verifyButton = screen.getByRole('button', { name: VERIFY }); expect(verifyButton).toBeDisabled(); @@ -163,34 +164,34 @@ describe('Board', () => { }); it('should show reset button and verified button when verify succeed ', async () => { - const { getByText } = setup(); + setup(); fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)); + fireEvent.click(screen.getByText(VERIFY)); await waitFor(() => { - expect(getByText(RESET)).toBeVisible(); + expect(screen.getByText(RESET)).toBeVisible(); }); await waitFor(() => { - expect(getByText(VERIFIED)).toBeTruthy(); + expect(screen.getByText(VERIFIED)).toBeTruthy(); }); }); it('should called verifyBoard method once when click verify button', async () => { - const { getByRole, getByText } = setup(); + setup(); fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })); + fireEvent.click(screen.getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(getByText('Verified')).toBeInTheDocument(); + expect(screen.getByText('Verified')).toBeInTheDocument(); }); }); it('should check loading animation when click verify button', async () => { - const { getByRole, container } = setup(); + const { container } = setup(); fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })); + fireEvent.click(screen.getByRole('button', { name: VERIFY })); await waitFor(() => { expect(container.getElementsByTagName('span')[0].getAttribute('role')).toEqual('progressbar'); @@ -199,17 +200,17 @@ describe('Board', () => { it('should check noCardPop show and disappear when board verify response status is 204', async () => { server.use(rest.post(MOCK_BOARD_URL_FOR_JIRA, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent)))); - const { getByText, getByRole } = setup(); + setup(); fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })); + fireEvent.click(screen.getByRole('button', { name: VERIFY })); await waitFor(() => { - expect(getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument(); + expect(screen.getByText(NO_CARD_ERROR_MESSAGE)).toBeInTheDocument(); }); - fireEvent.click(getByRole('button', { name: 'Ok' })); - expect(getByText(NO_CARD_ERROR_MESSAGE)).not.toBeVisible(); + fireEvent.click(screen.getByRole('button', { name: 'Ok' })); + expect(screen.getByText(NO_CARD_ERROR_MESSAGE)).not.toBeVisible(); }); it('should check error notification show and disappear when board verify response status is 401', async () => { @@ -218,14 +219,14 @@ describe('Board', () => { res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) ); - const { getByText, getByRole } = setup(); + setup(); fillBoardFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })); + fireEvent.click(screen.getByRole('button', { name: VERIFY })); await waitFor(() => { expect( - getByText(`${BOARD_TYPES.JIRA} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) + screen.getByText(`${BOARD_TYPES.JIRA} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) ).toBeInTheDocument(); }); }); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx index 001cb073ef..83060686bb 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/BranchSelection.test.tsx @@ -1,4 +1,5 @@ -import { act, render } from '@testing-library/react'; +import React from 'react'; +import { act, render, screen } from '@testing-library/react'; import { BranchSelection } from '@src/components/Metrics/ConfigStep/BranchSelection'; import { ALL, BRANCH, MOCK_AUTOCOMPLETE_LIST } from '../../../fixtures'; import { setupStore } from '../../../utils/setupStoreUtil'; @@ -32,26 +33,26 @@ describe('BranchSelection', () => { }); it('should has Option 2 when render BranchSelection component', async () => { - const { getByRole } = setup(); + setup(); - expect(getByRole('button', { name: 'Option 2' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Option 2' })).toBeVisible(); }); it('should show branches selection when getSteps succeed ', async () => { - const { getByRole, getByText } = setup(); + setup(); - expect(getByText(BRANCH)).toBeInTheDocument(); + expect(screen.getByText(BRANCH)).toBeInTheDocument(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: 'Branches' })); + await userEvent.click(screen.getByRole('combobox', { name: 'Branches' })); }); - const allOption = getByRole('option', { name: ALL }); + const allOption = screen.getByRole('option', { name: ALL }); await act(async () => { await userEvent.click(allOption); }); - const optionOne = getByRole('button', { name: 'Option 1' }); + const optionOne = screen.getByRole('button', { name: 'Option 1' }); expect(optionOne).toBeVisible(); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx index f0b930008a..05b25e006a 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx @@ -1,4 +1,5 @@ -import { act, fireEvent, Matcher, render, waitFor, within } from '@testing-library/react'; +import React from 'react'; +import { act, fireEvent, Matcher, render, waitFor, within, screen } from '@testing-library/react'; import ConfigStep from '@src/components/Metrics/ConfigStep'; import { CHINA_CALENDAR, @@ -44,17 +45,17 @@ describe('ConfigStep', () => { }); it('should show project name when render configStep', () => { - const { getByText } = setup(); + setup(); - expect(getByText(PROJECT_NAME_LABEL)).toBeInTheDocument(); + expect(screen.getByText(PROJECT_NAME_LABEL)).toBeInTheDocument(); }); it('should show project name when input some letters', () => { - const { getByRole, getByDisplayValue } = setup(); + setup(); const hasInputValue = (e: HTMLElement, inputValue: Matcher) => { - return getByDisplayValue(inputValue) === e; + return screen.getByDisplayValue(inputValue) === e; }; - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); + const input = screen.getByRole('textbox', { name: PROJECT_NAME_LABEL }); expect(input).toBeInTheDocument(); @@ -64,37 +65,37 @@ describe('ConfigStep', () => { }); it('should show error message when project name is Empty', () => { - const { getByRole, getByText } = setup(); - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); + setup(); + const input = screen.getByRole('textbox', { name: PROJECT_NAME_LABEL }); fireEvent.change(input, { target: { value: TEST_PROJECT_NAME } }); fireEvent.change(input, { target: { value: '' } }); - expect(getByText('Project name is required')).toBeInTheDocument(); + expect(screen.getByText('Project name is required')).toBeInTheDocument(); }); it('should show error message when click project name input with no letter', () => { - const { getByRole, getByText } = setup(); - const input = getByRole('textbox', { name: PROJECT_NAME_LABEL }); + setup(); + const input = screen.getByRole('textbox', { name: PROJECT_NAME_LABEL }); fireEvent.focus(input); - expect(getByText('Project name is required')).toBeInTheDocument(); + expect(screen.getByText('Project name is required')).toBeInTheDocument(); }); it('should select Regular calendar by default when rendering the radioGroup', () => { - const { getByRole } = setup(); - const defaultValue = getByRole('radio', { name: REGULAR_CALENDAR }); - const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }); + setup(); + const defaultValue = screen.getByRole('radio', { name: REGULAR_CALENDAR }); + const chinaCalendar = screen.getByRole('radio', { name: CHINA_CALENDAR }); expect(defaultValue).toBeChecked(); expect(chinaCalendar).not.toBeChecked(); }); it('should switch the radio when any radioLabel is selected', () => { - const { getByRole } = setup(); - const chinaCalendar = getByRole('radio', { name: CHINA_CALENDAR }); - const regularCalendar = getByRole('radio', { name: REGULAR_CALENDAR }); + setup(); + const chinaCalendar = screen.getByRole('radio', { name: CHINA_CALENDAR }); + const regularCalendar = screen.getByRole('radio', { name: REGULAR_CALENDAR }); fireEvent.click(chinaCalendar); expect(chinaCalendar).toBeChecked(); @@ -107,47 +108,47 @@ describe('ConfigStep', () => { }); it('should not show board component when init ConfigStep component ', async () => { - const { queryByText } = setup(); + setup(); await waitFor(() => { - expect(queryByText(CONFIG_TITLE.BOARD)).toBeNull(); + expect(screen.queryByText(CONFIG_TITLE.BOARD)).toBeNull(); }); }); it('should show board component when MetricsTypeCheckbox select Velocity,Cycle time', () => { - const { getByRole, getAllByText } = setup(); + setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); - const requireDateSelection = within(getByRole('listbox')); + fireEvent.mouseDown(screen.getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(screen.getByRole('listbox')); fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); fireEvent.click(requireDateSelection.getByRole('option', { name: CYCLE_TIME })); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + expect(screen.getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); }); it('should show board component when MetricsTypeCheckbox select Classification, ', () => { - const { getByRole, getAllByText } = setup(); + setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); - const requireDateSelection = within(getByRole('listbox')); + fireEvent.mouseDown(screen.getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(screen.getByRole('listbox')); fireEvent.click(requireDateSelection.getByRole('option', { name: 'Classification' })); - expect(getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); + expect(screen.getAllByText(CONFIG_TITLE.BOARD)[0]).toBeInTheDocument(); }); it('should verify again when calendar type is changed given board fields are filled and verified', () => { - const { getByRole, getByText, queryByText } = setup(); + setup(); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); - const requireDateSelection = within(getByRole('listbox')); + fireEvent.mouseDown(screen.getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(screen.getByRole('listbox')); fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)); - fireEvent.click(getByText(CHINA_CALENDAR)); + fireEvent.click(screen.getByText(VERIFY)); + fireEvent.click(screen.getByText(CHINA_CALENDAR)); - expect(queryByText(VERIFY)).toBeVisible(); - expect(queryByText('Verified')).toBeNull(); - expect(queryByText(RESET)).toBeNull(); + expect(screen.queryByText(VERIFY)).toBeVisible(); + expect(screen.queryByText('Verified')).toBeNull(); + expect(screen.queryByText(RESET)).toBeNull(); }); it('should verify again when date picker is changed given board fields are filled and verified', () => { @@ -155,11 +156,11 @@ describe('ConfigStep', () => { const today = dayjs().format('MM/DD/YYYY'); const startDateInput = getByLabelText('From *'); - fireEvent.mouseDown(getByRole('button', { name: REQUIRED_DATA })); - const requireDateSelection = within(getByRole('listbox')); + fireEvent.mouseDown(screen.getByRole('button', { name: REQUIRED_DATA })); + const requireDateSelection = within(screen.getByRole('listbox')); fireEvent.click(requireDateSelection.getByRole('option', { name: VELOCITY })); fillBoardFieldsInformation(); - fireEvent.click(getByText(VERIFY)); + fireEvent.click(screen.getByText(VERIFY)); fireEvent.change(startDateInput, { target: { value: today } }); expect(queryByText(VERIFY)).toBeVisible(); @@ -168,9 +169,9 @@ describe('ConfigStep', () => { }); it('should show warning message when selectWarningMessage has a value', async () => { - const { getByText } = setup(); + setup(); - expect(getByText('Test warning Message')).toBeVisible(); + expect(screen.getByText('Test warning Message')).toBeVisible(); }); it('should show disable warning message When selectWarningMessage has a value after two seconds', async () => { diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx index 92c1abe839..490ee1d2ec 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/DateRangePicker.test.tsx @@ -1,4 +1,5 @@ -import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; import { DateRangePicker } from '@src/components/Metrics/ConfigStep/DateRangePicker'; import { ERROR_DATE } from '../../../fixtures'; import dayjs from 'dayjs'; @@ -28,23 +29,23 @@ describe('DateRangePicker', () => { }; it('should render DateRangePicker', () => { - const { queryAllByText } = setup(); + setup(); - expect(queryAllByText(START_DATE_LABEL)).toHaveLength(1); - expect(queryAllByText(END_DATE_LABEL)).toHaveLength(1); + expect(screen.queryAllByText(START_DATE_LABEL)).toHaveLength(1); + expect(screen.queryAllByText(END_DATE_LABEL)).toHaveLength(1); }); it('should show right start date when input a valid date given init start date is null ', () => { - const { getByRole } = setup(); - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + setup(); + const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }); expectDate(startDateInput); }); it('should show right end date when input a valid date given init end date is null ', () => { - const { getByRole } = setup(); - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; + setup(); + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; fireEvent.change(endDateInput, { target: { value: INPUT_DATE_VALUE } }); @@ -52,10 +53,10 @@ describe('DateRangePicker', () => { }); it('should Auto-fill endDate which is after startDate 13 days when fill right startDate ', () => { - const { getByRole } = setup(); + setup(); const endDate = TODAY.add(13, 'day'); - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; + const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }); @@ -65,9 +66,9 @@ describe('DateRangePicker', () => { }); it('should not Auto-fill endDate which is after startDate 14 days when fill wrong format startDate ', () => { - const { getByRole } = setup(); - const startDateInput = getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; - const endDateInput = getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; + setup(); + const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; fireEvent.change(startDateInput, { target: { value: ERROR_DATE } }); diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx index b89593a9e0..dd20b1cd66 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/SourceControl.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { setupStore } from '../../../utils/setupStoreUtil'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { Provider } from 'react-redux'; @@ -60,43 +61,43 @@ describe('SourceControl', () => { }); it('should show sourceControl title and fields when render sourceControl component', () => { - const { getByLabelText, getAllByText } = setup(); + setup(); - expect(getAllByText(CONFIG_TITLE.SOURCE_CONTROL)[0]).toBeInTheDocument(); + expect(screen.getAllByText(CONFIG_TITLE.SOURCE_CONTROL)[0]).toBeInTheDocument(); SOURCE_CONTROL_FIELDS.map((field) => { - expect(getByLabelText(`${field} *`)).toBeInTheDocument(); + expect(screen.getByLabelText(`${field} *`)).toBeInTheDocument(); }); }); it('should show default value gitHub when init sourceControl component', () => { - const { getByText } = setup(); - const sourceControlType = getByText(SOURCE_CONTROL_TYPES.GITHUB); + setup(); + const sourceControlType = screen.getByText(SOURCE_CONTROL_TYPES.GITHUB); expect(sourceControlType).toBeInTheDocument(); }); it('should clear all fields information when click reset button', async () => { - const { getByRole, getByText, queryByRole } = setup(); + setup(); const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; fillSourceControlFieldsInformation(); - fireEvent.click(getByText(VERIFY)); + fireEvent.click(screen.getByText(VERIFY)); await waitFor(() => { - expect(getByRole('button', { name: RESET })).toBeTruthy(); - fireEvent.click(getByRole('button', { name: RESET })); + expect(screen.getByRole('button', { name: RESET })).toBeTruthy(); + fireEvent.click(screen.getByRole('button', { name: RESET })); }); expect(tokenInput.value).toEqual(''); - expect(getByText(SOURCE_CONTROL_TYPES.GITHUB)).toBeInTheDocument(); - expect(queryByRole('button', { name: RESET })).not.toBeTruthy(); - expect(getByRole('button', { name: VERIFY })).toBeDisabled(); + expect(screen.getByText(SOURCE_CONTROL_TYPES.GITHUB)).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: RESET })).not.toBeTruthy(); + expect(screen.getByRole('button', { name: VERIFY })).toBeDisabled(); }); it('should enable verify button when all fields checked correctly given disable verify button', () => { - const { getByRole } = setup(); - const verifyButton = getByRole('button', { name: VERIFY }); + setup(); + const verifyButton = screen.getByRole('button', { name: VERIFY }); expect(verifyButton).toBeDisabled(); @@ -106,22 +107,22 @@ describe('SourceControl', () => { }); it('should show reset button and verified button when verify successfully', async () => { - const { getByText } = setup(); + setup(); fillSourceControlFieldsInformation(); - fireEvent.click(getByText(VERIFY)); + fireEvent.click(screen.getByText(VERIFY)); await waitFor(() => { - expect(getByText(RESET)).toBeTruthy(); + expect(screen.getByText(RESET)).toBeTruthy(); }); await waitFor(() => { - expect(getByText(VERIFIED)).toBeTruthy(); + expect(screen.getByText(VERIFIED)).toBeTruthy(); }); }); it('should show error message and error style when token is empty', () => { - const { getByText } = setup(); + setup(); fillSourceControlFieldsInformation(); @@ -129,20 +130,20 @@ describe('SourceControl', () => { fireEvent.change(tokenInput, { target: { value: '' } }); - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toBeInTheDocument(); - expect(getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR); + expect(screen.getByText(TOKEN_ERROR_MESSAGE[1])).toBeInTheDocument(); + expect(screen.getByText(TOKEN_ERROR_MESSAGE[1])).toHaveStyle(ERROR_MESSAGE_COLOR); }); it('should show error message and error style when token is invalid', () => { - const { getByText } = setup(); + setup(); const mockInfo = 'mockToken'; const tokenInput = screen.getByTestId('sourceControlTextField').querySelector('input') as HTMLInputElement; fireEvent.change(tokenInput, { target: { value: mockInfo } }); expect(tokenInput.value).toEqual(mockInfo); - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument(); - expect(getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR); + expect(screen.getByText(TOKEN_ERROR_MESSAGE[0])).toBeInTheDocument(); + expect(screen.getByText(TOKEN_ERROR_MESSAGE[0])).toHaveStyle(ERROR_MESSAGE_COLOR); }); it('should show error notification when sourceControl verify response status is 401', async () => { @@ -151,15 +152,15 @@ describe('SourceControl', () => { res(ctx.status(HttpStatusCode.Unauthorized), ctx.json({ hintInfo: VERIFY_ERROR_MESSAGE.UNAUTHORIZED })) ) ); - const { getByText, getByRole } = setup(); + setup(); fillSourceControlFieldsInformation(); - fireEvent.click(getByRole('button', { name: VERIFY })); + fireEvent.click(screen.getByRole('button', { name: VERIFY })); await waitFor(() => { expect( - getByText(`${SOURCE_CONTROL_TYPES.GITHUB} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) + screen.getByText(`${SOURCE_CONTROL_TYPES.GITHUB} ${VERIFY_FAILED}: ${VERIFY_ERROR_MESSAGE.UNAUTHORIZED}`) ).toBeInTheDocument(); }); }); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx index 0b86eb7ea1..537d0969cd 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/Classification.test.tsx @@ -1,4 +1,5 @@ -import { act, render, waitFor, within } from '@testing-library/react'; +import React from 'react'; +import { act, render, waitFor, within, screen } from '@testing-library/react'; import { Classification } from '@src/components/Metrics/MetricsStep/Classification'; import userEvent from '@testing-library/user-event'; import { setupStore } from '../../../utils/setupStoreUtil'; @@ -41,90 +42,90 @@ describe('Classification', () => { }); it('should show Classification when render Classification component', () => { - const { getByText } = setup(); + setup(); - expect(getByText(mockTitle)).toBeInTheDocument(); - expect(getByText(mockLabel)).toBeInTheDocument(); + expect(screen.getByText(mockTitle)).toBeInTheDocument(); + expect(screen.getByText(mockLabel)).toBeInTheDocument(); }); it('should show default options when initialization', () => { - const { queryByText, getByText } = setup(); + setup(); - expect(getByText('Issue')).toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); + expect(screen.getByText('Issue')).toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); }); it('should show all options when click selectBox', async () => { - const { getByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); - expect(getByRole('option', { name: 'Issue' })).toBeInTheDocument(); - expect(getByRole('option', { name: 'Type' })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Issue' })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Type' })).toBeInTheDocument(); }); it('should show all targetField when click All and show nothing when cancel click', async () => { - const { getByText, getByRole, queryByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); await act(async () => { - await userEvent.click(getByText('All')); + await userEvent.click(screen.getByText('All')); }); const names = mockTargetFields.map((item) => item.name); - expect(getByRole('button', { name: names[0] })).toBeVisible(); - expect(getByRole('button', { name: names[1] })).toBeVisible(); + expect(screen.getByRole('button', { name: names[0] })).toBeVisible(); + expect(screen.getByRole('button', { name: names[1] })).toBeVisible(); await act(async () => { - await userEvent.click(getByText('All')); + await userEvent.click(screen.getByText('All')); }); - expect(queryByRole('button', { name: names[0] })).not.toBeInTheDocument(); - expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: names[0] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); }); it('should show selected targetField when click selected field', async () => { - const { getByRole, getByText, queryByRole } = setup(); + setup(); const names = mockTargetFields.map((item) => item.name); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); await act(async () => { - await userEvent.click(getByText('All')); + await userEvent.click(screen.getByText('All')); }); await act(async () => { - await userEvent.click(getByText('All')); + await userEvent.click(screen.getByText('All')); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); await act(async () => { await userEvent.click(listBox.getByRole('option', { name: names[0] })); }); - expect(queryByRole('button', { name: names[0] })).toBeInTheDocument(); - expect(queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: names[0] })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: names[1] })).not.toBeInTheDocument(); }); it('should show warning message when classification warning message has a value in cycleTime component', () => { - const { getByText } = setup(); + setup(); - expect(getByText('Test warning Message')).toBeVisible(); + expect(screen.getByText('Test warning Message')).toBeVisible(); }); it('should show disable warning message when classification warning message has a value after two seconds in cycleTime component', async () => { jest.useFakeTimers(); - const { queryByText } = setup(); + setup(); act(() => { jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + expect(screen.queryByText('Test warning Message')).not.toBeInTheDocument(); }); }); }); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx index 09da6d2363..6a15bb402f 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx @@ -1,4 +1,5 @@ -import { act, render, waitFor, within } from '@testing-library/react'; +import React from 'react'; +import { act, render, screen, waitFor, within } from '@testing-library/react' import { Crews } from '@src/components/Metrics/MetricsStep/Crews'; import userEvent from '@testing-library/user-event'; import { setupStore } from '../../../utils/setupStoreUtil'; @@ -41,25 +42,25 @@ describe('Crew', () => { }); it('should show Crews when render Crews component', () => { - const { getByText } = setup(); + setup(); - expect(getByText(mockTitle)).toBeInTheDocument(); + expect(screen.getByText(mockTitle)).toBeInTheDocument(); }); it('should selected all options by default when initializing', () => { - const { getByRole } = setup(); + setup(); - expect(getByRole('button', { name: 'crew A' })).toBeInTheDocument(); - expect(getByRole('button', { name: 'crew B' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'crew A' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'crew B' })).toBeInTheDocument(); }); it('should show detail options when click Included crews button', async () => { - const { getByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); expect(listBox.getByRole('option', { name: 'All' })).toBeVisible(); expect(listBox.getByRole('option', { name: 'crew A' })).toBeVisible(); @@ -67,70 +68,70 @@ describe('Crew', () => { }); it('should show error message when crews is null', async () => { - const { getByRole, getByText } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); await act(async () => { - await userEvent.click(getByText('All')); + await userEvent.click(screen.getByText('All')); }); - const requiredText = getByText('required'); + const requiredText = screen.getByText('required'); expect(requiredText.tagName).toBe('STRONG'); }); it('should show other selections when cancel one option given default all selections in crews', async () => { - const { getByRole, queryByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); await act(async () => { await userEvent.click(listBox.getByRole('option', { name: mockOptions[0] })); }); - expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); - expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); }); it('should clear crews data when check all option', async () => { - const { getByRole, queryByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('combobox', { name: mockLabel })); + await userEvent.click(screen.getByRole('combobox', { name: mockLabel })); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); const allOption = listBox.getByRole('option', { name: 'All' }); await act(async () => { await userEvent.click(allOption); }); - expect(queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); - expect(queryByRole('button', { name: mockOptions[1] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: mockOptions[0] })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: mockOptions[1] })).not.toBeInTheDocument(); await act(async () => { await userEvent.click(allOption); }); - expect(queryByRole('button', { name: mockOptions[0] })).toBeInTheDocument(); - expect(queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); - }, 50000); + expect(screen.queryByRole('button', { name: mockOptions[0] })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: mockOptions[1] })).toBeInTheDocument(); + }); it('should show radio group when render Crews component', async () => { - const { getByRole, getByText } = setup(); + setup(); - expect(getByText(assigneeFilterLabels[0])).toBeInTheDocument(); - expect(getByText(assigneeFilterLabels[1])).toBeInTheDocument(); - expect(getByRole('radiogroup', { name: 'assigneeFilter' })).toBeVisible(); + expect(screen.getByText(assigneeFilterLabels[0])).toBeInTheDocument(); + expect(screen.getByText(assigneeFilterLabels[1])).toBeInTheDocument(); + expect(screen.getByRole('radiogroup', { name: 'assigneeFilter' })).toBeVisible(); }); it('should show radio group with init value when render Crews component', async () => { - const { getAllByRole } = setup(); + setup(); - const radioGroups = getAllByRole('radio'); + const radioGroups = screen.getAllByRole('radio'); const optionValues = radioGroups.map((option) => option.getAttribute('value')); const checkedValues = radioGroups.map((option) => option.getAttribute('checked')); @@ -140,10 +141,10 @@ describe('Crew', () => { }); it('should call update function when change radio option', async () => { - const { getByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('radio', { name: assigneeFilterLabels[1] })); + await userEvent.click(screen.getByRole('radio', { name: assigneeFilterLabels[1] })); }); await waitFor(() => { diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx index 4eee7a15c2..cc97a25b0b 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/CycleTime.test.tsx @@ -1,4 +1,5 @@ -import { act, render, waitFor, within } from '@testing-library/react'; +import React from 'react'; +import { act, render, waitFor, within, screen } from '@testing-library/react'; import { CycleTime } from '@src/components/Metrics/MetricsStep/CycleTime'; import userEvent from '@testing-library/user-event'; import { Provider } from 'react-redux'; @@ -62,36 +63,36 @@ describe('CycleTime', () => { describe('CycleTime Title', () => { it('should show Cycle Time title when render Crews component', () => { - const { getByText } = setup(); - expect(getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument(); + setup(); + expect(screen.getByText(CYCLE_TIME_SETTINGS)).toBeInTheDocument(); }); it('should show Cycle Time tooltip when render Crews component', () => { - const { getByTestId } = setup(); - expect(getByTestId('InfoOutlinedIcon')).toBeInTheDocument(); + setup(); + expect(screen.getByTestId('InfoOutlinedIcon')).toBeInTheDocument(); }); }); describe('CycleTime Selector List', () => { it('should show selectors title when render Crews component', () => { - const { getByText } = setup(); + setup(); - expect(getByText('Analysis, In Dev, doing')).toBeInTheDocument(); - expect(getByText('Test')).toBeInTheDocument(); - expect(getByText('To do')).toBeInTheDocument(); + expect(screen.getByText('Analysis, In Dev, doing')).toBeInTheDocument(); + expect(screen.getByText('Test')).toBeInTheDocument(); + expect(screen.getByText('To do')).toBeInTheDocument(); }); it('should always show board status column tooltip', async () => { - const { getByText, getByRole } = setup(); - userEvent.hover(getByText('Analysis, In Dev, doing')); + setup(); + userEvent.hover(screen.getByText('Analysis, In Dev, doing')); await waitFor(() => { - expect(getByRole('tooltip', { name: 'Analysis, In Dev, doing' })).toBeVisible(); + expect(screen.getByRole('tooltip', { name: 'Analysis, In Dev, doing' })).toBeVisible(); }); }); it('should show right input value when initializing', async () => { - const { getAllByRole } = setup(); - const inputElements = getAllByRole('combobox'); + setup(); + const inputElements = screen.getAllByRole('combobox'); const selectedInputValues = inputElements.map((input) => input.getAttribute('value')); const expectedInputValues = ['Analysis', 'Review', NO_RESULT_DASH]; @@ -100,12 +101,12 @@ describe('CycleTime', () => { }); it('should show detail options when click included button', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.click(columnsArray[0]); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); const options = listBox.getAllByRole('option'); const optionText = options.map((option) => option.textContent); @@ -127,12 +128,12 @@ describe('CycleTime', () => { }); it('should show the right options when input the keyword to search', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.type(columnsArray[0], 'Done'); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); const options = listBox.getAllByRole('option'); const optionTexts = options.map((option) => option.textContent); @@ -142,23 +143,23 @@ describe('CycleTime', () => { }); it('should show no options when enter the wrong keyword', async () => { - const { getAllByRole, getByText } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.type(columnsArray[0], 'wrong keyword'); }); - expect(getByText('No options')).toBeInTheDocument(); + expect(screen.getByText('No options')).toBeInTheDocument(); }); it('should show selected option when click the dropDown button ', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.click(columnsArray[2]); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); const options = listBox.getAllByRole('option'); const selectedOption = options.find((option) => option.getAttribute('aria-selected') === 'true'); @@ -168,19 +169,19 @@ describe('CycleTime', () => { }); it('should show other selections when change option and will not affect Real done', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.click(columnsArray[2]); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); const mockOptions = listBox.getAllByRole('option'); await act(async () => { await userEvent.click(mockOptions[1]); }); - const inputElements = getAllByRole('combobox'); + const inputElements = screen.getAllByRole('combobox'); const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[2]; expect(selectedInputValue).toBe('To do'); @@ -188,18 +189,18 @@ describe('CycleTime', () => { }); it('should reset Real done when marked as done from other options', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.click(columnsArray[0]); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); await act(async () => { await userEvent.click(listBox.getAllByRole('option')[8]); }); - const inputElements = getAllByRole('combobox'); + const inputElements = screen.getAllByRole('combobox'); const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0]; @@ -208,13 +209,13 @@ describe('CycleTime', () => { }); it('should show the right selected value when cancel the done', async () => { - const { getAllByRole, getByRole } = setup(); - const columnsArray = getAllByRole('button', { name: LIST_OPEN }); + setup(); + const columnsArray = screen.getAllByRole('button', { name: LIST_OPEN }); await act(async () => { await userEvent.click(columnsArray[0]); }); - const listBox = within(getByRole('listbox')); + const listBox = within(screen.getByRole('listbox')); await act(async () => { await userEvent.click(listBox.getAllByRole('option')[8]); }); @@ -223,12 +224,12 @@ describe('CycleTime', () => { await userEvent.click(columnsArray[0]); }); - const newListBox = within(getByRole('listbox')); + const newListBox = within(screen.getByRole('listbox')); await act(async () => { await userEvent.click(newListBox.getAllByRole('option')[7]); }); - const inputElements = getAllByRole('combobox'); + const inputElements = screen.getAllByRole('combobox'); const selectedInputValue = inputElements.map((option) => option.getAttribute('value'))[0]; expect(selectedInputValue).toBe('Review'); @@ -238,19 +239,19 @@ describe('CycleTime', () => { describe('CycleTime Flag as Block', () => { it('should show FlagAsBlock when render Crews component', () => { - const { getByText } = setup(); - expect(getByText(FlagAsBlock)).toBeInTheDocument(); + setup(); + expect(screen.getByText(FlagAsBlock)).toBeInTheDocument(); }); it('should be checked by default when initializing', () => { - const { getByRole } = setup(); - expect(getByRole('checkbox')).toHaveProperty('checked', true); + setup(); + expect(screen.getByRole('checkbox')).toHaveProperty('checked', true); }); it('should change checked when click', async () => { - const { getByRole } = setup(); + setup(); await act(async () => { - await userEvent.click(getByRole('checkbox')); + await userEvent.click(screen.getByRole('checkbox')); }); await waitFor(() => { @@ -260,21 +261,21 @@ describe('CycleTime', () => { }); it('should show warning message when selectWarningMessage has a value in cycleTime component', () => { - const { getByText } = setup(); + setup(); - expect(getByText('Test warning Message')).toBeVisible(); + expect(screen.getByText('Test warning Message')).toBeVisible(); }); it('should show disable warning message when selectWarningMessage has a value after two seconds in cycleTime component', async () => { jest.useFakeTimers(); - const { queryByText } = setup(); + setup(); act(() => { jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + expect(screen.queryByText('Test warning Message')).not.toBeInTheDocument(); }); }); }); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx index 9f07fcc210..f8e4d7ee68 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection.test.tsx @@ -1,4 +1,4 @@ -import { act, render, waitFor, within } from '@testing-library/react'; +import { act, render, waitFor, within, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Provider } from 'react-redux'; import { setupStore } from '../../../../utils/setupStoreUtil'; @@ -86,10 +86,10 @@ describe('PipelineMetricSelection', () => { }); it('should render PipelineMetricSelection when isShowRemoveButton is true', async () => { - const { getByText } = await setup(deploymentFrequencySetting, true, false); + await setup(deploymentFrequencySetting, true, false); - expect(getByText(REMOVE_BUTTON)).toBeInTheDocument(); - expect(getByText(ORGANIZATION)).toBeInTheDocument(); + expect(screen.getByText(REMOVE_BUTTON)).toBeInTheDocument(); + expect(screen.getByText(ORGANIZATION)).toBeInTheDocument(); }); it('should render PipelineMetricSelection when isShowRemoveButton is false', async () => { diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx index 9a16a03aeb..057785800d 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event'; import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; import { act } from 'react-dom/test-utils'; @@ -27,23 +27,23 @@ describe('MultiAutoComplete', () => { ); it('renders the component', () => { - const { getByTestId } = setup(); + setup(); - expect(getByTestId(testId)).toBeInTheDocument(); + expect(screen.getByTestId(testId)).toBeInTheDocument(); }); it('When passed selectedoption changed, the correct option would be displayed', async () => { - const { getByRole } = setup(); + setup(); - expect(getByRole('button', { name: 'Option 1' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Option 1' })).toBeVisible(); }); it('When user select All option, all options in drop box would be selected', async () => { - const { getByRole } = setup(); + setup(); - const inputField = getByRole('combobox'); + const inputField = screen.getByRole('combobox'); await userEvent.click(inputField); - const allOption = getByRole('option', { name: 'All' }); + const allOption = screen.getByRole('option', { name: 'All' }); await act(async () => { await userEvent.click(allOption); }); From df752c1a35f16611a84158368061eecfcb22de1d Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 14:35:38 +0800 Subject: [PATCH 84/90] [frontend] fix codacy again --- .../src/components/Metrics/ConfigStep/ConfigStep.test.tsx | 4 ++-- .../src/components/Metrics/MetricsStep/Crews.test.tsx | 2 +- .../Metrics/MetricsStep/MultiAutoComplete.test.tsx | 2 +- frontend/cypress/plugins/clearDownloadFile.ts | 2 +- frontend/scripts/runE2eWithServer.ts | 2 +- .../context/Metrics/{metricsSlice.tsx => metricsSlice.ts} | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) rename frontend/src/context/Metrics/{metricsSlice.tsx => metricsSlice.ts} (99%) diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx index 05b25e006a..10a77ba55b 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx @@ -175,14 +175,14 @@ describe('ConfigStep', () => { }); it('should show disable warning message When selectWarningMessage has a value after two seconds', async () => { - const { queryByText } = setup(); + setup(); act(() => { jest.advanceTimersByTime(ERROR_MESSAGE_TIME_DURATION); }); await waitFor(() => { - expect(queryByText('Test warning Message')).not.toBeInTheDocument(); + expect(screen.queryByText('Test warning Message')).not.toBeInTheDocument(); }); }); }); diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx index 6a15bb402f..19566e5262 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/Crews.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { act, render, screen, waitFor, within } from '@testing-library/react' +import { act, render, screen, waitFor, within } from '@testing-library/react'; import { Crews } from '@src/components/Metrics/MetricsStep/Crews'; import userEvent from '@testing-library/user-event'; import { setupStore } from '../../../utils/setupStoreUtil'; diff --git a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx index 057785800d..86d62fc7ec 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStep/MultiAutoComplete.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react' +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import MultiAutoComplete from '@src/components/Common/MultiAutoComplete'; import { act } from 'react-dom/test-utils'; diff --git a/frontend/cypress/plugins/clearDownloadFile.ts b/frontend/cypress/plugins/clearDownloadFile.ts index 06014c68de..6c7b665bc1 100644 --- a/frontend/cypress/plugins/clearDownloadFile.ts +++ b/frontend/cypress/plugins/clearDownloadFile.ts @@ -1,7 +1,7 @@ // import fs = require('fs'); -module.exports = (on, config) => { +module.exports = (on) => { on('task', { clearDownloads: () => { const downloadsFolder = 'cypress/downloads'; diff --git a/frontend/scripts/runE2eWithServer.ts b/frontend/scripts/runE2eWithServer.ts index a8ea445364..929fa2da06 100644 --- a/frontend/scripts/runE2eWithServer.ts +++ b/frontend/scripts/runE2eWithServer.ts @@ -138,6 +138,6 @@ const main = async (args: string[]) => { }); }; -const [_, __, ...args] = process.argv; +const args = process.argv.slice(2); main(args); diff --git a/frontend/src/context/Metrics/metricsSlice.tsx b/frontend/src/context/Metrics/metricsSlice.ts similarity index 99% rename from frontend/src/context/Metrics/metricsSlice.tsx rename to frontend/src/context/Metrics/metricsSlice.ts index 69cafe3f2a..bca622e51d 100644 --- a/frontend/src/context/Metrics/metricsSlice.tsx +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -442,19 +442,19 @@ export const selectCycleTimeWarningMessage = (state: RootState) => state.metrics export const selectClassificationWarningMessage = (state: RootState) => state.metrics.classificationWarningMessage; export const selectRealDoneWarningMessage = (state: RootState) => state.metrics.realDoneWarningMessage; -export const selectOrganizationWarningMessage = (state: RootState, id: number, type: string) => { +export const selectOrganizationWarningMessage = (state: RootState, id: number) => { const { deploymentWarningMessage } = state.metrics; const warningMessage = deploymentWarningMessage; return warningMessage.find((item) => item.id === id)?.organization; }; -export const selectPipelineNameWarningMessage = (state: RootState, id: number, type: string) => { +export const selectPipelineNameWarningMessage = (state: RootState, id: number) => { const { deploymentWarningMessage } = state.metrics; const warningMessage = deploymentWarningMessage; return warningMessage.find((item) => item.id === id)?.pipelineName; }; -export const selectStepWarningMessage = (state: RootState, id: number, type: string) => { +export const selectStepWarningMessage = (state: RootState, id: number) => { const { deploymentWarningMessage } = state.metrics; const warningMessage = deploymentWarningMessage; return warningMessage.find((item) => item.id === id)?.step; From e266b13e705ef73b313953329c66abb78638f942 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 15:07:23 +0800 Subject: [PATCH 85/90] [frontend] fix testing --- frontend/__tests__/src/context/metricsSlice.test.ts | 12 ++++++------ .../PipelineMetricSelection/index.tsx | 6 +++--- frontend/src/context/Metrics/metricsSlice.ts | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/frontend/__tests__/src/context/metricsSlice.test.ts b/frontend/__tests__/src/context/metricsSlice.test.ts index 2090963088..ab6550c2e5 100644 --- a/frontend/__tests__/src/context/metricsSlice.test.ts +++ b/frontend/__tests__/src/context/metricsSlice.test.ts @@ -903,28 +903,28 @@ describe('saveMetricsSetting reducer', () => { }); it('should return organization warning message given its id and type', () => { expect( - selectOrganizationWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectOrganizationWarningMessage(store.getState(), 0) ).toEqual(ORGANIZATION_WARNING_MESSAGE); expect( - selectOrganizationWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectOrganizationWarningMessage(store.getState(), 1) ).toBeNull(); }); it('should return pipelineName warning message given its id and type', () => { expect( - selectPipelineNameWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectPipelineNameWarningMessage(store.getState(), 0) ).toBeNull(); expect( - selectPipelineNameWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectPipelineNameWarningMessage(store.getState(), 1) ).toEqual(PIPELINE_NAME_WARNING_MESSAGE); }); it('should return step warning message given its id and type', () => { expect( - selectStepWarningMessage(store.getState(), 0, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectStepWarningMessage(store.getState(), 0) ).toBeNull(); expect( - selectStepWarningMessage(store.getState(), 1, PIPELINE_SETTING_TYPES.DEPLOYMENT_FREQUENCY_SETTINGS_TYPE) + selectStepWarningMessage(store.getState(), 1) ).toEqual(STEP_WARNING_MESSAGE); }); }); diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx index e0cdc70160..d5ecbb57d2 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PipelineMetricSelection/index.tsx @@ -52,9 +52,9 @@ export const PipelineMetricSelection = ({ const organizationNameOptions = selectPipelineOrganizations(store.getState()); const pipelineNameOptions = selectPipelineNames(store.getState(), organization); const stepsOptions = selectSteps(store.getState(), organization, pipelineName); - const organizationWarningMessage = selectOrganizationWarningMessage(store.getState(), id, type); - const pipelineNameWarningMessage = selectPipelineNameWarningMessage(store.getState(), id, type); - const stepWarningMessage = selectStepWarningMessage(store.getState(), id, type); + const organizationWarningMessage = selectOrganizationWarningMessage(store.getState(), id); + const pipelineNameWarningMessage = selectPipelineNameWarningMessage(store.getState(), id); + const stepWarningMessage = selectStepWarningMessage(store.getState(), id); const [isShowNoStepWarning, setIsShowNoStepWarning] = useState(false); const handleRemoveClick = () => { diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index bca622e51d..bacdd7e7e5 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -432,7 +432,6 @@ export const { } = metricsSlice.actions; export const selectDeploymentFrequencySettings = (state: RootState) => state.metrics.deploymentFrequencySettings; -export const selectLeadTimeForChanges = (state: RootState) => state.metrics.leadTimeForChanges; export const selectCycleTimeSettings = (state: RootState) => state.metrics.cycleTimeSettings; export const selectMetricsContent = (state: RootState) => state.metrics; From ddf1a158026f4ecda9e5060c4ae8d574ecd7ad69 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 15:10:08 +0800 Subject: [PATCH 86/90] [frontend] fix lint --- .../src/context/metricsSlice.test.ts | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/frontend/__tests__/src/context/metricsSlice.test.ts b/frontend/__tests__/src/context/metricsSlice.test.ts index ab6550c2e5..ec83d9e548 100644 --- a/frontend/__tests__/src/context/metricsSlice.test.ts +++ b/frontend/__tests__/src/context/metricsSlice.test.ts @@ -902,30 +902,18 @@ describe('saveMetricsSetting reducer', () => { jest.clearAllMocks(); }); it('should return organization warning message given its id and type', () => { - expect( - selectOrganizationWarningMessage(store.getState(), 0) - ).toEqual(ORGANIZATION_WARNING_MESSAGE); - expect( - selectOrganizationWarningMessage(store.getState(), 1) - ).toBeNull(); + expect(selectOrganizationWarningMessage(store.getState(), 0)).toEqual(ORGANIZATION_WARNING_MESSAGE); + expect(selectOrganizationWarningMessage(store.getState(), 1)).toBeNull(); }); it('should return pipelineName warning message given its id and type', () => { - expect( - selectPipelineNameWarningMessage(store.getState(), 0) - ).toBeNull(); - expect( - selectPipelineNameWarningMessage(store.getState(), 1) - ).toEqual(PIPELINE_NAME_WARNING_MESSAGE); + expect(selectPipelineNameWarningMessage(store.getState(), 0)).toBeNull(); + expect(selectPipelineNameWarningMessage(store.getState(), 1)).toEqual(PIPELINE_NAME_WARNING_MESSAGE); }); it('should return step warning message given its id and type', () => { - expect( - selectStepWarningMessage(store.getState(), 0) - ).toBeNull(); - expect( - selectStepWarningMessage(store.getState(), 1) - ).toEqual(STEP_WARNING_MESSAGE); + expect(selectStepWarningMessage(store.getState(), 0)).toBeNull(); + expect(selectStepWarningMessage(store.getState(), 1)).toEqual(STEP_WARNING_MESSAGE); }); }); }); From fbfe6b87103844fb3530c6daf3efe604e1343cc8 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 17:21:49 +0800 Subject: [PATCH 87/90] [frontend] rebase main --- .../MetricsStepper/MetricsStepper.test.tsx | 336 +++++++-------- .../ReportStep/ReportDetail/board.test.tsx | 144 +++---- .../ReportStep/ReportDetail/dora.test.tsx | 140 +++---- .../ReportStep/ReportDetail/withBack.test.tsx | 38 +- .../Metrics/ReportStep/ReportStep.test.tsx | 388 +++++++++--------- frontend/cypress.config.ts | 2 +- frontend/cypress/e2e/createANewProject.cy.ts | 42 +- frontend/cypress/pages/metrics/report.ts | 14 +- frontend/src/clients/report/dto/response.ts | 160 ++++---- .../Common/ReportGrid/ReportTitle/style.tsx | 12 +- .../components/Metrics/MetricsStep/style.tsx | 14 +- .../Metrics/ReportButtonGroup/index.tsx | 46 +-- .../Metrics/ReportButtonGroup/style.tsx | 14 +- .../Metrics/ReportStep/BoradMetrics/index.tsx | 52 +-- .../Metrics/ReportStep/BoradMetrics/style.tsx | 4 +- .../Metrics/ReportStep/DoraMetrics/index.tsx | 118 +++--- .../Metrics/ReportStep/DoraMetrics/style.tsx | 4 +- .../Metrics/ReportStep/ReportDetail/board.tsx | 30 +- .../Metrics/ReportStep/ReportDetail/dora.tsx | 28 +- .../Metrics/ReportStep/ReportDetail/index.ts | 4 +- .../ReportStep/ReportDetail/withBack.tsx | 16 +- .../components/Metrics/ReportStep/index.tsx | 134 +++--- frontend/src/constants/resources.ts | 6 +- frontend/src/context/config/configSlice.ts | 24 +- frontend/src/hooks/useGenerateReportEffect.ts | 12 +- frontend/src/utils/types.ts | 4 +- 26 files changed, 893 insertions(+), 893 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx index 119c43661a..9b7fae3ba7 100644 --- a/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx +++ b/frontend/__tests__/src/components/Metrics/MetricsStepper/MetricsStepper.test.tsx @@ -1,7 +1,7 @@ -import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import MetricsStepper from '@src/components/Metrics/MetricsStepper' -import { Provider } from 'react-redux' -import { setupStore } from '../../../utils/setupStoreUtil' +import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import MetricsStepper from '@src/components/Metrics/MetricsStepper'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../utils/setupStoreUtil'; import { BASE_PAGE_ROUTE, BOARD_TYPES, @@ -16,8 +16,8 @@ import { STEPPER, TEST_PROJECT_NAME, VELOCITY, -} from '../../../fixtures' -import userEvent from '@testing-library/user-event' +} from '../../../fixtures'; +import userEvent from '@testing-library/user-event'; import { updateBoard, updateBoardVerifyState, @@ -26,12 +26,12 @@ import { updatePipelineToolVerifyState, updateSourceControl, updateSourceControlVerifyState, -} from '@src/context/config/configSlice' -import dayjs from 'dayjs' -import { navigateMock } from '../../../../setupTests' -import { setupServer } from 'msw/node' -import { rest } from 'msw' -import { HttpStatusCode } from 'axios' +} from '@src/context/config/configSlice'; +import dayjs from 'dayjs'; +import { navigateMock } from '../../../../setupTests'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { HttpStatusCode } from 'axios'; import { saveCycleTimeSettings, saveDoneColumn, @@ -39,19 +39,19 @@ import { saveUsers, updateDeploymentFrequencySettings, updateTreatFlagCardAsBlock, -} from '@src/context/Metrics/metricsSlice' -import { exportToJsonFile } from '@src/utils/util' -import { ASSIGNEE_FILTER_TYPES } from '@src/constants/resources' - -const START_DATE_LABEL = 'From *' -const TODAY = dayjs() -const INPUT_DATE_VALUE = TODAY.format('MM/DD/YYYY') -const END_DATE_LABEL = 'To *' -const YES = 'Yes' -const CANCEL = 'Cancel' -const METRICS = 'Metrics' -const REPORT = 'Report' -const stepperColor = 'rgba(0, 0, 0, 0.87)' +} from '@src/context/Metrics/metricsSlice'; +import { exportToJsonFile } from '@src/utils/util'; +import { ASSIGNEE_FILTER_TYPES } from '@src/constants/resources'; + +const START_DATE_LABEL = 'From *'; +const TODAY = dayjs(); +const INPUT_DATE_VALUE = TODAY.format('MM/DD/YYYY'); +const END_DATE_LABEL = 'To *'; +const YES = 'Yes'; +const CANCEL = 'Cancel'; +const METRICS = 'Metrics'; +const REPORT = 'Report'; +const stepperColor = 'rgba(0, 0, 0, 0.87)'; const mockValidationCheckContext = { deploymentFrequencySettingsErrorMessages: [], @@ -60,11 +60,11 @@ const mockValidationCheckContext = { checkDuplicatedPipeline: jest.fn(), isPipelineValid: jest.fn().mockReturnValue(true), getDuplicatedPipeLineIds: jest.fn().mockReturnValue([]), -} +}; jest.mock('@src/hooks/useMetricsStepValidationCheckContext', () => ({ useMetricsStepValidationCheckContext: () => mockValidationCheckContext, -})) +})); jest.mock('@src/context/config/configSlice', () => ({ ...jest.requireActual('@src/context/config/configSlice'), @@ -75,12 +75,12 @@ jest.mock('@src/context/config/configSlice', () => ({ updateSourceControl: jest.fn().mockReturnValue({ type: 'UPDATE_SOURCE_CONTROL' }), updatePipelineTool: jest.fn().mockReturnValue({ type: 'UPDATE_PIPELINE_TOOL' }), updateBoard: jest.fn().mockReturnValue({ type: 'UPDATE_BOARD' }), -})) +})); jest.mock('@src/emojis/emoji', () => ({ getEmojiUrls: jest.fn().mockReturnValue(['https://buildkiteassets.com/emojis/img-buildkite-64/charger64.png']), removeExtraEmojiName: jest.fn(), -})) +})); jest.mock('@src/utils/util', () => ({ exportToJsonFile: jest.fn(), @@ -89,7 +89,7 @@ jest.mock('@src/utils/util', () => ({ findCaseInsensitiveType: jest.fn(), filterAndMapCycleTimeSettings: jest.fn(), formatDate: jest.fn(), -})) +})); jest.mock('@src/hooks/useGenerateReportEffect', () => ({ useGenerateReportEffect: jest.fn().mockReturnValue({ @@ -99,33 +99,33 @@ jest.mock('@src/hooks/useGenerateReportEffect', () => ({ isServerError: false, errorMessage: '', }), -})) +})); -const server = setupServer(rest.post(MOCK_REPORT_URL, (_, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))) +const server = setupServer(rest.post(MOCK_REPORT_URL, (_, res, ctx) => res(ctx.status(HttpStatusCode.Ok)))); -const mockLocation = { reload: jest.fn() } -Object.defineProperty(window, 'location', { value: mockLocation }) +const mockLocation = { reload: jest.fn() }; +Object.defineProperty(window, 'location', { value: mockLocation }); -let store = setupStore() +let store = setupStore(); const fillConfigPageData = async () => { - const projectNameInput = await screen.findByRole('textbox', { name: PROJECT_NAME_LABEL }) - fireEvent.change(projectNameInput, { target: { value: TEST_PROJECT_NAME } }) - const startDateInput = (await screen.findByRole('textbox', { name: START_DATE_LABEL })) as HTMLInputElement - fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }) + const projectNameInput = await screen.findByRole('textbox', { name: PROJECT_NAME_LABEL }); + fireEvent.change(projectNameInput, { target: { value: TEST_PROJECT_NAME } }); + const startDateInput = (await screen.findByRole('textbox', { name: START_DATE_LABEL })) as HTMLInputElement; + fireEvent.change(startDateInput, { target: { value: INPUT_DATE_VALUE } }); act(() => { - store.dispatch(updateMetrics([VELOCITY])) - store.dispatch(updateBoardVerifyState(true)) - store.dispatch(updatePipelineToolVerifyState(true)) - store.dispatch(updateSourceControlVerifyState(true)) - }) -} + store.dispatch(updateMetrics([VELOCITY])); + store.dispatch(updateBoardVerifyState(true)); + store.dispatch(updatePipelineToolVerifyState(true)); + store.dispatch(updateSourceControlVerifyState(true)); + }); +}; const fillMetricsData = () => { act(() => { - store.dispatch(updateMetrics([VELOCITY])) - }) -} + store.dispatch(updateMetrics([VELOCITY])); + }); +}; const fillMetricsPageDate = async () => { await act(async () => { @@ -142,148 +142,148 @@ const fillMetricsPageDate = async () => { updateDeploymentFrequencySettings({ updateId: 0, label: 'pipelineName', value: 'mock new pipelineName' }) ), store.dispatch(updateDeploymentFrequencySettings({ updateId: 0, label: 'step', value: 'mock new step' })), - ]) - }) -} + ]); + }); +}; describe('MetricsStepper', () => { - beforeAll(() => server.listen()) - afterAll(() => server.close()) + beforeAll(() => server.listen()); + afterAll(() => server.close()); beforeEach(() => { - store = setupStore() - }) + store = setupStore(); + }); afterEach(() => { - navigateMock.mockClear() - }) + navigateMock.mockClear(); + }); const setup = () => render( - ) + ); it('should show metrics stepper', () => { - setup() + setup(); STEPPER.map((label) => { - expect(screen.getByText(label)).toBeInTheDocument() - }) + expect(screen.getByText(label)).toBeInTheDocument(); + }); - expect(screen.getByText(NEXT)).toBeInTheDocument() - expect(screen.getByText(PREVIOUS)).toBeInTheDocument() - }) + expect(screen.getByText(NEXT)).toBeInTheDocument(); + expect(screen.getByText(PREVIOUS)).toBeInTheDocument(); + }); it('should show metrics config step when click back button given config step ', async () => { - setup() + setup(); - await userEvent.click(screen.getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)); - expect(screen.getByText(PROJECT_NAME_LABEL)).toBeInTheDocument() - }) + expect(screen.getByText(PROJECT_NAME_LABEL)).toBeInTheDocument(); + }); it('should show confirm dialog when click back button in config page', async () => { - setup() + setup(); - await userEvent.click(screen.getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)); - expect(screen.getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument() - }) + expect(screen.getByText(CONFIRM_DIALOG_DESCRIPTION)).toBeInTheDocument(); + }); it('should close confirm dialog when click cancel button', async () => { - setup() + setup(); - await userEvent.click(screen.getByText(PREVIOUS)) - await userEvent.click(screen.getByText(CANCEL)) + await userEvent.click(screen.getByText(PREVIOUS)); + await userEvent.click(screen.getByText(CANCEL)); - expect(screen.queryByText(CONFIRM_DIALOG_DESCRIPTION)).not.toBeInTheDocument() - }) + expect(screen.queryByText(CONFIRM_DIALOG_DESCRIPTION)).not.toBeInTheDocument(); + }); it('should go to home page when click Yes button', async () => { - setup() + setup(); - await userEvent.click(screen.getByText(PREVIOUS)) + await userEvent.click(screen.getByText(PREVIOUS)); - expect(screen.getByText(YES)).toBeVisible() + expect(screen.getByText(YES)).toBeVisible(); - await userEvent.click(screen.getByText(YES)) + await userEvent.click(screen.getByText(YES)); - expect(navigateMock).toHaveBeenCalledTimes(1) - expect(navigateMock).toHaveBeenCalledWith(BASE_PAGE_ROUTE) - }) + expect(navigateMock).toHaveBeenCalledTimes(1); + expect(navigateMock).toHaveBeenCalledWith(BASE_PAGE_ROUTE); + }); it('should disable next when required data is empty ', async () => { - setup() + setup(); act(() => { - store.dispatch(updateMetrics([])) - }) + store.dispatch(updateMetrics([])); + }); - expect(screen.getByText(NEXT)).toBeDisabled() - }) + expect(screen.getByText(NEXT)).toBeDisabled(); + }); it('should disable next when dataRange is empty ', async () => { - setup() - await fillConfigPageData() + setup(); + await fillConfigPageData(); - const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement - const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const startDateInput = screen.getByRole('textbox', { name: START_DATE_LABEL }) as HTMLInputElement; + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; - await userEvent.clear(startDateInput) - await userEvent.clear(endDateInput) + await userEvent.clear(startDateInput); + await userEvent.clear(endDateInput); - expect(screen.getByText(NEXT)).toBeDisabled() - }, 50000) + expect(screen.getByText(NEXT)).toBeDisabled(); + }, 50000); it('should disable next when endDate is empty ', async () => { - setup() - await fillConfigPageData() + setup(); + await fillConfigPageData(); - const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement + const endDateInput = screen.getByRole('textbox', { name: END_DATE_LABEL }) as HTMLInputElement; - await userEvent.clear(endDateInput) + await userEvent.clear(endDateInput); - expect(screen.getByText(NEXT)).toBeDisabled() - }) + expect(screen.getByText(NEXT)).toBeDisabled(); + }); it('should enable next when every selected component is show and verified', async () => { - setup() - await fillConfigPageData() + setup(); + await fillConfigPageData(); - expect(screen.getByText(NEXT)).toBeEnabled() - }) + expect(screen.getByText(NEXT)).toBeEnabled(); + }); it('should disable next when board component is exist but not verified successfully', async () => { - setup() + setup(); act(() => { - store.dispatch(updateMetrics([VELOCITY])) - store.dispatch(updateBoardVerifyState(false)) - }) + store.dispatch(updateMetrics([VELOCITY])); + store.dispatch(updateBoardVerifyState(false)); + }); - expect(screen.getByText(NEXT)).toBeDisabled() - }) + expect(screen.getByText(NEXT)).toBeDisabled(); + }); it('should go metrics page when click next button given next button enabled', async () => { - setup() + setup(); - await fillConfigPageData() - fireEvent.click(screen.getByText(NEXT)) + await fillConfigPageData(); + fireEvent.click(screen.getByText(NEXT)); - expect(screen.getByText(METRICS)).toHaveStyle(`color:${stepperColor}`) - }) + expect(screen.getByText(METRICS)).toHaveStyle(`color:${stepperColor}`); + }); it('should show metrics export step when click next button given export step', async () => { - setup() - await fillConfigPageData() - await userEvent.click(screen.getByText(NEXT)) - await fillMetricsPageDate() + setup(); + await fillConfigPageData(); + await userEvent.click(screen.getByText(NEXT)); + await fillMetricsPageDate(); waitFor(() => { - expect(screen.getByText(NEXT)).toBeInTheDocument() - }) - await userEvent.click(screen.getByText(NEXT)) + expect(screen.getByText(NEXT)).toBeInTheDocument(); + }); + await userEvent.click(screen.getByText(NEXT)); - expect(screen.getByText(REPORT)).toHaveStyle(`color:${stepperColor}`) - }) + expect(screen.getByText(REPORT)).toHaveStyle(`color:${stepperColor}`); + }); it('should export json when click save button', async () => { - const expectedFileName = 'config' + const expectedFileName = 'config'; const expectedJson = { board: undefined, calendarType: 'Regular Calendar(Weekend Considered)', @@ -295,16 +295,16 @@ describe('MetricsStepper', () => { pipelineTool: undefined, projectName: '', sourceControl: undefined, - } - setup() + }; + setup(); - await userEvent.click(screen.getByText(SAVE)) + await userEvent.click(screen.getByText(SAVE)); - expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) - }) + expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson); + }); it('should export json when click save button when pipelineTool, sourceControl, and board is not empty', async () => { - const expectedFileName = 'config' + const expectedFileName = 'config'; const expectedJson = { board: { boardId: '', email: '', projectKey: '', site: '', token: '', type: 'Jira' }, calendarType: 'Regular Calendar(Weekend Considered)', @@ -316,18 +316,18 @@ describe('MetricsStepper', () => { pipelineTool: undefined, projectName: '', sourceControl: undefined, - } + }; - setup() - await fillMetricsData() + setup(); + await fillMetricsData(); - await userEvent.click(screen.getByText(SAVE)) + await userEvent.click(screen.getByText(SAVE)); - expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) - }) + expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson); + }); it('should export json file when click save button in metrics page given all content is empty', async () => { - const expectedFileName = 'config' + const expectedFileName = 'config'; const expectedJson = { assigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, board: { boardId: '', email: '', projectKey: '', site: '', token: '', type: 'Jira' }, @@ -346,18 +346,18 @@ describe('MetricsStepper', () => { deployment: undefined, doneStatus: undefined, leadTime: undefined, - } - setup() + }; + setup(); - await fillConfigPageData() - await userEvent.click(screen.getByText(NEXT)) - await userEvent.click(screen.getByText(SAVE)) + await fillConfigPageData(); + await userEvent.click(screen.getByText(NEXT)); + await userEvent.click(screen.getByText(SAVE)); - expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) - }, 50000) + expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson); + }, 50000); it('should export json file when click save button in report page given all content is empty', async () => { - const expectedFileName = 'config' + const expectedFileName = 'config'; const expectedJson = { assigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, board: { boardId: '', email: '', projectKey: '', site: '', token: '', type: 'Jira' }, @@ -376,26 +376,26 @@ describe('MetricsStepper', () => { deployment: undefined, doneStatus: undefined, leadTime: undefined, - } + }; - setup() - await fillConfigPageData() - await userEvent.click(screen.getByText(NEXT)) - await fillMetricsPageDate() + setup(); + await fillConfigPageData(); + await userEvent.click(screen.getByText(NEXT)); + await fillMetricsPageDate(); waitFor(() => { - expect(screen.getByText(NEXT)).toBeInTheDocument() - }) - await userEvent.click(screen.getByText(NEXT)) - await userEvent.click(screen.getByText(SAVE)) + expect(screen.getByText(NEXT)).toBeInTheDocument(); + }); + await userEvent.click(screen.getByText(NEXT)); + await userEvent.click(screen.getByText(SAVE)); - expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson) - }, 50000) + expect(exportToJsonFile).toHaveBeenCalledWith(expectedFileName, expectedJson); + }, 50000); it('should clean the config information that is hidden when click next button', async () => { - setup() + setup(); - await fillConfigPageData() - await userEvent.click(screen.getByText(NEXT)) + await fillConfigPageData(); + await userEvent.click(screen.getByText(NEXT)); expect(updateBoard).not.toHaveBeenCalledWith({ type: BOARD_TYPES.JIRA, @@ -404,8 +404,8 @@ describe('MetricsStepper', () => { projectKey: '', site: '', token: '', - }) - expect(updateSourceControl).toHaveBeenCalledWith({ type: SOURCE_CONTROL_TYPES.GITHUB, token: '' }) - expect(updatePipelineTool).toHaveBeenCalledWith({ type: PIPELINE_TOOL_TYPES.BUILD_KITE, token: '' }) - }, 50000) -}) + }); + expect(updateSourceControl).toHaveBeenCalledWith({ type: SOURCE_CONTROL_TYPES.GITHUB, token: '' }); + expect(updatePipelineTool).toHaveBeenCalledWith({ type: PIPELINE_TOOL_TYPES.BUILD_KITE, token: '' }); + }, 50000); +}); diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx index 9004317d9b..34b4189379 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/board.test.tsx @@ -1,101 +1,101 @@ -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { render, screen, within } from '@testing-library/react' -import { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail' -import { reportMapper } from '@src/hooks/reportMapper/report' -import React from 'react' +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { render, screen, within } from '@testing-library/react'; +import { BoardDetail } from '@src/components/Metrics/ReportStep/ReportDetail'; +import { reportMapper } from '@src/hooks/reportMapper/report'; +import React from 'react'; -jest.mock('@src/hooks/reportMapper/report') +jest.mock('@src/hooks/reportMapper/report'); describe('board', () => { - const data: ReportResponseDTO = {} as ReportResponseDTO + const data: ReportResponseDTO = {} as ReportResponseDTO; - afterEach(jest.clearAllMocks) + afterEach(jest.clearAllMocks); it('should render a back link', () => { - ;(reportMapper as jest.Mock).mockReturnValue({}) - render() - expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() - expect(screen.getByText('Back')).toBeInTheDocument() - }) + (reportMapper as jest.Mock).mockReturnValue({}); + render(); + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument(); + expect(screen.getByText('Back')).toBeInTheDocument(); + }); describe('Velocity', () => { it('should show velocity when velocity data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ velocityList: [ { id: 0, name: 'name1', valueList: [{ value: 1 }] }, { id: 1, name: 'name2', valueList: [{ value: 2 }] }, ], - }) - render() - const velocityTable = screen.getByTestId('Velocity') - expect(screen.getByText('Velocity')).toBeInTheDocument() - expect(velocityTable).toBeInTheDocument() - expect(within(velocityTable).queryAllByTestId('tr').length).toBe(2) - }) + }); + render(); + const velocityTable = screen.getByTestId('Velocity'); + expect(screen.getByText('Velocity')).toBeInTheDocument(); + expect(velocityTable).toBeInTheDocument(); + expect(within(velocityTable).queryAllByTestId('tr').length).toBe(2); + }); it('should not show velocity when velocity data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ velocityList: null, - }) - render() - expect(screen.queryAllByText('Velocity').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Velocity').length).toEqual(0); + }); + }); describe('Cycle Time', () => { it('should show cycle time when cycle time data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ cycleTimeList: [ { id: 0, name: 'name1', valueList: [{ value: 1 }] }, { id: 1, name: 'name2', valueList: [{ value: 2 }] }, { id: 2, name: 'name3', valueList: [{ value: 3 }] }, ], - }) - render() - const cycleTimeTable = screen.getByTestId('Cycle Time') - expect(screen.getByText('Cycle Time')).toBeInTheDocument() - expect(cycleTimeTable).toBeInTheDocument() - expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(3) - }) + }); + render(); + const cycleTimeTable = screen.getByTestId('Cycle Time'); + expect(screen.getByText('Cycle Time')).toBeInTheDocument(); + expect(cycleTimeTable).toBeInTheDocument(); + expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(3); + }); it('should not show cycle time when cycle time data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ cycleTimeList: null, - }) - render() - expect(screen.queryAllByText('Cycle Time').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Cycle Time').length).toEqual(0); + }); + }); describe('Classification', () => { it('should show classifications when classifications data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ classification: [ { id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }, { id: 1, name: 'name2', valuesList: [{ name: 'test2', value: 2 }] }, { id: 2, name: 'name3', valuesList: [{ name: 'test3', value: 3 }] }, { id: 3, name: 'name4', valuesList: [{ name: 'test4', value: 4 }] }, ], - }) + }); - render() - const classificationTable = screen.getByTestId('Classification') - expect(screen.getByText('Classification')).toBeInTheDocument() - expect(classificationTable).toBeInTheDocument() - expect(within(classificationTable).queryAllByTestId('tr').length).toBe(8) - }) + render(); + const classificationTable = screen.getByTestId('Classification'); + expect(screen.getByText('Classification')).toBeInTheDocument(); + expect(classificationTable).toBeInTheDocument(); + expect(within(classificationTable).queryAllByTestId('tr').length).toBe(8); + }); it('should not show classifications when classifications data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ classification: null, - }) - render() - expect(screen.queryAllByText('Classification').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Classification').length).toEqual(0); + }); + }); it('should show all data when all data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ velocityList: [{ id: 0, name: 'name1', valueList: [{ value: 1 }] }], cycleTimeList: [ { id: 0, name: 'name1', valueList: [{ value: 1 }] }, @@ -106,20 +106,20 @@ describe('board', () => { { id: 1, name: 'name2', valuesList: [{ name: 'test2', value: 2 }] }, { id: 2, name: 'name3', valuesList: [{ name: 'test3', value: 3 }] }, ], - }) - render() - const velocityTable = screen.getByTestId('Velocity') - const cycleTimeTable = screen.getByTestId('Cycle Time') - const classificationTable = screen.getByTestId('Classification') - expect(screen.getByText('Velocity')).toBeInTheDocument() - expect(velocityTable).toBeInTheDocument() - expect(screen.getByText('Cycle Time')).toBeInTheDocument() - expect(cycleTimeTable).toBeInTheDocument() - expect(screen.getByText('Classification')).toBeInTheDocument() - expect(classificationTable).toBeInTheDocument() + }); + render(); + const velocityTable = screen.getByTestId('Velocity'); + const cycleTimeTable = screen.getByTestId('Cycle Time'); + const classificationTable = screen.getByTestId('Classification'); + expect(screen.getByText('Velocity')).toBeInTheDocument(); + expect(velocityTable).toBeInTheDocument(); + expect(screen.getByText('Cycle Time')).toBeInTheDocument(); + expect(cycleTimeTable).toBeInTheDocument(); + expect(screen.getByText('Classification')).toBeInTheDocument(); + expect(classificationTable).toBeInTheDocument(); - expect(within(velocityTable).queryAllByTestId('tr').length).toBe(1) - expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(2) - expect(within(classificationTable).queryAllByTestId('tr').length).toBe(6) - }) -}) + expect(within(velocityTable).queryAllByTestId('tr').length).toBe(1); + expect(within(cycleTimeTable).queryAllByTestId('tr').length).toBe(2); + expect(within(classificationTable).queryAllByTestId('tr').length).toBe(6); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx index ab48c21e93..2142ba238c 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/dora.test.tsx @@ -1,103 +1,103 @@ -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { render, screen, within } from '@testing-library/react' -import { reportMapper } from '@src/hooks/reportMapper/report' -import { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail' -import React from 'react' +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { render, screen, within } from '@testing-library/react'; +import { reportMapper } from '@src/hooks/reportMapper/report'; +import { DoraDetail } from '@src/components/Metrics/ReportStep/ReportDetail'; +import React from 'react'; -jest.mock('@src/hooks/reportMapper/report') +jest.mock('@src/hooks/reportMapper/report'); describe('DoraDetail', () => { - const data: ReportResponseDTO = {} as ReportResponseDTO + const data: ReportResponseDTO = {} as ReportResponseDTO; - afterEach(jest.clearAllMocks) + afterEach(jest.clearAllMocks); it('should render a back link', () => { - ;(reportMapper as jest.Mock).mockReturnValue({}) - render() - expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() - expect(screen.getByText('Back')).toBeInTheDocument() - }) + (reportMapper as jest.Mock).mockReturnValue({}); + render(); + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument(); + expect(screen.getByText('Back')).toBeInTheDocument(); + }); describe('Deployment Frequency', () => { it('should show deploymentFrequencyList when deploymentFrequencyList data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ deploymentFrequencyList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], - }) - render() - const deploymentFrequencyTable = screen.getByTestId('Deployment Frequency') - expect(screen.getByText('Deployment Frequency')).toBeInTheDocument() - expect(deploymentFrequencyTable).toBeInTheDocument() - expect(within(deploymentFrequencyTable).queryAllByTestId('tr').length).toBe(2) - }) + }); + render(); + const deploymentFrequencyTable = screen.getByTestId('Deployment Frequency'); + expect(screen.getByText('Deployment Frequency')).toBeInTheDocument(); + expect(deploymentFrequencyTable).toBeInTheDocument(); + expect(within(deploymentFrequencyTable).queryAllByTestId('tr').length).toBe(2); + }); it('should not show deploymentFrequencyList when deploymentFrequencyList data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ deploymentFrequencyList: null, - }) - render() - expect(screen.queryAllByText('Deployment Frequency').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Deployment Frequency').length).toEqual(0); + }); + }); describe('Lead Time For Changes', () => { it('should show leadTimeForChangesList when leadTimeForChangesList data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ leadTimeForChangesList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], - }) - render() - const leadTimeForChangesTable = screen.getByTestId('Lead Time For Changes') - expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument() - expect(leadTimeForChangesTable).toBeInTheDocument() - expect(within(leadTimeForChangesTable).queryAllByTestId('tr').length).toBe(2) - }) + }); + render(); + const leadTimeForChangesTable = screen.getByTestId('Lead Time For Changes'); + expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument(); + expect(leadTimeForChangesTable).toBeInTheDocument(); + expect(within(leadTimeForChangesTable).queryAllByTestId('tr').length).toBe(2); + }); it('should not show leadTimeForChangesList when leadTimeForChangesList data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ leadTimeForChangesList: null, - }) - render() - expect(screen.queryAllByText('Lead Time For Changes').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Lead Time For Changes').length).toEqual(0); + }); + }); describe('Change Failure Rate', () => { it('should show changeFailureRateList when changeFailureRateList data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ changeFailureRateList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], - }) - render() - const changeFailureRateTable = screen.getByTestId('Change Failure Rate') - expect(screen.getByText('Change Failure Rate')).toBeInTheDocument() - expect(changeFailureRateTable).toBeInTheDocument() - expect(within(changeFailureRateTable).queryAllByTestId('tr').length).toBe(2) - }) + }); + render(); + const changeFailureRateTable = screen.getByTestId('Change Failure Rate'); + expect(screen.getByText('Change Failure Rate')).toBeInTheDocument(); + expect(changeFailureRateTable).toBeInTheDocument(); + expect(within(changeFailureRateTable).queryAllByTestId('tr').length).toBe(2); + }); it('should not show changeFailureRateList when changeFailureRateList data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ changeFailureRateList: null, - }) - render() - expect(screen.queryAllByText('Change Failure Rate').length).toEqual(0) - }) - }) + }); + render(); + expect(screen.queryAllByText('Change Failure Rate').length).toEqual(0); + }); + }); describe('Mean Time To Recovery', () => { it('should show meanTimeToRecoveryList when meanTimeToRecoveryList data is existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ meanTimeToRecoveryList: [{ id: 0, name: 'name1', valuesList: [{ name: 'test1', value: 1 }] }], - }) - render() - const meanTimeToRecoveryTable = screen.getByTestId('Mean Time To Recovery') - expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument() - expect(meanTimeToRecoveryTable).toBeInTheDocument() - expect(within(meanTimeToRecoveryTable).queryAllByTestId('tr').length).toBe(2) - }) + }); + render(); + const meanTimeToRecoveryTable = screen.getByTestId('Mean Time To Recovery'); + expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument(); + expect(meanTimeToRecoveryTable).toBeInTheDocument(); + expect(within(meanTimeToRecoveryTable).queryAllByTestId('tr').length).toBe(2); + }); it('should not show meanTimeToRecoveryList when meanTimeToRecoveryList data is not existing', () => { - ;(reportMapper as jest.Mock).mockReturnValue({ + (reportMapper as jest.Mock).mockReturnValue({ meanTimeToRecoveryList: null, - }) - render() - expect(screen.queryAllByText('Mean Time To Recovery').length).toEqual(0) - }) - }) -}) + }); + render(); + expect(screen.queryAllByText('Mean Time To Recovery').length).toEqual(0); + }); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx index 780c349995..143e7a6fa2 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportDetail/withBack.test.tsx @@ -1,28 +1,28 @@ -import { withGoBack } from '@src/components/Metrics/ReportStep/ReportDetail/withBack' -import { render, fireEvent, screen } from '@testing-library/react' -import React from 'react' +import { withGoBack } from '@src/components/Metrics/ReportStep/ReportDetail/withBack'; +import { render, fireEvent, screen } from '@testing-library/react'; +import React from 'react'; describe('withGoBack', () => { - const onBack = jest.fn() + const onBack = jest.fn(); - afterEach(jest.clearAllMocks) + afterEach(jest.clearAllMocks); it('should render a link with back', () => { - const Component = withGoBack(() =>
    {'test1'}
    ) - render() - expect(screen.getByText('Back')).toBeInTheDocument() - }) + const Component = withGoBack(() =>
    {'test1'}
    ); + render(); + expect(screen.getByText('Back')).toBeInTheDocument(); + }); it('should render the icon', () => { - const Component = withGoBack(() =>
    {'test2'}
    ) - render() - expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument() - }) + const Component = withGoBack(() =>
    {'test2'}
    ); + render(); + expect(screen.getByTestId('ArrowBackIcon')).toBeInTheDocument(); + }); it('should call onBack when the back is clicked', () => { - const Component = withGoBack(() =>
    {'test3'}
    ) - render() - fireEvent.click(screen.getByText('Back')) - expect(onBack).toBeCalled() - }) -}) + const Component = withGoBack(() =>
    {'test3'}
    ); + render(); + fireEvent.click(screen.getByText('Back')); + expect(onBack).toBeCalled(); + }); +}); diff --git a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx index eb48902314..c01aa3f2c6 100644 --- a/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ReportStep/ReportStep.test.tsx @@ -1,5 +1,5 @@ -import { render, renderHook, screen, waitFor } from '@testing-library/react' -import ReportStep from '@src/components/Metrics/ReportStep' +import { render, renderHook, screen, waitFor } from '@testing-library/react'; +import ReportStep from '@src/components/Metrics/ReportStep'; import { BACK, EMPTY_REPORT_VALUES, @@ -13,28 +13,28 @@ import { REQUIRED_DATA_LIST, SAVE, SHOW_MORE, -} from '../../../fixtures' -import { setupStore } from '../../../utils/setupStoreUtil' -import { Provider } from 'react-redux' -import { updateDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice' +} from '../../../fixtures'; +import { setupStore } from '../../../utils/setupStoreUtil'; +import { Provider } from 'react-redux'; +import { updateDeploymentFrequencySettings } from '@src/context/Metrics/metricsSlice'; import { updateJiraVerifyResponse, updateMetrics, updatePipelineToolVerifyResponse, -} from '@src/context/config/configSlice' -import userEvent from '@testing-library/user-event' -import { backStep } from '@src/context/stepper/StepperSlice' -import { navigateMock } from '../../../../setupTests' -import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' -import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect' -import React from 'react' -import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -import { MESSAGE } from '@src/constants/resources' +} from '@src/context/config/configSlice'; +import userEvent from '@testing-library/user-event'; +import { backStep } from '@src/context/stepper/StepperSlice'; +import { navigateMock } from '../../../../setupTests'; +import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect'; +import { useNotificationLayoutEffect } from '@src/hooks/useNotificationLayoutEffect'; +import React from 'react'; +import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect'; +import { MESSAGE } from '@src/constants/resources'; jest.mock('@src/context/stepper/StepperSlice', () => ({ ...jest.requireActual('@src/context/stepper/StepperSlice'), backStep: jest.fn().mockReturnValue({ type: 'BACK_STEP' }), -})) +})); jest.mock('@src/hooks/useExportCsvEffect', () => ({ useExportCsvEffect: jest.fn().mockReturnValue({ @@ -42,7 +42,7 @@ jest.mock('@src/hooks/useExportCsvEffect', () => ({ errorMessage: 'failed export csv', isExpired: false, }), -})) +})); jest.mock('@src/hooks/useGenerateReportEffect', () => ({ useGenerateReportEffect: jest.fn().mockReturnValue({ @@ -52,12 +52,12 @@ jest.mock('@src/hooks/useGenerateReportEffect', () => ({ isServerError: false, errorMessage: '', }), -})) +})); jest.mock('@src/emojis/emoji', () => ({ getEmojiUrls: jest.fn().mockReturnValue(['']), removeExtraEmojiName: jest.fn(), -})) +})); jest.mock('@src/utils/util', () => ({ transformToCleanedBuildKiteEmoji: jest.fn(), @@ -65,44 +65,44 @@ jest.mock('@src/utils/util', () => ({ filterAndMapCycleTimeSettings: jest.fn(), formatMinToHours: jest.fn().mockImplementation((time) => time / 60), formatMillisecondsToHours: jest.fn().mockImplementation((time) => time / 60 / 60 / 1000), -})) +})); -let store = null +let store = null; describe('Report Step', () => { - const { result: notificationHook } = renderHook(() => useNotificationLayoutEffect()) - const { result: reportHook } = renderHook(() => useGenerateReportEffect()) + const { result: notificationHook } = renderHook(() => useNotificationLayoutEffect()); + const { result: reportHook } = renderHook(() => useGenerateReportEffect()); beforeEach(() => { - resetReportHook() - }) + resetReportHook(); + }); afterAll(() => { - jest.clearAllMocks() - }) + jest.clearAllMocks(); + }); const resetReportHook = async () => { - reportHook.current.startToRequestBoardData = jest.fn() - reportHook.current.startToRequestDoraData = jest.fn() - reportHook.current.stopPollingReports = jest.fn() - reportHook.current.isServerError = false - reportHook.current.errorMessage = '' - reportHook.current.reportData = { ...MOCK_REPORT_RESPONSE, exportValidityTime: 30 } - } - const handleSaveMock = jest.fn() + reportHook.current.startToRequestBoardData = jest.fn(); + reportHook.current.startToRequestDoraData = jest.fn(); + reportHook.current.stopPollingReports = jest.fn(); + reportHook.current.isServerError = false; + reportHook.current.errorMessage = ''; + reportHook.current.reportData = { ...MOCK_REPORT_RESPONSE, exportValidityTime: 30 }; + }; + const handleSaveMock = jest.fn(); const setup = (params: string[]) => { - store = setupStore() + store = setupStore(); store.dispatch( updateJiraVerifyResponse({ jiraColumns: MOCK_JIRA_VERIFY_RESPONSE.jiraColumns, targetFields: MOCK_JIRA_VERIFY_RESPONSE.targetFields, users: MOCK_JIRA_VERIFY_RESPONSE.users, }) - ) - store.dispatch(updateMetrics(params)) + ); + store.dispatch(updateMetrics(params)); store.dispatch( updateDeploymentFrequencySettings({ updateId: 0, label: 'organization', value: 'mock organization' }) - ) + ); store.dispatch( updateDeploymentFrequencySettings({ updateId: 0, label: 'pipelineName', value: 'mock pipeline name' }) - ) - store.dispatch(updateDeploymentFrequencySettings({ updateId: 0, label: 'step', value: 'mock step1' })) + ); + store.dispatch(updateDeploymentFrequencySettings({ updateId: 0, label: 'step', value: 'mock step1' })); store.dispatch( updatePipelineToolVerifyResponse({ pipelineList: [ @@ -116,313 +116,313 @@ describe('Report Step', () => { }, ], }) - ) + ); return render( - ) - } + ); + }; afterEach(() => { - store = null - jest.clearAllMocks() - }) + store = null; + jest.clearAllMocks(); + }); describe('render correctly', () => { it('should render report page', () => { - setup(REQUIRED_DATA_LIST) - - expect(screen.getByText('Board Metrics')).toBeInTheDocument() - expect(screen.getByText('Velocity')).toBeInTheDocument() - expect(screen.getByText('Cycle Time')).toBeInTheDocument() - expect(screen.getByText('DORA Metrics')).toBeInTheDocument() - expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument() - expect(screen.getByText('Deployment Frequency')).toBeInTheDocument() - expect(screen.getByText('Change Failure Rate')).toBeInTheDocument() - expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument() - }) + setup(REQUIRED_DATA_LIST); + + expect(screen.getByText('Board Metrics')).toBeInTheDocument(); + expect(screen.getByText('Velocity')).toBeInTheDocument(); + expect(screen.getByText('Cycle Time')).toBeInTheDocument(); + expect(screen.getByText('DORA Metrics')).toBeInTheDocument(); + expect(screen.getByText('Lead Time For Changes')).toBeInTheDocument(); + expect(screen.getByText('Deployment Frequency')).toBeInTheDocument(); + expect(screen.getByText('Change Failure Rate')).toBeInTheDocument(); + expect(screen.getByText('Mean Time To Recovery')).toBeInTheDocument(); + }); it('should render loading page when report data is empty', () => { - reportHook.current.reportData = EMPTY_REPORT_VALUES + reportHook.current.reportData = EMPTY_REPORT_VALUES; - setup(REQUIRED_DATA_LIST) + setup(REQUIRED_DATA_LIST); - expect(screen.getAllByTestId('loading-page')).toHaveLength(6) - }) + expect(screen.getAllByTestId('loading-page')).toHaveLength(6); + }); it('should render the velocity component with correct props', async () => { - setup([REQUIRED_DATA_LIST[1]]) + setup([REQUIRED_DATA_LIST[1]]); - expect(screen.getByText('20')).toBeInTheDocument() - expect(screen.getByText('14')).toBeInTheDocument() - }) + expect(screen.getByText('20')).toBeInTheDocument(); + expect(screen.getByText('14')).toBeInTheDocument(); + }); it('should render the CycleTime component with correct props', () => { - setup([REQUIRED_DATA_LIST[2]]) + setup([REQUIRED_DATA_LIST[2]]); - expect(screen.getByText('30.26')).toBeInTheDocument() - expect(screen.getByText('21.18')).toBeInTheDocument() - }) + expect(screen.getByText('30.26')).toBeInTheDocument(); + expect(screen.getByText('21.18')).toBeInTheDocument(); + }); it('should render the Lead Time For Change component with correct props', () => { - setup([REQUIRED_DATA_LIST[4]]) + setup([REQUIRED_DATA_LIST[4]]); - expect(screen.getByText('60.79')).toBeInTheDocument() - expect(screen.getByText('39.03')).toBeInTheDocument() - expect(screen.getByText('99.82')).toBeInTheDocument() - }) + expect(screen.getByText('60.79')).toBeInTheDocument(); + expect(screen.getByText('39.03')).toBeInTheDocument(); + expect(screen.getByText('99.82')).toBeInTheDocument(); + }); it('should render the Deployment frequency component with correct props', () => { - setup([REQUIRED_DATA_LIST[5]]) + setup([REQUIRED_DATA_LIST[5]]); - expect(screen.getByText('0.40')).toBeInTheDocument() - }) + expect(screen.getByText('0.40')).toBeInTheDocument(); + }); it('should render the Change failure rate component with correct props', () => { - setup([REQUIRED_DATA_LIST[6]]) + setup([REQUIRED_DATA_LIST[6]]); - expect(screen.getByText('0.00')).toBeInTheDocument() - expect(screen.getByText('% (0/6)')).toBeInTheDocument() - }) + expect(screen.getByText('0.00')).toBeInTheDocument(); + expect(screen.getByText('% (0/6)')).toBeInTheDocument(); + }); it('should render the Mean time to recovery component with correct props', () => { - setup([REQUIRED_DATA_LIST[7]]) + setup([REQUIRED_DATA_LIST[7]]); - expect(screen.getByText('4.00')).toBeInTheDocument() - }) + expect(screen.getByText('4.00')).toBeInTheDocument(); + }); it('should show errorMessage when generating report has error message', () => { - reportHook.current.errorMessage = 'error message' + reportHook.current.errorMessage = 'error message'; - setup(['']) + setup(['']); - expect(screen.getByText('error message')).toBeInTheDocument() - }) - }) + expect(screen.getByText('error message')).toBeInTheDocument(); + }); + }); describe('behavior', () => { it('should call handleBack method when clicking back button given back button enabled', async () => { - setup(['']) + setup(['']); - const back = screen.getByText(PREVIOUS) - await userEvent.click(back) + const back = screen.getByText(PREVIOUS); + await userEvent.click(back); - expect(backStep).toHaveBeenCalledTimes(1) - }) + expect(backStep).toHaveBeenCalledTimes(1); + }); it('should call handleSaveMock method when clicking save button', async () => { - setup(['']) + setup(['']); - const save = screen.getByText(SAVE) - await userEvent.click(save) + const save = screen.getByText(SAVE); + await userEvent.click(save); - expect(handleSaveMock).toHaveBeenCalledTimes(1) - }) + expect(handleSaveMock).toHaveBeenCalledTimes(1); + }); it('should check error page show when isReportError is true', () => { - reportHook.current.isServerError = true - reportHook.current.errorMessage = 'error message' + reportHook.current.isServerError = true; + reportHook.current.errorMessage = 'error message'; - setup([REQUIRED_DATA_LIST[1]]) + setup([REQUIRED_DATA_LIST[1]]); - expect(navigateMock).toHaveBeenCalledWith(ERROR_PAGE_ROUTE) - }) + expect(navigateMock).toHaveBeenCalledWith(ERROR_PAGE_ROUTE); + }); it('should call resetProps and updateProps when remaining time is less than or equal to 5 minutes', () => { - const resetProps = jest.fn() - const updateProps = jest.fn() - notificationHook.current.resetProps = resetProps - notificationHook.current.updateProps = updateProps - jest.useFakeTimers() + const resetProps = jest.fn(); + const updateProps = jest.fn(); + notificationHook.current.resetProps = resetProps; + notificationHook.current.updateProps = updateProps; + jest.useFakeTimers(); - setup(['']) + setup(['']); - expect(resetProps).not.toBeCalled() + expect(resetProps).not.toBeCalled(); expect(updateProps).not.toBeCalledWith({ open: true, title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, - }) + }); - jest.advanceTimersByTime(500000) + jest.advanceTimersByTime(500000); expect(updateProps).not.toBeCalledWith({ open: true, title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, - }) + }); - jest.advanceTimersByTime(1000000) + jest.advanceTimersByTime(1000000); expect(updateProps).toBeCalledWith({ open: true, title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, - }) + }); - jest.useRealTimers() - }) + jest.useRealTimers(); + }); it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should render detail page when clicking show more button given metric %s', async (requiredData) => { - setup([requiredData]) + setup([requiredData]); - await userEvent.click(screen.getByText(SHOW_MORE)) + await userEvent.click(screen.getByText(SHOW_MORE)); await waitFor(() => { - expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() - }) - expect(screen.getByText(BACK)).toBeInTheDocument() + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument(); + }); + expect(screen.getByText(BACK)).toBeInTheDocument(); } - ) + ); it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should return report page when clicking back button in Breadcrumb in detail page given metric %s', async (requiredData) => { - setup([requiredData]) + setup([requiredData]); - await userEvent.click(screen.getByText(SHOW_MORE)) + await userEvent.click(screen.getByText(SHOW_MORE)); await waitFor(() => { - expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() - }) + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument(); + }); - await userEvent.click(screen.getByText(BACK)) + await userEvent.click(screen.getByText(BACK)); await waitFor(() => { - expect(screen.queryByText(BACK)).not.toBeInTheDocument() - }) - expect(screen.getByText(SHOW_MORE)).toBeInTheDocument() + expect(screen.queryByText(BACK)).not.toBeInTheDocument(); + }); + expect(screen.getByText(SHOW_MORE)).toBeInTheDocument(); } - ) + ); it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])( 'should return report page when clicking previous button in detail page given metric %s', async (requiredData) => { - setup([requiredData]) + setup([requiredData]); - const showMore = screen.getByText(SHOW_MORE) + const showMore = screen.getByText(SHOW_MORE); - await userEvent.click(showMore) + await userEvent.click(showMore); await waitFor(() => { - expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument() - }) - const previous = screen.getByText(PREVIOUS) + expect(screen.queryByText(SHOW_MORE)).not.toBeInTheDocument(); + }); + const previous = screen.getByText(PREVIOUS); - await userEvent.click(previous) + await userEvent.click(previous); await waitFor(() => { - expect(screen.getByText(SHOW_MORE)).toBeInTheDocument() - }) + expect(screen.getByText(SHOW_MORE)).toBeInTheDocument(); + }); } - ) - }) + ); + }); describe('export pipeline data', () => { it('should not show export pipeline button when not selecting deployment frequency', () => { - const { queryByText } = setup([REQUIRED_DATA_LIST[1]]) + const { queryByText } = setup([REQUIRED_DATA_LIST[1]]); - const exportPipelineButton = queryByText(EXPORT_PIPELINE_DATA) + const exportPipelineButton = queryByText(EXPORT_PIPELINE_DATA); - expect(exportPipelineButton).not.toBeInTheDocument() - }) + expect(exportPipelineButton).not.toBeInTheDocument(); + }); it.each([[REQUIRED_DATA_LIST[4]], [REQUIRED_DATA_LIST[5]], [REQUIRED_DATA_LIST[6]], [REQUIRED_DATA_LIST[7]]])( 'should show export pipeline button when selecting %s', (requiredData) => { - setup([requiredData]) + setup([requiredData]); - const exportPipelineButton = screen.getByText(EXPORT_PIPELINE_DATA) + const exportPipelineButton = screen.getByText(EXPORT_PIPELINE_DATA); - expect(exportPipelineButton).toBeInTheDocument() + expect(exportPipelineButton).toBeInTheDocument(); } - ) + ); it('should call fetchExportData when clicking "Export pipeline data"', async () => { - const { result } = renderHook(() => useExportCsvEffect()) - setup([REQUIRED_DATA_LIST[6]]) + const { result } = renderHook(() => useExportCsvEffect()); + setup([REQUIRED_DATA_LIST[6]]); - const exportButton = screen.getByText(EXPORT_PIPELINE_DATA) - expect(exportButton).toBeInTheDocument() - await userEvent.click(exportButton) + const exportButton = screen.getByText(EXPORT_PIPELINE_DATA); + expect(exportButton).toBeInTheDocument(); + await userEvent.click(exportButton); expect(result.current.fetchExportData).toBeCalledWith({ csvTimeStamp: 0, dataType: 'pipeline', endDate: '', startDate: '', - }) - }) - }) + }); + }); + }); describe('export board data', () => { it('should not show export board button when not selecting board metrics', () => { - const { queryByText } = setup([REQUIRED_DATA_LIST[4]]) + const { queryByText } = setup([REQUIRED_DATA_LIST[4]]); - const exportPipelineButton = queryByText(EXPORT_BOARD_DATA) + const exportPipelineButton = queryByText(EXPORT_BOARD_DATA); - expect(exportPipelineButton).not.toBeInTheDocument() - }) + expect(exportPipelineButton).not.toBeInTheDocument(); + }); it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[2]]])( 'should show export board button when selecting %s', (requiredData) => { - setup([requiredData]) + setup([requiredData]); - const exportPipelineButton = screen.getByText(EXPORT_BOARD_DATA) + const exportPipelineButton = screen.getByText(EXPORT_BOARD_DATA); - expect(exportPipelineButton).toBeInTheDocument() + expect(exportPipelineButton).toBeInTheDocument(); } - ) + ); it('should call fetchExportData when clicking "Export board data"', async () => { - const { result } = renderHook(() => useExportCsvEffect()) - setup([REQUIRED_DATA_LIST[2]]) + const { result } = renderHook(() => useExportCsvEffect()); + setup([REQUIRED_DATA_LIST[2]]); - const exportButton = screen.getByText(EXPORT_BOARD_DATA) - expect(exportButton).toBeInTheDocument() - await userEvent.click(exportButton) + const exportButton = screen.getByText(EXPORT_BOARD_DATA); + expect(exportButton).toBeInTheDocument(); + await userEvent.click(exportButton); expect(result.current.fetchExportData).toBeCalledWith({ csvTimeStamp: 0, dataType: 'board', endDate: '', startDate: '', - }) - }) - }) + }); + }); + }); describe('export metric data', () => { it('should show export metric button when visiting this page', () => { - setup(['']) + setup(['']); - const exportMetricButton = screen.getByText(EXPORT_METRIC_DATA) + const exportMetricButton = screen.getByText(EXPORT_METRIC_DATA); - expect(exportMetricButton).toBeInTheDocument() - }) + expect(exportMetricButton).toBeInTheDocument(); + }); it('should call fetchExportData when clicking "Export metric data"', async () => { - const { result } = renderHook(() => useExportCsvEffect()) - setup(['']) + const { result } = renderHook(() => useExportCsvEffect()); + setup(['']); - const exportButton = screen.getByText(EXPORT_METRIC_DATA) - expect(exportButton).toBeInTheDocument() - await userEvent.click(exportButton) + const exportButton = screen.getByText(EXPORT_METRIC_DATA); + expect(exportButton).toBeInTheDocument(); + await userEvent.click(exportButton); expect(result.current.fetchExportData).toBeCalledWith({ csvTimeStamp: 0, dataType: 'metric', endDate: '', startDate: '', - }) - }) + }); + }); it('should show errorMessage when clicking export metric button given csv not exist', async () => { - setup(['']) - await userEvent.click(screen.getByText(EXPORT_METRIC_DATA)) - expect(screen.getByText('Export metric data')).toBeInTheDocument() - }) - }) -}) + setup(['']); + await userEvent.click(screen.getByText(EXPORT_METRIC_DATA)); + expect(screen.getByText('Export metric data')).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts index 44e71af90b..f747f3128d 100644 --- a/frontend/cypress.config.ts +++ b/frontend/cypress.config.ts @@ -20,4 +20,4 @@ export default defineConfig({ modifyObstructiveCode: false, //Increase timeout defaultCommandTimeout: 10000, -}) +}); diff --git a/frontend/cypress/e2e/createANewProject.cy.ts b/frontend/cypress/e2e/createANewProject.cy.ts index 3449bd9427..136fa923d2 100644 --- a/frontend/cypress/e2e/createANewProject.cy.ts +++ b/frontend/cypress/e2e/createANewProject.cy.ts @@ -141,32 +141,32 @@ const checkMetricsCalculation = (testId: string, boardData: MetricsDataItem[]) = }; const checkBoardShowMore = () => { - reportPage.showMoreBoardButton.should('exist') - reportPage.goToBoardDetailPage() - cy.get(`[data-test-id="${METRICS_TITLE.VELOCITY}"]`).find('tbody > tr').should('have.length', 2) - cy.get(`[data-test-id="${METRICS_TITLE.CYCLE_TIME}"]`).find('tbody > tr').should('have.length', 17) - cy.get(`[data-test-id="${METRICS_TITLE.CLASSIFICATION}"]`).find('tbody > tr').should('have.length', 103) + reportPage.showMoreBoardButton.should('exist'); + reportPage.goToBoardDetailPage(); + cy.get(`[data-test-id="${METRICS_TITLE.VELOCITY}"]`).find('tbody > tr').should('have.length', 2); + cy.get(`[data-test-id="${METRICS_TITLE.CYCLE_TIME}"]`).find('tbody > tr').should('have.length', 17); + cy.get(`[data-test-id="${METRICS_TITLE.CLASSIFICATION}"]`).find('tbody > tr').should('have.length', 103); - reportPage.exportBoardData() - checkBoardCSV() + reportPage.exportBoardData(); + checkBoardCSV(); - reportPage.boardGoToReportPage() -} + reportPage.boardGoToReportPage(); +}; const checkDoraShowMore = () => { - reportPage.showMoreDoraButton.should('exist') - reportPage.goToDoraDetailPage() + reportPage.showMoreDoraButton.should('exist'); + reportPage.goToDoraDetailPage(); - cy.get(`[data-test-id="${METRICS_TITLE.DEPLOYMENT_FREQUENCY}"]`).find('tbody > tr').should('have.length', 2) - cy.get(`[data-test-id="${METRICS_TITLE.LEAD_TIME_FOR_CHANGES}"]`).find('tbody > tr').should('have.length', 4) - cy.get(`[data-test-id="${METRICS_TITLE.CHANGE_FAILURE_RATE}"]`).find('tbody > tr').should('have.length', 2) - cy.get(`[data-test-id="${METRICS_TITLE.MEAN_TIME_TO_RECOVERY}"]`).find('tbody > tr').should('have.length', 2) + cy.get(`[data-test-id="${METRICS_TITLE.DEPLOYMENT_FREQUENCY}"]`).find('tbody > tr').should('have.length', 2); + cy.get(`[data-test-id="${METRICS_TITLE.LEAD_TIME_FOR_CHANGES}"]`).find('tbody > tr').should('have.length', 4); + cy.get(`[data-test-id="${METRICS_TITLE.CHANGE_FAILURE_RATE}"]`).find('tbody > tr').should('have.length', 2); + cy.get(`[data-test-id="${METRICS_TITLE.MEAN_TIME_TO_RECOVERY}"]`).find('tbody > tr').should('have.length', 2); - reportPage.exportPipelineData() - checkPipelineCSV() + reportPage.exportPipelineData(); + checkPipelineCSV(); - reportPage.doraGoToReportPage() -} + reportPage.doraGoToReportPage(); +}; const checkCycleTimeTooltip = () => { metricsPage.cycleTimeTitleTooltip.trigger('mouseover'); @@ -354,8 +354,8 @@ describe('Create a new project', () => { reportPage.firstNotification.should('not.exist'); - checkBoardShowMore() - checkDoraShowMore() + checkBoardShowMore(); + checkDoraShowMore(); // checkpoint back to metrics step reportPage.backToMetricsStep(); diff --git a/frontend/cypress/pages/metrics/report.ts b/frontend/cypress/pages/metrics/report.ts index f7b18b6370..42ce6dfe76 100644 --- a/frontend/cypress/pages/metrics/report.ts +++ b/frontend/cypress/pages/metrics/report.ts @@ -32,31 +32,31 @@ class Report { } get showMoreBoardButton() { - return cy.contains('Board Metrics').parent().siblings().eq(0) + return cy.contains('Board Metrics').parent().siblings().eq(0); } get showMoreDoraButton() { - return cy.contains('DORA Metrics').parent().siblings().eq(0) + return cy.contains('DORA Metrics').parent().siblings().eq(0); } get topBackButton() { - return cy.contains('Back') + return cy.contains('Back'); } boardGoToReportPage() { - this.topBackButton.click() + this.topBackButton.click(); } doraGoToReportPage() { - this.backButton.click() + this.backButton.click(); } goToBoardDetailPage() { - this.showMoreBoardButton.click() + this.showMoreBoardButton.click(); } goToDoraDetailPage() { - this.showMoreDoraButton.click() + this.showMoreDoraButton.click(); } backToMetricsStep() { diff --git a/frontend/src/clients/report/dto/response.ts b/frontend/src/clients/report/dto/response.ts index 85a55496c8..b7df0db9e6 100644 --- a/frontend/src/clients/report/dto/response.ts +++ b/frontend/src/clients/report/dto/response.ts @@ -1,143 +1,143 @@ -import { ReportDataWithThreeColumns, ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { Nullable } from '@src/utils/types' +import { ReportDataWithThreeColumns, ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { Nullable } from '@src/utils/types'; export interface ReportResponseDTO { - velocity: Nullable - cycleTime: Nullable - classificationList: Nullable - deploymentFrequency: Nullable - meanTimeToRecovery: Nullable - leadTimeForChanges: Nullable - changeFailureRate: Nullable - exportValidityTime: Nullable - isBoardMetricsReady: Nullable - isPipelineMetricsReady: Nullable - isSourceControlMetricsReady: Nullable - isAllMetricsReady: boolean + velocity: Nullable; + cycleTime: Nullable; + classificationList: Nullable; + deploymentFrequency: Nullable; + meanTimeToRecovery: Nullable; + leadTimeForChanges: Nullable; + changeFailureRate: Nullable; + exportValidityTime: Nullable; + isBoardMetricsReady: Nullable; + isPipelineMetricsReady: Nullable; + isSourceControlMetricsReady: Nullable; + isAllMetricsReady: boolean; } export interface VelocityResponse { - velocityForSP: number - velocityForCards: number + velocityForSP: number; + velocityForCards: number; } export interface CycleTimeResponse { - totalTimeForCards: number - averageCycleTimePerCard: number - averageCycleTimePerSP: number - swimlaneList: Array + totalTimeForCards: number; + averageCycleTimePerCard: number; + averageCycleTimePerSP: number; + swimlaneList: Array; } export interface ClassificationResponse { - fieldName: string - pairList: Array + fieldName: string; + pairList: Array; } export interface DeploymentFrequencyResponse { - avgDeploymentFrequency: AVGDeploymentFrequency - deploymentFrequencyOfPipelines: DeploymentFrequencyOfPipeline[] + avgDeploymentFrequency: AVGDeploymentFrequency; + deploymentFrequencyOfPipelines: DeploymentFrequencyOfPipeline[]; } export interface LeadTimeForChangesResponse { - leadTimeForChangesOfPipelines: Array - avgLeadTimeForChanges: AvgLeadTime + leadTimeForChangesOfPipelines: Array; + avgLeadTimeForChanges: AvgLeadTime; } export interface ChangeFailureRateResponse { - avgChangeFailureRate: AvgFailureRate - changeFailureRateOfPipelines: FailureRateOfPipeline[] + avgChangeFailureRate: AvgFailureRate; + changeFailureRateOfPipelines: FailureRateOfPipeline[]; } export interface Swimlane { - optionalItemName: string - averageTimeForSP: number - averageTimeForCards: number - totalTime: number + optionalItemName: string; + averageTimeForSP: number; + averageTimeForCards: number; + totalTime: number; } export interface AVGDeploymentFrequency { - name: string - step?: string - deploymentFrequency: number + name: string; + step?: string; + deploymentFrequency: number; } export interface DeploymentDateCount { - date: string - count: number + date: string; + count: number; } export interface DeploymentFrequencyOfPipeline { - name: string - step: string - deploymentFrequency: number - dailyDeploymentCounts: DeploymentDateCount[] + name: string; + step: string; + deploymentFrequency: number; + dailyDeploymentCounts: DeploymentDateCount[]; } export interface LeadTimeOfPipeline { - name: string - step: string - prLeadTime: number - pipelineLeadTime: number - totalDelayTime: number + name: string; + step: string; + prLeadTime: number; + pipelineLeadTime: number; + totalDelayTime: number; } export interface AvgLeadTime { - name: string - step?: string - prLeadTime: number - pipelineLeadTime: number - totalDelayTime: number + name: string; + step?: string; + prLeadTime: number; + pipelineLeadTime: number; + totalDelayTime: number; } export interface FailureRateOfPipeline { - name: string - step: string - failedTimesOfPipeline: number - totalTimesOfPipeline: number - failureRate: number + name: string; + step: string; + failedTimesOfPipeline: number; + totalTimesOfPipeline: number; + failureRate: number; } export interface AvgFailureRate { - name: string - step?: string - totalTimes: number - totalFailedTimes: number - failureRate: number + name: string; + step?: string; + totalTimes: number; + totalFailedTimes: number; + failureRate: number; } export interface MeanTimeToRecoveryOfPipeline { - name: string - step: string - timeToRecovery: number + name: string; + step: string; + timeToRecovery: number; } export interface AvgMeanTimeToRecovery { - name: string - timeToRecovery: number + name: string; + timeToRecovery: number; } export interface MeanTimeToRecoveryResponse { - avgMeanTimeToRecovery: AvgMeanTimeToRecovery - meanTimeRecoveryPipelines: MeanTimeToRecoveryOfPipeline[] + avgMeanTimeToRecovery: AvgMeanTimeToRecovery; + meanTimeRecoveryPipelines: MeanTimeToRecoveryOfPipeline[]; } export interface ClassificationNameValuePair { - name: string - value: number + name: string; + value: number; } export interface ReportCallbackResponse { - callbackUrl: string - interval: number + callbackUrl: string; + interval: number; } export interface ReportResponse { - velocityList?: ReportDataWithTwoColumns[] | null - cycleTimeList?: ReportDataWithTwoColumns[] | null - classification?: ReportDataWithThreeColumns[] | null - deploymentFrequencyList?: ReportDataWithThreeColumns[] | null - meanTimeToRecoveryList?: ReportDataWithThreeColumns[] | null - leadTimeForChangesList?: ReportDataWithThreeColumns[] | null - changeFailureRateList?: ReportDataWithThreeColumns[] | null - exportValidityTimeMin?: number | null + velocityList?: ReportDataWithTwoColumns[] | null; + cycleTimeList?: ReportDataWithTwoColumns[] | null; + classification?: ReportDataWithThreeColumns[] | null; + deploymentFrequencyList?: ReportDataWithThreeColumns[] | null; + meanTimeToRecoveryList?: ReportDataWithThreeColumns[] | null; + leadTimeForChangesList?: ReportDataWithThreeColumns[] | null; + changeFailureRateList?: ReportDataWithThreeColumns[] | null; + exportValidityTimeMin?: number | null; } diff --git a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx index c95dfa8def..b03f9f4e54 100644 --- a/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx +++ b/frontend/src/components/Common/ReportGrid/ReportTitle/style.tsx @@ -1,18 +1,18 @@ -import { styled } from '@mui/material/styles' -import { theme } from '@src/theme' -import { Typography } from '@mui/material' +import { styled } from '@mui/material/styles'; +import { theme } from '@src/theme'; +import { Typography } from '@mui/material'; export const StyledMetricsTitleSection = styled('div')({ display: 'flex', alignItems: 'center', -}) +}); export const StyledMetricsSign = styled('canvas')({ margin: '0.2rem 0.5rem', height: '1rem', width: '0.3rem', background: theme.main.backgroundColor, -}) +}); export const StyledMetricsTitle = styled(Typography)({ fontWeight: 600, @@ -21,4 +21,4 @@ export const StyledMetricsTitle = styled(Typography)({ textAlign: 'start', textOverflow: 'ellipsis', whiteSpace: 'nowrap', -}) +}); diff --git a/frontend/src/components/Metrics/MetricsStep/style.tsx b/frontend/src/components/Metrics/MetricsStep/style.tsx index aa2ed34fde..ff27366c73 100644 --- a/frontend/src/components/Metrics/MetricsStep/style.tsx +++ b/frontend/src/components/Metrics/MetricsStep/style.tsx @@ -1,12 +1,12 @@ -import { styled } from '@mui/material/styles' -import { Divider } from '@src/components/Common/MetricsSettingTitle/style' +import { styled } from '@mui/material/styles'; +import { Divider } from '@src/components/Common/MetricsSettingTitle/style'; export const MetricSelectionHeader = styled('div')({ display: 'flex', justifyContent: 'flex-end', position: 'relative', top: '1.6rem', -}) +}); export const MetricSelectionWrapper = styled('div')({ boxSizing: 'border-box', @@ -20,17 +20,17 @@ export const MetricSelectionWrapper = styled('div')({ border: '0.1rem', boxShadow: '0 0.25rem 1rem 0 rgba(0, 0, 0, 0.08)', position: 'relative', -}) +}); export const MetricsSelectionTitle = styled(Divider)({ fontSize: '1.2rem', lineHeight: '1.25rem', fontWeight: '600', fontFamily: 'sans-serif', -}) +}); export const ReportSelectionTitle = styled(MetricsSelectionTitle)({ marginBottom: '1.5rem', -}) +}); -export const ConfigSelectionTitle = styled(ReportSelectionTitle)({}) +export const ConfigSelectionTitle = styled(ReportSelectionTitle)({}); diff --git a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx index 8fe156da45..4392f205b4 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/index.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/index.tsx @@ -1,31 +1,31 @@ -import { Tooltip } from '@mui/material' -import { TIPS } from '@src/constants/resources' -import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style' -import SaveAltIcon from '@mui/icons-material/SaveAlt' -import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons' -import React, { useEffect } from 'react' -import { CSVReportRequestDTO } from '@src/clients/report/dto/request' -import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect' -import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog' +import { Tooltip } from '@mui/material'; +import { TIPS } from '@src/constants/resources'; +import { BackButton, SaveButton } from '@src/components/Metrics/MetricsStepper/style'; +import SaveAltIcon from '@mui/icons-material/SaveAlt'; +import { COMMON_BUTTONS, DOWNLOAD_TYPES } from '@src/constants/commons'; +import React, { useEffect } from 'react'; +import { CSVReportRequestDTO } from '@src/clients/report/dto/request'; +import { useExportCsvEffect } from '@src/hooks/useExportCsvEffect'; +import { ExpiredDialog } from '@src/components/Metrics/ReportStep/ExpiredDialog'; import { StyledButtonGroup, StyledExportButton, StyledRightButtonGroup, -} from '@src/components/Metrics/ReportButtonGroup/style' -import { ReportResponseDTO } from '@src/clients/report/dto/response' +} from '@src/components/Metrics/ReportButtonGroup/style'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; interface ReportButtonGroupProps { - handleSave?: () => void - handleBack: () => void - csvTimeStamp: number - startDate: string - endDate: string - setErrorMessage: (message: string) => void - reportData: ReportResponseDTO | undefined - isShowSave: boolean - isShowExportBoardButton: boolean - isShowExportPipelineButton: boolean - isShowExportMetrics: boolean + handleSave?: () => void; + handleBack: () => void; + csvTimeStamp: number; + startDate: string; + endDate: string; + setErrorMessage: (message: string) => void; + reportData: ReportResponseDTO | undefined; + isShowSave: boolean; + isShowExportBoardButton: boolean; + isShowExportPipelineButton: boolean; + isShowExportMetrics: boolean; } export const ReportButtonGroup = ({ @@ -41,7 +41,7 @@ export const ReportButtonGroup = ({ isShowExportBoardButton, isShowExportPipelineButton, }: ReportButtonGroupProps) => { - const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect() + const { fetchExportData, errorMessage, isExpired } = useExportCsvEffect(); useEffect(() => { setErrorMessage(errorMessage); diff --git a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx index 76e8dfc80d..9e8f7181bd 100644 --- a/frontend/src/components/Metrics/ReportButtonGroup/style.tsx +++ b/frontend/src/components/Metrics/ReportButtonGroup/style.tsx @@ -1,7 +1,7 @@ -import { styled } from '@mui/material/styles' -import { Button } from '@mui/material' -import { theme } from '@src/theme' -import { basicButtonStyle } from '@src/components/Metrics/ReportStep/style' +import { styled } from '@mui/material/styles'; +import { Button } from '@mui/material'; +import { theme } from '@src/theme'; +import { basicButtonStyle } from '@src/components/Metrics/ReportStep/style'; export const StyledRightButtonGroup = styled('div')({ [theme.breakpoints.down('lg')]: { @@ -10,7 +10,7 @@ export const StyledRightButtonGroup = styled('div')({ justifyContent: 'end', alignItems: 'center', }, -}) +}); export const StyledButtonGroup = styled('div')` box-sizing: border-box; @@ -21,7 +21,7 @@ export const StyledButtonGroup = styled('div')` justify-content: ${(props: { isShowSave: boolean }) => (props.isShowSave ? 'space-between' : 'flex-end')}; width: 100%; padding-top: 2rem; -` +`; export const StyledExportButton = styled(Button)({ ...basicButtonStyle, @@ -44,4 +44,4 @@ export const StyledExportButton = styled(Button)({ [theme.breakpoints.down('lg')]: { fontSize: '0.8rem', }, -}) +}); diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx index 9fe0ae0642..6c904c2c1a 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/index.tsx @@ -9,29 +9,29 @@ import { METRICS_TITLE, REQUIRED_DATA, SHOW_MORE, -} from '@src/constants/resources' -import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request' -import { selectMetricsContent } from '@src/context/Metrics/metricsSlice' -import dayjs from 'dayjs' +} from '@src/constants/resources'; +import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; +import { selectMetricsContent } from '@src/context/Metrics/metricsSlice'; +import dayjs from 'dayjs'; import { StyledMetricsSection, StyledShowMore, StyledTitleWrapper, -} from '@src/components/Metrics/ReportStep/BoradMetrics/style' -import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util' -import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' -import { ReportGrid } from '@src/components/Common/ReportGrid' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { Nullable } from '@src/utils/types' +} from '@src/components/Metrics/ReportStep/BoradMetrics/style'; +import { filterAndMapCycleTimeSettings, getJiraBoardToken } from '@src/utils/util'; +import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle'; +import { ReportGrid } from '@src/components/Common/ReportGrid'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { Nullable } from '@src/utils/types'; interface BoardMetricsProps { - startToRequestBoardData: (request: ReportRequestDTO) => void - onShowDetail: () => void - boardReport?: ReportResponseDTO - csvTimeStamp: number - startDate: Nullable - endDate: Nullable - isBackFromDetail: boolean + startToRequestBoardData: (request: ReportRequestDTO) => void; + onShowDetail: () => void; + boardReport?: ReportResponseDTO; + csvTimeStamp: number; + startDate: Nullable; + endDate: Nullable; + isBackFromDetail: boolean; } const BoardMetrics = ({ @@ -45,13 +45,13 @@ const BoardMetrics = ({ }: BoardMetricsProps) => { const configData = useAppSelector(selectConfig); const { cycleTimeSettings, treatFlagCardAsBlock, users, targetFields, doneColumn, assigneeFilter } = - useAppSelector(selectMetricsContent) - const jiraColumns = useAppSelector(selectJiraColumns) + useAppSelector(selectMetricsContent); + const jiraColumns = useAppSelector(selectJiraColumns); - const { metrics, calendarType } = configData.basic - const { board } = configData - const { token, type, site, projectKey, boardId, email } = board.config - const jiraToken = getJiraBoardToken(token, email) + const { metrics, calendarType } = configData.basic; + const { board } = configData; + const { token, type, site, projectKey, boardId, email } = board.config; + const jiraToken = getJiraBoardToken(token, email); const jiraColumnsWithValue = jiraColumns?.map( (obj: { key: string; value: { name: string; statuses: string[] } }) => obj.value ); @@ -76,7 +76,7 @@ const BoardMetrics = ({ doneColumn, }, csvTimeStamp: csvTimeStamp, - }) + }); const getBoardItems = () => { const velocity = boardReport?.velocity; @@ -123,8 +123,8 @@ const BoardMetrics = ({ }; useEffect(() => { - !isBackFromDetail && startToRequestBoardData(getBoardReportRequestBody()) - }, []) + !isBackFromDetail && startToRequestBoardData(getBoardReportRequestBody()); + }, []); return ( <> diff --git a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx index fe0bdd11ea..be05c615cf 100644 --- a/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/BoradMetrics/style.tsx @@ -8,11 +8,11 @@ export const StyledTitleWrapper = styled('div')({ display: 'flex', alignItems: 'center', marginBottom: '1rem', -}) +}); export const StyledShowMore = styled('div')({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', color: '#4350AF', -}) +}); diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx index 2ca6b38673..1424ac21ba 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/index.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from 'react' -import { useAppSelector } from '@src/hooks' -import { selectConfig } from '@src/context/config/configSlice' +import React, { useEffect } from 'react'; +import { useAppSelector } from '@src/hooks'; +import { selectConfig } from '@src/context/config/configSlice'; import { CALENDAR, DORA_METRICS, @@ -9,27 +9,27 @@ import { METRICS_TITLE, REQUIRED_DATA, SHOW_MORE, -} from '@src/constants/resources' -import { ReportRequestDTO } from '@src/clients/report/dto/request' -import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice' -import dayjs from 'dayjs' -import { StyledMetricsSection } from '@src/components/Metrics/ReportStep/DoraMetrics/style' -import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle' -import { ReportGrid } from '@src/components/Common/ReportGrid' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { StyledSpacing } from '@src/components/Metrics/ReportStep/style' -import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util' -import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style' -import { Nullable } from '@src/utils/types' +} from '@src/constants/resources'; +import { ReportRequestDTO } from '@src/clients/report/dto/request'; +import { IPipelineConfig, selectMetricsContent } from '@src/context/Metrics/metricsSlice'; +import dayjs from 'dayjs'; +import { StyledMetricsSection } from '@src/components/Metrics/ReportStep/DoraMetrics/style'; +import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle'; +import { ReportGrid } from '@src/components/Common/ReportGrid'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { StyledSpacing } from '@src/components/Metrics/ReportStep/style'; +import { formatMillisecondsToHours, formatMinToHours } from '@src/utils/util'; +import { StyledShowMore, StyledTitleWrapper } from '@src/components/Metrics/ReportStep/DoraMetrics/style'; +import { Nullable } from '@src/utils/types'; interface DoraMetricsProps { - startToRequestDoraData: (request: ReportRequestDTO) => void - onShowDetail: () => void - doraReport?: ReportResponseDTO - csvTimeStamp: number - startDate: Nullable - endDate: Nullable - isBackFromDetail: boolean + startToRequestDoraData: (request: ReportRequestDTO) => void; + onShowDetail: () => void; + doraReport?: ReportResponseDTO; + csvTimeStamp: number; + startDate: Nullable; + endDate: Nullable; + isBackFromDetail: boolean; } const DoraMetrics = ({ @@ -41,14 +41,14 @@ const DoraMetrics = ({ startDate, endDate, }: DoraMetricsProps) => { - const configData = useAppSelector(selectConfig) - const { pipelineTool, sourceControl } = configData - const { metrics, calendarType } = configData.basic - const { pipelineCrews, deploymentFrequencySettings, leadTimeForChanges } = useAppSelector(selectMetricsContent) - const shouldShowSourceControl = metrics.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES) + const configData = useAppSelector(selectConfig); + const { pipelineTool, sourceControl } = configData; + const { metrics, calendarType } = configData.basic; + const { pipelineCrews, deploymentFrequencySettings, leadTimeForChanges } = useAppSelector(selectMetricsContent); + const shouldShowSourceControl = metrics.includes(REQUIRED_DATA.LEAD_TIME_FOR_CHANGES); const getDoraReportRequestBody = (): ReportRequestDTO => { - const doraMetrics = metrics.filter((metric) => DORA_METRICS.includes(metric)) + const doraMetrics = metrics.filter((metric) => DORA_METRICS.includes(metric)); return { metrics: doraMetrics, @@ -66,19 +66,19 @@ const DoraMetrics = ({ leadTime: getPipelineConfig(leadTimeForChanges), }, csvTimeStamp: csvTimeStamp, - } - } + }; + }; const getPipelineConfig = (pipelineConfigs: IPipelineConfig[]) => { if (!pipelineConfigs[0].organization && pipelineConfigs.length === 1) { - return [] + return []; } return pipelineConfigs.map(({ organization, pipelineName, step, branches }) => { const pipelineConfigFromPipelineList = configData.pipelineTool.verifiedResponse.pipelineList.find( (pipeline) => pipeline.name === pipelineName && pipeline.orgName === organization - ) + ); if (pipelineConfigFromPipelineList != undefined) { - const { orgName, orgId, name, id, repository } = pipelineConfigFromPipelineList + const { orgName, orgId, name, id, repository } = pipelineConfigFromPipelineList; return { orgId, orgName, @@ -87,21 +87,21 @@ const DoraMetrics = ({ step, repository, branches, - } + }; } }) as { - id: string - name: string - orgId: string - orgName: string - repository: string - step: string - branches: string[] - }[] - } + id: string; + name: string; + orgId: string; + orgName: string; + repository: string; + step: string; + branches: string[]; + }[]; + }; const getSourceControlItems = () => { - const leadTimeForChanges = doraReport?.leadTimeForChanges + const leadTimeForChanges = doraReport?.leadTimeForChanges; return [ { title: METRICS_TITLE.LEAD_TIME_FOR_CHANGES, @@ -120,13 +120,13 @@ const DoraMetrics = ({ }, ], }, - ] - } + ]; + }; const getPipelineItems = () => { - const deploymentFrequency = doraReport?.deploymentFrequency - const meanTimeToRecovery = doraReport?.meanTimeToRecovery - const changeFailureRate = doraReport?.changeFailureRate + const deploymentFrequency = doraReport?.deploymentFrequency; + const meanTimeToRecovery = doraReport?.meanTimeToRecovery; + const changeFailureRate = doraReport?.changeFailureRate; const deploymentFrequencyList = metrics.includes(REQUIRED_DATA.DEPLOYMENT_FREQUENCY) ? [ @@ -140,7 +140,7 @@ const DoraMetrics = ({ ], }, ] - : [] + : []; const meanTimeToRecoveryList = metrics.includes(REQUIRED_DATA.MEAN_TIME_TO_RECOVERY) ? [ @@ -154,7 +154,7 @@ const DoraMetrics = ({ ], }, ] - : [] + : []; const changeFailureRateList = metrics.includes(REQUIRED_DATA.CHANGE_FAILURE_RATE) ? [ @@ -169,14 +169,14 @@ const DoraMetrics = ({ ], }, ] - : [] + : []; - return [...deploymentFrequencyList, ...changeFailureRateList, ...meanTimeToRecoveryList] - } + return [...deploymentFrequencyList, ...changeFailureRateList, ...meanTimeToRecoveryList]; + }; useEffect(() => { - !isBackFromDetail && startToRequestDoraData(getDoraReportRequestBody()) - }, []) + !isBackFromDetail && startToRequestDoraData(getDoraReportRequestBody()); + }, []); return ( <> @@ -192,7 +192,7 @@ const DoraMetrics = ({ - ) -} + ); +}; -export default DoraMetrics +export default DoraMetrics; diff --git a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx index fe0bdd11ea..be05c615cf 100644 --- a/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx +++ b/frontend/src/components/Metrics/ReportStep/DoraMetrics/style.tsx @@ -8,11 +8,11 @@ export const StyledTitleWrapper = styled('div')({ display: 'flex', alignItems: 'center', marginBottom: '1rem', -}) +}); export const StyledShowMore = styled('div')({ marginLeft: '0.5rem', fontSize: '0.8rem', textDecoration: 'none', color: '#4350AF', -}) +}); diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx index b8f92dcbb1..531b4930be 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/board.tsx @@ -1,23 +1,23 @@ -import React from 'react' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns' -import { reportMapper } from '@src/hooks/reportMapper/report' -import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { Optional } from '@src/utils/types' -import { withGoBack } from './withBack' -import { METRICS_TITLE } from '@src/constants/resources' +import React from 'react'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns'; +import ReportForTwoColumns from '@src/components/Common/ReportForTwoColumns'; +import { reportMapper } from '@src/hooks/reportMapper/report'; +import { ReportDataWithTwoColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { Optional } from '@src/utils/types'; +import { withGoBack } from './withBack'; +import { METRICS_TITLE } from '@src/constants/resources'; interface Property { - data: ReportResponseDTO - onBack: () => void + data: ReportResponseDTO; + onBack: () => void; } const showSectionWith2Columns = (title: string, value: Optional) => - value && + value && ; export const BoardDetail = withGoBack(({ data }: Property) => { - const mappedData = reportMapper(data) + const mappedData = reportMapper(data); return ( <> @@ -32,5 +32,5 @@ export const BoardDetail = withGoBack(({ data }: Property) => { /> )} - ) -}) + ); +}); diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx index d8df62be9f..6f94b2a2b0 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/dora.tsx @@ -1,21 +1,21 @@ -import React from 'react' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns' -import { METRICS_TITLE, NAME, PIPELINE_STEP } from '@src/constants/resources' -import { reportMapper } from '@src/hooks/reportMapper/report' -import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure' -import { Optional } from '@src/utils/types' -import { withGoBack } from './withBack' +import React from 'react'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import ReportForThreeColumns from '@src/components/Common/ReportForThreeColumns'; +import { METRICS_TITLE, NAME, PIPELINE_STEP } from '@src/constants/resources'; +import { reportMapper } from '@src/hooks/reportMapper/report'; +import { ReportDataWithThreeColumns } from '@src/hooks/reportMapper/reportUIDataStructure'; +import { Optional } from '@src/utils/types'; +import { withGoBack } from './withBack'; interface Property { - data: ReportResponseDTO - onBack: () => void + data: ReportResponseDTO; + onBack: () => void; } const showSection = (title: string, value: Optional) => - value && + value && ; export const DoraDetail = withGoBack(({ data }: Property) => { - const mappedData = reportMapper(data) + const mappedData = reportMapper(data); return ( <> @@ -24,5 +24,5 @@ export const DoraDetail = withGoBack(({ data }: Property) => { {showSection(METRICS_TITLE.CHANGE_FAILURE_RATE, mappedData.changeFailureRateList)} {showSection(METRICS_TITLE.MEAN_TIME_TO_RECOVERY, mappedData.meanTimeToRecoveryList)} - ) -}) + ); +}); diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts index c89a903db8..a046ec7b37 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/index.ts @@ -1,2 +1,2 @@ -export * from '@src/components/Metrics/ReportStep/ReportDetail/board' -export * from '@src/components/Metrics/ReportStep/ReportDetail/dora' +export * from '@src/components/Metrics/ReportStep/ReportDetail/board'; +export * from '@src/components/Metrics/ReportStep/ReportDetail/dora'; diff --git a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx index a710e296b4..2087dff0e9 100644 --- a/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx +++ b/frontend/src/components/Metrics/ReportStep/ReportDetail/withBack.tsx @@ -1,9 +1,9 @@ -import React from 'react' -import { styled } from '@mui/material/styles' -import { ArrowBack } from '@mui/icons-material' -import { BACK } from '@src/constants/resources' +import React from 'react'; +import { styled } from '@mui/material/styles'; +import { ArrowBack } from '@mui/icons-material'; +import { BACK } from '@src/constants/resources'; interface Property { - onBack: () => void + onBack: () => void; } const StyledDiv = styled('div')` @@ -15,12 +15,12 @@ const StyledDiv = styled('div')` color: #595959; cursor: pointer; font-size: 1rem; -` +`; const StyledArrowBack = styled(ArrowBack)` width: 1.5rem; margin-right: 0.5rem; -` +`; export const withGoBack =

    (Child: React.ComponentType

    ) => @@ -33,4 +33,4 @@ export const withGoBack = - ) + ); diff --git a/frontend/src/components/Metrics/ReportStep/index.tsx b/frontend/src/components/Metrics/ReportStep/index.tsx index c4f0b926b1..ef83e188b6 100644 --- a/frontend/src/components/Metrics/ReportStep/index.tsx +++ b/frontend/src/components/Metrics/ReportStep/index.tsx @@ -1,31 +1,31 @@ -import React, { useEffect, useLayoutEffect, useState } from 'react' -import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect' -import { useAppSelector } from '@src/hooks' -import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice' -import { MESSAGE, REPORT_PAGE_TYPE } from '@src/constants/resources' -import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style' -import { ErrorNotification } from '@src/components/ErrorNotification' -import { useNavigate } from 'react-router-dom' -import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect' -import { ROUTE } from '@src/constants/router' -import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup' -import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics' -import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics' -import { backStep, selectTimeStamp } from '@src/context/stepper/StepperSlice' -import DateRangeViewer from '@src/components/Common/DateRangeViewer' -import { MetricSelectionHeader } from '../MetricsStep/style' -import { BoardDetail, DoraDetail } from './ReportDetail' -import { ReportResponseDTO } from '@src/clients/report/dto/response' -import { useAppDispatch } from '@src/hooks/useAppDispatch' +import React, { useEffect, useLayoutEffect, useState } from 'react'; +import { useGenerateReportEffect } from '@src/hooks/useGenerateReportEffect'; +import { useAppSelector } from '@src/hooks'; +import { isSelectBoardMetrics, isSelectDoraMetrics, selectConfig } from '@src/context/config/configSlice'; +import { MESSAGE, REPORT_PAGE_TYPE } from '@src/constants/resources'; +import { StyledErrorNotification } from '@src/components/Metrics/ReportStep/style'; +import { ErrorNotification } from '@src/components/ErrorNotification'; +import { useNavigate } from 'react-router-dom'; +import { useNotificationLayoutEffectInterface } from '@src/hooks/useNotificationLayoutEffect'; +import { ROUTE } from '@src/constants/router'; +import { ReportButtonGroup } from '@src/components/Metrics/ReportButtonGroup'; +import BoardMetrics from '@src/components/Metrics/ReportStep/BoradMetrics'; +import DoraMetrics from '@src/components/Metrics/ReportStep/DoraMetrics'; +import { backStep, selectTimeStamp } from '@src/context/stepper/StepperSlice'; +import DateRangeViewer from '@src/components/Common/DateRangeViewer'; +import { MetricSelectionHeader } from '../MetricsStep/style'; +import { BoardDetail, DoraDetail } from './ReportDetail'; +import { ReportResponseDTO } from '@src/clients/report/dto/response'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; export interface ReportStepProps { - notification: useNotificationLayoutEffectInterface - handleSave: () => void + notification: useNotificationLayoutEffectInterface; + handleSave: () => void; } const ReportStep = ({ notification, handleSave }: ReportStepProps) => { - const navigate = useNavigate() - const dispatch = useAppDispatch() + const navigate = useNavigate(); + const dispatch = useAppDispatch(); const { isServerError, errorMessage: reportErrorMsg, @@ -33,22 +33,22 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { startToRequestDoraData, reportData, stopPollingReports, - } = useGenerateReportEffect() + } = useGenerateReportEffect(); - const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined) - const [pageType, setPageType] = useState(REPORT_PAGE_TYPE.SUMMARY) - const [isBackFromDetail, setIsBackFromDetail] = useState(false) - const configData = useAppSelector(selectConfig) - const csvTimeStamp = useAppSelector(selectTimeStamp) + const [exportValidityTimeMin, setExportValidityTimeMin] = useState(undefined); + const [pageType, setPageType] = useState(REPORT_PAGE_TYPE.SUMMARY); + const [isBackFromDetail, setIsBackFromDetail] = useState(false); + const configData = useAppSelector(selectConfig); + const csvTimeStamp = useAppSelector(selectTimeStamp); - const startDate = configData.basic.dateRange.startDate ?? '' - const endDate = configData.basic.dateRange.endDate ?? '' + const startDate = configData.basic.dateRange.startDate ?? ''; + const endDate = configData.basic.dateRange.endDate ?? ''; - const { updateProps } = notification - const [errorMessage, setErrorMessage] = useState() + const { updateProps } = notification; + const [errorMessage, setErrorMessage] = useState(); - const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics) - const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics) + const shouldShowBoardMetrics = useAppSelector(isSelectBoardMetrics); + const shouldShowDoraMetrics = useAppSelector(isSelectDoraMetrics); useLayoutEffect(() => { exportValidityTimeMin && @@ -56,47 +56,47 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { open: true, title: MESSAGE.NOTIFICATION_FIRST_REPORT.replace('%s', exportValidityTimeMin.toString()), closeAutomatically: true, - }) - }, [exportValidityTimeMin]) + }); + }, [exportValidityTimeMin]); useLayoutEffect(() => { if (exportValidityTimeMin) { - const startTime = Date.now() + const startTime = Date.now(); const timer = setInterval(() => { - const currentTime = Date.now() - const elapsedTime = currentTime - startTime + const currentTime = Date.now(); + const elapsedTime = currentTime - startTime; - const remainingExpireTime = 5 * 60 * 1000 - const remainingTime = exportValidityTimeMin * 60 * 1000 - elapsedTime + const remainingExpireTime = 5 * 60 * 1000; + const remainingTime = exportValidityTimeMin * 60 * 1000 - elapsedTime; if (remainingTime <= remainingExpireTime) { updateProps?.({ open: true, title: MESSAGE.EXPIRE_IN_FIVE_MINUTES, closeAutomatically: true, - }) - clearInterval(timer) + }); + clearInterval(timer); } - }, 1000) + }, 1000); return () => { - clearInterval(timer) - } + clearInterval(timer); + }; } - }, [exportValidityTimeMin]) + }, [exportValidityTimeMin]); useEffect(() => { - setErrorMessage(reportErrorMsg) - }, [reportErrorMsg]) + setErrorMessage(reportErrorMsg); + }, [reportErrorMsg]); useEffect(() => { - setExportValidityTimeMin(reportData?.exportValidityTime) - }, [reportData]) + setExportValidityTimeMin(reportData?.exportValidityTime); + }, [reportData]); useLayoutEffect(() => { return () => { - stopPollingReports() - } - }, []) + stopPollingReports(); + }; + }, []); const showSummary = () => ( <> @@ -123,18 +123,18 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { /> )} - ) - const showBoardDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} /> - const showDoraDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} /> + ); + const showBoardDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} />; + const showDoraDetail = (data: ReportResponseDTO) => backToSummaryPage()} data={data} />; const handleBack = () => { - pageType === REPORT_PAGE_TYPE.SUMMARY ? dispatch(backStep()) : backToSummaryPage() - } + pageType === REPORT_PAGE_TYPE.SUMMARY ? dispatch(backStep()) : backToSummaryPage(); + }; const backToSummaryPage = () => { - setPageType(REPORT_PAGE_TYPE.SUMMARY) - setIsBackFromDetail(true) - } + setPageType(REPORT_PAGE_TYPE.SUMMARY); + setIsBackFromDetail(true); + }; return ( <> @@ -172,13 +172,13 @@ const ReportStep = ({ notification, handleSave }: ReportStepProps) => { endDate={endDate} csvTimeStamp={csvTimeStamp} setErrorMessage={(message) => { - setErrorMessage(message) + setErrorMessage(message); }} /> )} - ) -} + ); +}; -export default ReportStep +export default ReportStep; diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index b283ed0b8f..54b8335fec 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -7,10 +7,10 @@ export const REPORT_PAGE_TYPE = { SUMMARY: 'Summary', BOARD: 'BoardReport', DORA: 'DoraReport', -} +}; -export const SHOW_MORE = 'show more >' -export const BACK = 'Back' +export const SHOW_MORE = 'show more >'; +export const BACK = 'Back'; export enum REQUIRED_DATA { All = 'All', diff --git a/frontend/src/context/config/configSlice.ts b/frontend/src/context/config/configSlice.ts index 90d4c06a2a..33507fc629 100644 --- a/frontend/src/context/config/configSlice.ts +++ b/frontend/src/context/config/configSlice.ts @@ -181,20 +181,20 @@ export const { resetImportedData, } = configSlice.actions; -export const selectProjectName = (state: RootState) => state.config.basic.projectName -export const selectCalendarType = (state: RootState) => state.config.basic.calendarType -export const selectDateRange = (state: RootState) => state.config.basic.dateRange -export const selectMetrics = (state: RootState) => state.config.basic.metrics +export const selectProjectName = (state: RootState) => state.config.basic.projectName; +export const selectCalendarType = (state: RootState) => state.config.basic.calendarType; +export const selectDateRange = (state: RootState) => state.config.basic.dateRange; +export const selectMetrics = (state: RootState) => state.config.basic.metrics; export const isSelectBoardMetrics = (state: RootState) => - state.config.basic.metrics.some((metric) => BOARD_METRICS.includes(metric)) + state.config.basic.metrics.some((metric) => BOARD_METRICS.includes(metric)); export const isSelectDoraMetrics = (state: RootState) => - state.config.basic.metrics.some((metric) => DORA_METRICS.includes(metric)) -export const selectBoard = (state: RootState) => state.config.board.config -export const isPipelineToolVerified = (state: RootState) => state.config.pipelineTool.isVerified -export const selectPipelineTool = (state: RootState) => state.config.pipelineTool.config -export const isSourceControlVerified = (state: RootState) => state.config.sourceControl.isVerified -export const selectSourceControl = (state: RootState) => state.config.sourceControl.config -export const selectWarningMessage = (state: RootState) => state.config.warningMessage + state.config.basic.metrics.some((metric) => DORA_METRICS.includes(metric)); +export const selectBoard = (state: RootState) => state.config.board.config; +export const isPipelineToolVerified = (state: RootState) => state.config.pipelineTool.isVerified; +export const selectPipelineTool = (state: RootState) => state.config.pipelineTool.config; +export const isSourceControlVerified = (state: RootState) => state.config.sourceControl.isVerified; +export const selectSourceControl = (state: RootState) => state.config.sourceControl.config; +export const selectWarningMessage = (state: RootState) => state.config.warningMessage; export const selectConfig = (state: RootState) => state.config; diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 63bd6252d8..b548d5df2b 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -17,12 +17,12 @@ export interface useGenerateReportEffectInterface { } export const useGenerateReportEffect = (): useGenerateReportEffectInterface => { - const reportPath = '/reports' - const [isServerError, setIsServerError] = useState(false) - const [errorMessage, setErrorMessage] = useState('') - const [reportData, setReportData] = useState() - const timerIdRef = useRef() - let hasPollingStarted = false + const reportPath = '/reports'; + const [isServerError, setIsServerError] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [reportData, setReportData] = useState(); + const timerIdRef = useRef(); + let hasPollingStarted = false; const startToRequestBoardData = (boardParams: ReportRequestDTO) => { reportClient diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts index 82d6cfb4b5..59dcebe9eb 100644 --- a/frontend/src/utils/types.ts +++ b/frontend/src/utils/types.ts @@ -1,2 +1,2 @@ -export type Nullable = T | null -export type Optional = T | null | undefined +export type Nullable = T | null; +export type Optional = T | null | undefined; From d76cbffaa0f42de19bf16d730e7fde4a36ef4632 Mon Sep 17 00:00:00 2001 From: "Jianxun.Ma" Date: Wed, 10 Jan 2024 17:41:41 +0800 Subject: [PATCH 88/90] [frontend] update testing --- .../components/Metrics/ConfigStep/ConfigStep.test.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx index 10a77ba55b..f776d34184 100644 --- a/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx +++ b/frontend/__tests__/src/components/Metrics/ConfigStep/ConfigStep.test.tsx @@ -152,9 +152,9 @@ describe('ConfigStep', () => { }); it('should verify again when date picker is changed given board fields are filled and verified', () => { - const { getByRole, getByText, queryByText, getByLabelText } = setup(); + setup(); const today = dayjs().format('MM/DD/YYYY'); - const startDateInput = getByLabelText('From *'); + const startDateInput = screen.getByLabelText('From *'); fireEvent.mouseDown(screen.getByRole('button', { name: REQUIRED_DATA })); const requireDateSelection = within(screen.getByRole('listbox')); @@ -163,9 +163,9 @@ describe('ConfigStep', () => { fireEvent.click(screen.getByText(VERIFY)); fireEvent.change(startDateInput, { target: { value: today } }); - expect(queryByText(VERIFY)).toBeVisible(); - expect(queryByText('Verified')).toBeNull(); - expect(queryByText(RESET)).toBeNull(); + expect(screen.queryByText(VERIFY)).toBeVisible(); + expect(screen.queryByText('Verified')).toBeNull(); + expect(screen.queryByText(RESET)).toBeNull(); }); it('should show warning message when selectWarningMessage has a value', async () => { From 57618b5d19e0afd43c076516437ac7a2346003f0 Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Wed, 10 Jan 2024 18:06:49 +0800 Subject: [PATCH 89/90] ADM-708:[backend]refactor: add serializable for class --- .../heartbeat/client/dto/board/jira/JiraBoardProject.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/heartbeat/client/dto/board/jira/JiraBoardProject.java b/backend/src/main/java/heartbeat/client/dto/board/jira/JiraBoardProject.java index fae99c457e..c88baa1b62 100644 --- a/backend/src/main/java/heartbeat/client/dto/board/jira/JiraBoardProject.java +++ b/backend/src/main/java/heartbeat/client/dto/board/jira/JiraBoardProject.java @@ -6,12 +6,14 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + @AllArgsConstructor @Data @Builder @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class JiraBoardProject { +public class JiraBoardProject implements Serializable { private String style; From e7df6d638485749a31dd3663d5780c36908a9fee Mon Sep 17 00:00:00 2001 From: "weiran.sun" <907221539@qq.com> Date: Wed, 10 Jan 2024 18:07:25 +0800 Subject: [PATCH 90/90] ADM-708:[backend]refactor: update get board info exception message --- .../main/java/heartbeat/service/board/jira/JiraService.java | 4 ++-- .../src/test/java/heartbeat/service/jira/JiraServiceTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 daf96550b2..73aed6d84f 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -159,13 +159,13 @@ public BoardConfigDTO getInfo(BoardType boardType, BoardRequestParam boardReques } catch (RuntimeException e) { Throwable cause = Optional.ofNullable(e.getCause()).orElse(e); - log.error("Failed to call Jira to get board config, project key: {}, board id: {}, e: {}", + log.error("Failed to call Jira to get board info, project key: {}, board id: {}, e: {}", boardRequestParam.getBoardId(), boardRequestParam.getProjectKey(), cause.getMessage()); if (cause instanceof BaseException baseException) { throw baseException; } throw new InternalServerErrorException( - String.format("Failed to call Jira to get board config, cause is %s", cause.getMessage())); + String.format("Failed to call Jira to get board info, cause is %s", cause.getMessage())); } } diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index 1dc9332440..115b2fe9cb 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -631,7 +631,7 @@ void shouldCallJiraFeignClientAndThrowNonColumnWhenGetJiraBoardInfo() { jiraService.getInfo(boardTypeJira, boardRequestParam); }); assertThat(thrown).isInstanceOf(InternalServerErrorException.class) - .hasMessageContaining("Failed to call Jira to get board config, cause is"); + .hasMessageContaining("Failed to call Jira to get board info, cause is"); } @Test