diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 07cbae52d..7148815a0 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -23,7 +23,7 @@ body: required: true - label: I've tried running `main`-labeled docker image and the issue still persists there required: true - - label: I'm running a supported version of the application which is listed [here](https://github.com/kafbat/kafka-ui/blob/main/SECURITY.md) + - label: I'm running a supported version of the application which is listed [here](https://github.com/kafbat/kafka-ui/blob/main/.github/SECURITY.md) required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index d678d1ab3..f9e0df5d2 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -19,7 +19,7 @@ body: options: - label: I've searched for an already existing issues [here](https://github.com/kafbat/kafka-ui/issues) required: true - - label: I'm running a supported version of the application which is listed [here](https://github.com/kafbat/kafka-ui/blob/main/SECURITY.md) and the feature is not present there + - label: I'm running a supported version of the application which is listed [here](https://github.com/kafbat/kafka-ui/blob/main/.github/SECURITY.md) and the feature is not present there required: true - type: textarea diff --git a/.github/workflows/branch-deploy.yml b/.github/workflows/branch-deploy.yml index 107a93116..6969bd2c6 100644 --- a/.github/workflows/branch-deploy.yml +++ b/.github/workflows/branch-deploy.yml @@ -8,6 +8,7 @@ on: permissions: contents: read + statuses: write jobs: build: diff --git a/.github/workflows/e2e-manual.yml b/.github/workflows/e2e-manual.yml new file mode 100644 index 000000000..edb9eead0 --- /dev/null +++ b/.github/workflows/e2e-manual.yml @@ -0,0 +1,26 @@ +name: "E2E: Suite run" +on: + workflow_dispatch: + inputs: + test_suite: + description: 'Select test suite to run' + default: 'regression' + required: true + type: choice + options: + - regression + - sanity + - smoke + +permissions: + contents: read + checks: write + statuses: write + +jobs: + build-and-test: + uses: ./.github/workflows/e2e-run.yml + secrets: inherit + with: + suite_name: ${{ github.event.inputs.test_suite }} + sha: ${{ github.sha }} diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml new file mode 100644 index 000000000..d934e5bba --- /dev/null +++ b/.github/workflows/e2e-pr.yml @@ -0,0 +1,24 @@ +name: "E2E: PR smoke tests" +on: + pull_request: + types: [ "opened", "reopened", "synchronize" ] + paths: + - "pom.xml" + - "contract/**" + - "api/**" + - "serde-api/**" + - "frontend/**" + - "e2e-tests/**" + +permissions: + contents: read + checks: write + statuses: write + +jobs: + build-and-test: + uses: ./.github/workflows/e2e-run.yml + secrets: inherit + with: + suite_name: "smoke" + sha: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/e2e-run.yml b/.github/workflows/e2e-run.yml new file mode 100644 index 000000000..e42e40c56 --- /dev/null +++ b/.github/workflows/e2e-run.yml @@ -0,0 +1,164 @@ +name: "E2E: Run tests" + +on: + workflow_call: + inputs: + suite_name: + description: 'Test suite name to run' + default: 'regression' + required: true + type: string + sha: + required: true + type: string + +permissions: + contents: read + checks: write + statuses: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ github.token }} + ref: ${{ inputs.sha }} + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'zulu' + cache: 'maven' + + - name: Build with Maven + id: build_app + run: | + ./mvnw -B -ntp versions:set -DnewVersion=${{ inputs.sha }} + ./mvnw -B -V -ntp clean install -Pprod -Dmaven.test.skip=true + + - name: Upload maven artifacts + uses: actions/upload-artifact@v4 + with: + name: artifacts + path: ~/.m2/repository/io/kafbat/ui/**/* + retention-days: 7 + + - name: Dump docker image + run: | + docker image save ghcr.io/kafbat/kafka-ui:latest > /tmp/image.tar + + - name: Upload docker image + uses: actions/upload-artifact@v4 + with: + name: image + path: /tmp/image.tar + retention-days: 7 + + tests: + runs-on: ubuntu-latest + needs: build + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ github.token }} + ref: ${{ inputs.sha }} + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'zulu' + cache: 'maven' + + - name: Download maven artifacts + uses: actions/download-artifact@v4 + with: + name: artifacts + path: ~/.m2/repository/io/kafbat/ui + + - name: Download docker image + uses: actions/download-artifact@v4 + with: + name: image + path: /tmp + + - name: Load Docker image + run: | + docker load --input /tmp/image.tar + + - name: Cache Docker images. + uses: ScribeMD/docker-cache@0.5.0 + with: + key: docker-${{ runner.os }}-${{ hashFiles('./e2e-tests/selenoid/selenoid-ci.yaml', './documentation/compose/e2e-tests.yaml') }} + + - name: Compose up + id: compose_app + # use the following command until #819 will be fixed # TODO recheck 819 + run: | + mkdir -p ./e2e-tests/target/selenoid-results/video + mkdir -p ./e2e-tests/target/selenoid-results/logs + docker-compose -f ./e2e-tests/selenoid/selenoid-ci.yaml up -d + docker-compose -f ./documentation/compose/e2e-tests.yaml up -d + + - name: Dump Docker logs on failure + if: failure() + uses: jwalton/gh-docker-logs@v2.2.2 + + - name: Run test suite + run: | + ./mvnw -B -ntp versions:set -DnewVersion=${{ inputs.sha }} + ./mvnw -B -V -ntp -Dsurefire.suiteXmlFiles='src/test/resources/${{ inputs.suite_name }}.xml' -f 'e2e-tests' test -Pprod + + - name: Upload allure reports artifact + if: '!cancelled()' + uses: actions/upload-artifact@v4 + with: + name: reports + path: ./e2e-tests/target/allure-results + retention-days: 7 + + reports: + runs-on: ubuntu-latest + needs: tests + if: ${{ !cancelled() && github.repository == 'kafbat/kafka-ui' }} + steps: + - name: Download allure reports artifact + uses: actions/download-artifact@v4 + with: + name: reports + path: ./e2e-tests/target/allure-results + + - name: Generate Allure report + uses: simple-elf/allure-report-action@v1.9 + id: allure-report + with: + allure_results: ./e2e-tests/target/allure-results + gh_pages: allure-results + allure_report: allure-report + subfolder: allure-results + report_url: "https://reports.kafbat.dev" + + - name: Upload allure report to R2 + uses: ryand56/r2-upload-action@latest + with: + source-dir: allure-history/allure-results + destination-dir: . + r2-bucket: "reports" + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} + r2-secret-access-key: ${{ secrets.R2_ACCESS_SECRET_KEY }} + + - name: Add allure link status check + uses: Sibz/github-status-action@v1.1.6 + with: + authToken: ${{secrets.GITHUB_TOKEN}} + context: "Click Details button to view Allure report" + state: "success" + sha: ${{ inputs.sha }} + target_url: https://reports.kafbat.dev/${{ github.run_number }} diff --git a/.github/workflows/e2e-weekly.yml b/.github/workflows/e2e-weekly.yml new file mode 100644 index 000000000..6b25c4679 --- /dev/null +++ b/.github/workflows/e2e-weekly.yml @@ -0,0 +1,17 @@ +name: "E2E: Weekly suite" +on: + schedule: + - cron: '0 1 * * 1' + +permissions: + contents: read + checks: write + statuses: write + +jobs: + build-and-test: + uses: ./.github/workflows/e2e-run.yml + secrets: inherit + with: + suite_name: "sanity" + sha: ${{ github.sha }} diff --git a/.github/workflows/welcome-first-time-contributors.yml b/.github/workflows/welcome-first-time-contributors.yml index 1ac861055..27cd67408 100644 --- a/.github/workflows/welcome-first-time-contributors.yml +++ b/.github/workflows/welcome-first-time-contributors.yml @@ -18,15 +18,22 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} issue-message: | - Hello there ${{ github.actor }}! 👋 + Hi ${{ github.actor }}! 👋 - Thank you and congratulations 🎉 for opening your very first issue in this project! 💖 + Welcome, and thank you for opening your first issue in the repo! - In case you want to claim this issue, please comment down below! We will try to get back to you as soon as we can. 👀 + Please wait for triaging by our maintainers. + + As development is carried out in our spare time, you can support us by sponsoring our activities or even funding the development of specific issues. + [Sponsorship link](https://github.com/kafbat) + + If you plan to raise a PR for this issue, please take a look at our [contributing guide](https://ui.docs.kafbat.io/development/contributing). pr-message: | - Hello there ${{ github.actor }}! 👋 + Hi ${{ github.actor }}! 👋 + + Welcome, and thank you for opening your first PR in the repo! - Thank you and congrats 🎉 for opening your first PR on this project! ✨ 💖 + Please wait for triaging by our maintainers. - We will try to review it soon! + Please take a look at our [contributing guide](https://ui.docs.kafbat.io/development/contributing). diff --git a/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java b/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java index 21eec879c..542cb2a27 100644 --- a/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java +++ b/api/src/main/java/io/kafbat/ui/KafkaUiApplication.java @@ -8,7 +8,7 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -@SpringBootApplication(exclude = LdapAutoConfiguration.class) +@SpringBootApplication @EnableScheduling @EnableAsync public class KafkaUiApplication { diff --git a/api/src/main/java/io/kafbat/ui/config/ReadOnlyModeFilter.java b/api/src/main/java/io/kafbat/ui/config/ReadOnlyModeFilter.java index 184eb8725..81165e988 100644 --- a/api/src/main/java/io/kafbat/ui/config/ReadOnlyModeFilter.java +++ b/api/src/main/java/io/kafbat/ui/config/ReadOnlyModeFilter.java @@ -5,6 +5,7 @@ import io.kafbat.ui.service.ClustersStorage; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.Set; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -23,6 +24,10 @@ public class ReadOnlyModeFilter implements WebFilter { private static final Pattern CLUSTER_NAME_REGEX = Pattern.compile("/api/clusters/(?[^/]++)"); + private static final Set SAFE_ENDPOINTS = Set.of( + Pattern.compile("/api/clusters/[^/]+/topics/[^/]+/(smartfilters)$") + ); + private final ClustersStorage clustersStorage; @NotNull @@ -35,10 +40,12 @@ public Mono filter(ServerWebExchange exchange, @NotNull WebFilterChain cha var path = exchange.getRequest().getPath().pathWithinApplication().value(); var decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8); + var matcher = CLUSTER_NAME_REGEX.matcher(decodedPath); if (!matcher.find()) { return chain.filter(exchange); } + var clusterName = matcher.group("clusterName"); var kafkaCluster = clustersStorage.getClusterByName(clusterName) .orElseThrow( @@ -49,6 +56,15 @@ public Mono filter(ServerWebExchange exchange, @NotNull WebFilterChain cha return chain.filter(exchange); } + var isSafeEndpoint = SAFE_ENDPOINTS + .stream() + .parallel() + .anyMatch(endpoint -> endpoint.matcher(decodedPath).matches()); + + if (isSafeEndpoint) { + return chain.filter(exchange); + } + return Mono.error(ReadOnlyModeException::new); } } diff --git a/api/src/main/java/io/kafbat/ui/config/auth/LdapProperties.java b/api/src/main/java/io/kafbat/ui/config/auth/LdapProperties.java index 138dc0c30..3478d3fbc 100644 --- a/api/src/main/java/io/kafbat/ui/config/auth/LdapProperties.java +++ b/api/src/main/java/io/kafbat/ui/config/auth/LdapProperties.java @@ -20,7 +20,7 @@ public class LdapProperties { @Value("${oauth2.ldap.activeDirectory:false}") private boolean isActiveDirectory; - @Value("${oauth2.ldap.aсtiveDirectory.domain:@null}") + @Value("${oauth2.ldap.activeDirectory.domain:@null}") private String activeDirectoryDomain; } diff --git a/api/src/main/java/io/kafbat/ui/config/auth/LdapSecurityConfig.java b/api/src/main/java/io/kafbat/ui/config/auth/LdapSecurityConfig.java index a292055da..1b5a8ca87 100644 --- a/api/src/main/java/io/kafbat/ui/config/auth/LdapSecurityConfig.java +++ b/api/src/main/java/io/kafbat/ui/config/auth/LdapSecurityConfig.java @@ -10,13 +10,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.core.support.LdapContextSource; @@ -43,7 +40,6 @@ @Configuration @EnableWebFluxSecurity @ConditionalOnProperty(value = "auth.type", havingValue = "LDAP") -@Import(LdapAutoConfiguration.class) @EnableConfigurationProperties(LdapProperties.class) @RequiredArgsConstructor @Slf4j @@ -52,18 +48,18 @@ public class LdapSecurityConfig { private final LdapProperties props; @Bean - public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, + public ReactiveAuthenticationManager authenticationManager(LdapContextSource ldapContextSource, LdapAuthoritiesPopulator authoritiesExtractor, AccessControlService acs) { var rbacEnabled = acs.isRbacEnabled(); - BindAuthenticator ba = new BindAuthenticator(contextSource); + BindAuthenticator ba = new BindAuthenticator(ldapContextSource); if (props.getBase() != null) { ba.setUserDnPatterns(new String[] {props.getBase()}); } if (props.getUserFilterSearchFilter() != null) { LdapUserSearch userSearch = new FilterBasedLdapUserSearch(props.getUserFilterSearchBase(), props.getUserFilterSearchFilter(), - contextSource); + ldapContextSource); ba.setUserSearch(userSearch); } @@ -88,8 +84,7 @@ public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSo } @Bean - @Primary - public BaseLdapPathContextSource contextSource() { + public LdapContextSource ldapContextSource() { LdapContextSource ctx = new LdapContextSource(); ctx.setUrl(props.getUrls()); ctx.setUserDn(props.getAdminUser()); @@ -99,7 +94,6 @@ public BaseLdapPathContextSource contextSource() { } @Bean - @Primary public DefaultLdapAuthoritiesPopulator ldapAuthoritiesExtractor(ApplicationContext context, BaseLdapPathContextSource contextSource, AccessControlService acs) { diff --git a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java index 0a722b455..600c9c4e6 100644 --- a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java +++ b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java @@ -2,6 +2,7 @@ import io.kafbat.ui.model.rbac.permission.AclAction; import io.kafbat.ui.model.rbac.permission.ApplicationConfigAction; +import io.kafbat.ui.model.rbac.permission.AuditAction; import io.kafbat.ui.model.rbac.permission.ClientQuotaAction; import io.kafbat.ui.model.rbac.permission.ClusterConfigAction; import io.kafbat.ui.model.rbac.permission.ConnectAction; @@ -33,7 +34,7 @@ public enum Resource { ACL(AclAction.values()), - AUDIT(AclAction.values()), + AUDIT(AuditAction.values()), CLIENT_QUOTAS(ClientQuotaAction.values()); diff --git a/api/src/main/java/io/kafbat/ui/service/KafkaConfigSanitizer.java b/api/src/main/java/io/kafbat/ui/service/KafkaConfigSanitizer.java index f2630175a..6cbf44e44 100644 --- a/api/src/main/java/io/kafbat/ui/service/KafkaConfigSanitizer.java +++ b/api/src/main/java/io/kafbat/ui/service/KafkaConfigSanitizer.java @@ -30,7 +30,8 @@ class KafkaConfigSanitizer { .add( "basic.auth.user.info", /* For Schema Registry credentials */ "password", "secret", "token", "key", ".*credentials.*", /* General credential patterns */ - "aws.access.*", "aws.secret.*", "aws.session.*" /* AWS-related credential patterns */ + "aws.access.*", "aws.secret.*", "aws.session.*", /* AWS-related credential patterns */ + "connection.uri" /* mongo credential patterns */ ) .build(); diff --git a/api/src/main/resources/application-local.yml b/api/src/main/resources/application-local.yml index b87349807..0c40ff079 100644 --- a/api/src/main/resources/application-local.yml +++ b/api/src/main/resources/application-local.yml @@ -40,7 +40,7 @@ dynamic.config.enabled: true oauth2: ldap: activeDirectory: false - aсtiveDirectory.domain: domain.com + activeDirectory.domain: domain.com auth: type: DISABLED diff --git a/api/src/test/java/io/kafbat/ui/service/KafkaConfigSanitizerTest.java b/api/src/test/java/io/kafbat/ui/service/KafkaConfigSanitizerTest.java index e1cacaf1b..34f293220 100644 --- a/api/src/test/java/io/kafbat/ui/service/KafkaConfigSanitizerTest.java +++ b/api/src/test/java/io/kafbat/ui/service/KafkaConfigSanitizerTest.java @@ -34,6 +34,9 @@ void obfuscateCredentials() { assertThat(sanitizer.sanitize("aws.secret.access.key", "secret")).isEqualTo("******"); assertThat(sanitizer.sanitize("aws.secretAccessKey", "secret")).isEqualTo("******"); assertThat(sanitizer.sanitize("aws.sessionToken", "secret")).isEqualTo("******"); + + //Mongo var sanitizing + assertThat(sanitizer.sanitize("connection.uri", "secret")).isEqualTo("******"); } @Test diff --git a/documentation/compose/ui-ldap.yaml b/documentation/compose/ui-ldap.yaml index bf8a8efd8..b7855dfdb 100644 --- a/documentation/compose/ui-ldap.yaml +++ b/documentation/compose/ui-ldap.yaml @@ -25,7 +25,7 @@ services: SPRING_LDAP_USER_FILTER_SEARCH_FILTER: "(&(uid={0})(objectClass=inetOrgPerson))" SPRING_LDAP_GROUP_FILTER_SEARCH_BASE: "ou=people,dc=planetexpress,dc=com" # OAUTH2.LDAP.ACTIVEDIRECTORY: true -# OAUTH2.LDAP.AСTIVEDIRECTORY.DOMAIN: "memelord.lol" +# OAUTH2.LDAP.ACTIVEDIRECTORY.DOMAIN: "memelord.lol" ldap: image: rroemhild/test-openldap:latest diff --git a/e2e-tests/.env.ci b/e2e-tests/.env.ci deleted file mode 100644 index cf6cd6d43..000000000 --- a/e2e-tests/.env.ci +++ /dev/null @@ -1,3 +0,0 @@ -USE_LOCAL_BROWSER=false -SHOULD_START_SELENOID=true -TURN_OFF_SCREENSHOTS=true diff --git a/e2e-tests/.gitignore b/e2e-tests/.gitignore index e1f37b83c..c52b34a36 100644 --- a/e2e-tests/.gitignore +++ b/e2e-tests/.gitignore @@ -1,6 +1,3 @@ .env build/ -allure-results/ -selenoid/video/ target/ -selenoid/logs/ diff --git a/e2e-tests/QASE.md b/e2e-tests/QASE.md deleted file mode 100644 index 84d021658..000000000 --- a/e2e-tests/QASE.md +++ /dev/null @@ -1,70 +0,0 @@ -### E2E integration with Qase.io TMS (for internal users) - -### Table of Contents - -- [Intro](#intro) -- [Set up Qase.io integration](#set-up-qase-integration) -- [Test case creation](#test-case-creation) -- [Test run reporting](#test-run-reporting) - -### Intro - -We're using [Qase.io](https://help.qase.io/en/) as TMS to keep test cases and accumulate test runs. -Integration is set up through API using [qase-api](https://mvnrepository.com/artifact/io.qase/qase-api) -and [qase-testng](https://mvnrepository.com/artifact/io.qase/qase-testng) libraries. - -### Set up Qase integration - -To set up integration locally add next VM option `-DQASEIO_API_TOKEN='%s'` -(add your [Qase token](https://app.qase.io/user/api/token) instead of '%s') into your run configuration - -### Test case creation - -All new test cases can be added into TMS by default if they have no QaseId and QaseTitle matching already existing -cases. -But to handle `@Suite` and `@Automation` we added custom QaseCreateListener. To create new test case for next sync with -Qase (see example `e2e-tests/src/test/java/io/kafbat/ui/qaseSuite/Template.java`): - -1. Create new class in `e2e-tests/src/test/java/io/kafbat/ui/qaseSuite/suit` -2. Inherit it from `e2e-tests/src/test/java/io/kafbat/ui/qaseSuite/BaseQaseTest.java` -3. Create new test method with some name inside the class and annotate it with: - -- `@Automation` (optional - Not automated by default) - to set one of automation states: NOT_AUTOMATED, TO_BE_AUTOMATED, - AUTOMATED -- `@QaseTitle` (required) - to set title for new test case and to check is there no existing cases with same title in - Qase.io -- `@Status` (optional - Draft by default) - to set one of case statuses: ACTUAL, DRAFT, DEPRECATED -- `@Suite` (optional) - to store new case in some existing package need to set its id, otherwise case will be stored in - the root -- `@Test` (required) - annotation from TestNG to specify this method as test - -4. Create new private void step methods with some name inside the same class and annotate it with - @io.qase.api.annotation.Step to specify this method as step. -5. Use defined step methods inside created test method in concrete order -6. If there are any additional cases to create you can repeat scenario in a new class -7. There are two ways to sync newly created cases in the framework with Qase.io: - -- sync can be performed locally - run new test classes with - already [set up Qase.io integration](#Set up Qase.io integration) -- also you can commit and push your changes, then - run [E2E Manual suite](https://github.com/kafbat/kafka-ui/actions/workflows/e2e-manual.yml) on your branch - -8. No test run in Qase.io will be created, new test case will be stored defined directory - in [project's repository](https://app.qase.io/project/KAFKAUI) -9. To add expected results into created test case edit in Qase.io manually - -### Test run reporting - -To handle manual test cases with status `Skipped` we added custom QaseResultListener. To create new test run: - -1. All test methods should be annotated with actual `@QaseId` -2. There are two ways to sync newly created cases in the framework with Qase.io: - -- run can be performed locally - run test classes (or suites) with - already [set up Qase.io integration](#Set up Qase.io integration), they will be labeled as `Automation CUSTOM suite` -- also you can commit and push your changes, then - run [E2E Automation suite](https://github.com/kafbat/kafka-ui/actions/workflows/e2e-automation.yml) on your branch - -3. All new test runs will be added into [project's test runs](https://app.qase.io/run/KAFKAUI) with corresponding label - using QaseId to identify existing cases -4. All test cases from manual suite are set up to have `Skipped` status in test runs to perform them manually diff --git a/e2e-tests/README.md b/e2e-tests/README.md index 83d8cfdec..89ffc6246 100644 --- a/e2e-tests/README.md +++ b/e2e-tests/README.md @@ -7,9 +7,7 @@ This repository is for E2E UI automation. - [Prerequisites](#prerequisites) - [How to install](#how-to-install) - [How to run checks](#how-to-run-checks) -- [Qase.io integration (for internal users)](#qase-integration) - [Reporting](#reporting) -- [Environments setup](#environments-setup) - [Test Data](#test-data) - [Actions](#actions) - [Checks](#checks) @@ -19,7 +17,7 @@ This repository is for E2E UI automation. ### Prerequisites - Docker & Docker-compose -- Java (install aarch64 jdk if you have M1/arm chip) +- Java (install arch64 jdk if you have M1/arm chip) - Maven ### How to install @@ -27,7 +25,7 @@ This repository is for E2E UI automation. ``` git clone https://github.com/kafbat/kafka-ui.git cd e2e-tests -docker pull selenoid/vnc_chrome:103.0 +docker pull selenoid/vnc_chrome:117.0 ``` ### How to run checks @@ -36,21 +34,17 @@ docker pull selenoid/vnc_chrome:103.0 ``` cd kafbat-ui -docker-compose -f e2e-tests/docker/selenoid-local.yaml up -d +docker-compose -f e2e-tests/selenoid/selenoid-local.yaml up -d docker-compose -f documentation/compose/e2e-tests.yaml up -d ``` -2. To run test suite select its name (options: regression, sanity, smoke) and put it instead %s into command below +2. To run test suite select its name (options: `regression`, `sanity`, `smoke`) and put it instead %s into command below ``` ./mvnw -Dsurefire.suiteXmlFiles='src/test/resources/%s.xml' -f 'e2e-tests' test -Pprod ``` -3. To run tests on your local Chrome browser just add next VM option to the Run Configuration - -``` --Dbrowser=local -``` +3. To run tests on your local Chrome browser check `selenoid-off` profile on Maven panel, also there you can check `headless-off` Expected Location of Chrome ``` @@ -60,32 +54,17 @@ Windows XP: %HOMEPATH%\Local Settings\Application Data\Google\Ch Windows Vista and newer: C:\Users%USERNAME%\AppData\Local\Google\Chrome\Application\chrome.exe ``` -### Qase integration - -Found instruction for Qase.io integration (for internal use only) at `e2e-tests/QASE.md` - ### Reporting -Reports are in `allure-results` folder. -If you have installed allure commandline [here](https://www.npmjs.com/package/allure-commandline)) -You can see allure report with command: +Screenshots are stored in `target/selenide-results/reports` folder. + +Reports are stored in `target/allure-results` folder. +If you have installed allure commandline [here](https://www.npmjs.com/package/allure-commandline), you can see allure report with command: ``` allure serve ``` -### Screenshots - -Reference screenshots are in `SCREENSHOTS_FOLDER` (default,`e2e-tests/screenshots`) - -### How to develop - -> ⚠️ todo - -### Setting for different environments - -> ⚠️ todo - ### Test Data > ⚠️ todo @@ -102,7 +81,6 @@ Reference screenshots are in `SCREENSHOTS_FOLDER` (default,`e2e-tests/screensho > ⚠️ todo -### Tips - -- install `Selenium UI Testing plugin` in IDEA +### How to develop +> ⚠️ todo diff --git a/e2e-tests/docker/selenoid-git.yaml b/e2e-tests/docker/selenoid-git.yaml deleted file mode 100644 index f4c5430f1..000000000 --- a/e2e-tests/docker/selenoid-git.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -version: '3' - -services: - - selenoid: - network_mode: bridge - image: aerokube/selenoid:1.10.7 - volumes: - - "../selenoid/config:/etc/selenoid" - - "/var/run/docker.sock:/var/run/docker.sock" - - "../selenoid/video:/opt/selenoid/video" - - "../selenoid/logs:/opt/selenoid/logs" - environment: - - OVERRIDE_VIDEO_OUTPUT_DIR=../selenoid/video - command: [ "-conf", "/etc/selenoid/browsersGit.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ] - ports: - - "4444:4444" - - selenoid-ui: - network_mode: bridge - image: aerokube/selenoid-ui:latest-release - links: - - selenoid - ports: - - "8081:8080" - command: [ "--selenoid-uri", "http://selenoid:4444" ] - - selenoid-chrome: - network_mode: bridge - image: selenoid/vnc_chrome:103.0 - extra_hosts: - - "host.docker.internal:host-gateway" diff --git a/e2e-tests/docker/selenoid-local.yaml b/e2e-tests/docker/selenoid-local.yaml deleted file mode 100644 index 9d7fb8e0b..000000000 --- a/e2e-tests/docker/selenoid-local.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -version: '3' - -services: - - selenoid: - network_mode: bridge - image: aerokube/selenoid:1.10.7 - volumes: - - "../selenoid/config:/etc/selenoid" - - "/var/run/docker.sock:/var/run/docker.sock" - - "../selenoid/video:/opt/selenoid/video" - - "../selenoid/logs:/opt/selenoid/logs" - environment: - - OVERRIDE_VIDEO_OUTPUT_DIR=../selenoid/video - command: [ "-conf", "/etc/selenoid/browsersLocal.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ] - ports: - - "4444:4444" - - selenoid-ui: - network_mode: bridge - image: aerokube/selenoid-ui:latest-release - links: - - selenoid - ports: - - "8081:8080" - command: [ "--selenoid-uri", "http://selenoid:4444" ] - - selenoid-chrome: - network_mode: bridge - image: selenoid/vnc_chrome:103.0 - extra_hosts: - - "host.docker.internal:host-gateway" diff --git a/e2e-tests/pom.xml b/e2e-tests/pom.xml index 9516808fd..c9b6babba 100644 --- a/e2e-tests/pom.xml +++ b/e2e-tests/pom.xml @@ -12,20 +12,13 @@ e2e-tests - 3.2.5 + 3.3.1 ${project.version} - 1.19.5 - 5.2.1 - 4.8.1 - 6.12.3 - 7.9.0 - 2.25.0 - 3.0.5 + 17 + 3.2.3 + 3.2.5 1.9.9.1 - 3.25.3 - 2.2 - 2.0.7 - 3.3.1 + 2.27.0 @@ -33,93 +26,17 @@ org.apache.kafka kafka_2.13 ${kafka.version} - - - io.netty - netty-buffer - - - io.netty - netty-common - - - io.netty - netty-codec - - - io.netty - netty-handler - - - io.netty - netty-resolver - - - io.netty - netty-transport - - - io.netty - netty-transport-native-epoll - - - io.netty - netty-transport-native-unix-common - - - - - - - io.netty - netty-buffer - - - io.netty - netty-common - - - io.netty - netty-codec - - - io.netty - netty-handler - io.netty - netty-resolver - - - io.netty - netty-transport - - - io.netty - netty-transport-native-epoll - - - io.netty - netty-transport-native-unix-common - - - io.netty - netty-resolver-dns-native-macos - osx-aarch_64 + io.kafbat.ui + contract + ${contract} - org.testcontainers - testcontainers - ${testcontainers.version} - - - org.testcontainers - selenium - ${testcontainers.version} + org.aspectj + aspectjweaver + ${aspectj.version} org.projectlombok @@ -127,79 +44,34 @@ ${org.projectlombok.version} - org.apache.httpcomponents.core5 - httpcore5 - ${httpcomponents.version} + commons-io + commons-io + 2.16.1 - org.apache.httpcomponents.client5 - httpclient5 - ${httpcomponents.version} - - - org.seleniumhq.selenium - selenium-http-jdk-client - ${selenium.version} - - - org.seleniumhq.selenium - selenium-http - ${selenium.version} + org.testng + testng + 7.10.0 com.codeborne selenide - ${selenide.version} - - - org.testng - testng - ${testng.version} + 7.2.3 io.qameta.allure - allure-selenide + allure-testng ${allure.version} io.qameta.allure - allure-testng + allure-selenide ${allure.version} - io.qase - qase-testng - ${qase.io.version} - - - io.qase - qase-api - ${qase.io.version} - - - org.hamcrest - hamcrest - ${hamcrest.version} - - - org.assertj - assertj-core - ${assertj.version} - - - org.aspectj - aspectjrt - ${aspectj.version} - - - org.slf4j - slf4j-simple - ${slf4j.version} - - - io.kafbat.ui - contract - ${contract} + io.github.bonigarcia + webdrivermanager + 5.8.0 @@ -210,28 +82,9 @@ true - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - - - - org.apache.maven.surefire - surefire-testng - ${maven.surefire-plugin.version} - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - + + true + prod @@ -240,17 +93,26 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven.surefire-plugin.version} + ${maven.surefire.release} -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" + + + ${headless.state} + + ${selenoid.state} + + ${project.basedir}/target/allure-results + + org.apache.maven.surefire surefire-testng - ${maven.surefire-plugin.version} + ${maven.surefire-testng} org.aspectj @@ -287,16 +149,27 @@ true true true - file:${basedir}/../etc/checkstyle/checkstyle-e2e.xml + file:${basedir}/../etc/checkstyle/checkstyle-e2e.xml + file:${basedir}/../etc/checkstyle/apache-header.txt - - + + e2e-headless-off + + false + + + + e2e-selenoid-off + + false + + diff --git a/e2e-tests/selenoid/config/browsersGit.json b/e2e-tests/selenoid/config/browsers-ci.json similarity index 66% rename from e2e-tests/selenoid/config/browsersGit.json rename to e2e-tests/selenoid/config/browsers-ci.json index 9e0186161..11b492c1b 100644 --- a/e2e-tests/selenoid/config/browsersGit.json +++ b/e2e-tests/selenoid/config/browsers-ci.json @@ -1,9 +1,9 @@ { "chrome": { - "default": "103.0", + "default": "117.0", "versions": { - "103.0": { - "image": "selenoid/vnc_chrome:103.0", + "117.0": { + "image": "selenoid/vnc_chrome:117.0", "hosts": [ "host.docker.internal:172.17.0.1" ], diff --git a/e2e-tests/selenoid/config/browsersLocal.json b/e2e-tests/selenoid/config/browsers-local.json similarity index 52% rename from e2e-tests/selenoid/config/browsersLocal.json rename to e2e-tests/selenoid/config/browsers-local.json index 35a494f33..af299a40b 100644 --- a/e2e-tests/selenoid/config/browsersLocal.json +++ b/e2e-tests/selenoid/config/browsers-local.json @@ -1,9 +1,9 @@ { "chrome": { - "default": "103.0", + "default": "117.0", "versions": { - "103.0": { - "image": "selenoid/vnc_chrome:103.0", + "117.0": { + "image": "selenoid/vnc_chrome:117.0", "port": "4444", "path": "/" } diff --git a/e2e-tests/selenoid/selenoid-ci.yaml b/e2e-tests/selenoid/selenoid-ci.yaml new file mode 100644 index 000000000..578e6d621 --- /dev/null +++ b/e2e-tests/selenoid/selenoid-ci.yaml @@ -0,0 +1,38 @@ +--- +version: '3' + +services: + + # https://github.com/aerokube/selenoid/releases + selenoid: + network_mode: bridge + image: aerokube/selenoid:1.11.2 + volumes: + - "../selenoid/config:/etc/selenoid" + - "/var/run/docker.sock:/var/run/docker.sock" + - "../target/selenoid-results/video:/opt/selenoid/video" + - "../target/selenoid-results/logs:/opt/selenoid/logs" + environment: + - OVERRIDE_VIDEO_OUTPUT_DIR=../docker/selenoid/video + ports: + - "4444:4444" + command: [ "-limit", "5", "-conf", "/etc/selenoid/browsers-ci.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ] + + # https://github.com/aerokube/selenoid-ui/releases + selenoid-ui: + network_mode: bridge + image: aerokube/selenoid-ui:1.10.11 + links: + - selenoid + ports: + - "8081:8080" + command: [ "--selenoid-uri", "http://selenoid:4444" ] + + # https://aerokube.com/images/latest + selenoid-chrome: + image: selenoid/vnc_chrome:117.0 + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - selenoid + - selenoid-ui diff --git a/e2e-tests/selenoid/selenoid-local.yaml b/e2e-tests/selenoid/selenoid-local.yaml new file mode 100644 index 000000000..a5e1f006f --- /dev/null +++ b/e2e-tests/selenoid/selenoid-local.yaml @@ -0,0 +1,38 @@ +--- +version: '3' + +services: + + # https://github.com/aerokube/selenoid/releases + selenoid: + network_mode: bridge + image: aerokube/selenoid:1.11.2 + volumes: + - "../selenoid/config:/etc/selenoid" + - "/var/run/docker.sock:/var/run/docker.sock" + - "../target/selenoid-results/video:/opt/selenoid/video" + - "../target/selenoid-results/logs:/opt/selenoid/logs" + environment: + - OVERRIDE_VIDEO_OUTPUT_DIR=../docker/selenoid/video + ports: + - "4444:4444" + command: [ "-limit", "5", "-conf", "/etc/selenoid/browsers-local.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ] + + # https://github.com/aerokube/selenoid-ui/releases + selenoid-ui: + network_mode: bridge + image: aerokube/selenoid-ui:1.10.11 + links: + - selenoid + ports: + - "8081:8080" + command: [ "--selenoid-uri", "http://selenoid:4444" ] + + # https://aerokube.com/images/latest + selenoid-chrome: + image: selenoid/vnc_chrome:117.0 + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - selenoid + - selenoid-ui diff --git a/e2e-tests/src/main/java/io/kafbat/ui/models/Connector.java b/e2e-tests/src/main/java/io/kafbat/ui/models/Connector.java index 2885e3302..3f1826ac4 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/models/Connector.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/models/Connector.java @@ -1,5 +1,7 @@ package io.kafbat.ui.models; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + import lombok.Data; import lombok.experimental.Accessors; @@ -8,4 +10,10 @@ public class Connector { private String name, config; + + public static Connector createConnector(String config) { + return new Connector() + .setName("aqa_connector_" + randomAlphabetic(5)) + .setConfig(config); + } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/models/Schema.java b/e2e-tests/src/main/java/io/kafbat/ui/models/Schema.java index edfdc3e10..9e89f5613 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/models/Schema.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/models/Schema.java @@ -18,19 +18,19 @@ public class Schema { public static Schema createSchemaAvro() { return new Schema().setName("schema_avro-" + randomAlphabetic(5)) .setType(SchemaType.AVRO) - .setValuePath(System.getProperty(USER_DIR) + "/src/main/resources/testData/schemas/schema_avro_value.json"); + .setValuePath(System.getProperty(USER_DIR) + "/src/main/resources/testdata/schemas/schema_avro_value.json"); } public static Schema createSchemaJson() { return new Schema().setName("schema_json-" + randomAlphabetic(5)) .setType(SchemaType.JSON) - .setValuePath(System.getProperty(USER_DIR) + "/src/main/resources/testData/schemas/schema_json_Value.json"); + .setValuePath(System.getProperty(USER_DIR) + "/src/main/resources/testdata/schemas/schema_json_value.json"); } public static Schema createSchemaProtobuf() { return new Schema().setName("schema_protobuf-" + randomAlphabetic(5)) .setType(SchemaType.PROTOBUF) .setValuePath( - System.getProperty(USER_DIR) + "/src/main/resources/testData/schemas/schema_protobuf_value.txt"); + System.getProperty(USER_DIR) + "/src/main/resources/testdata/schemas/schema_protobuf_value.txt"); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/models/Topic.java b/e2e-tests/src/main/java/io/kafbat/ui/models/Topic.java index 55d724b85..660e16339 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/models/Topic.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/models/Topic.java @@ -1,9 +1,11 @@ package io.kafbat.ui.models; -import io.kafbat.ui.pages.topics.enums.CleanupPolicyValue; -import io.kafbat.ui.pages.topics.enums.CustomParameterType; -import io.kafbat.ui.pages.topics.enums.MaxSizeOnDisk; -import io.kafbat.ui.pages.topics.enums.TimeToRetain; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + +import io.kafbat.ui.screens.topics.enums.CleanupPolicyValue; +import io.kafbat.ui.screens.topics.enums.CustomParameterType; +import io.kafbat.ui.screens.topics.enums.MaxSizeOnDisk; +import io.kafbat.ui.screens.topics.enums.TimeToRetain; import lombok.Data; import lombok.experimental.Accessors; @@ -17,4 +19,12 @@ public class Topic { private CleanupPolicyValue cleanupPolicyValue; private MaxSizeOnDisk maxSizeOnDisk; private TimeToRetain timeToRetain; + + public static Topic createTopic() { + return new Topic() + .setName("aqa_topic_" + randomAlphabetic(5)) + .setNumberOfPartitions(1) + .setMessageKey(randomAlphabetic(5)) + .setMessageValue(randomAlphabetic(10)); + } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/BasePage.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/BasePage.java similarity index 87% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/BasePage.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/BasePage.java index faee6ccce..11202e2da 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/BasePage.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/BasePage.java @@ -1,21 +1,23 @@ -package io.kafbat.ui.pages; +package io.kafbat.ui.screens; import static com.codeborne.selenide.Selenide.$$x; import static com.codeborne.selenide.Selenide.$x; +import static io.kafbat.ui.variables.Common.LOG_RESULT; import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; import com.codeborne.selenide.WebDriverRunner; -import io.kafbat.ui.pages.panels.enums.MenuItem; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.panels.enums.MenuItem; +import io.kafbat.ui.utilities.WebUtil; import java.time.Duration; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.openqa.selenium.Keys; import org.openqa.selenium.interactions.Actions; @Slf4j -public abstract class BasePage extends WebUtils { +public abstract class BasePage extends WebUtil { protected SelenideElement loadingSpinner = $x("//div[@role='progressbar']"); protected SelenideElement submitBtn = $x("//button[@type='submit']"); @@ -49,8 +51,14 @@ protected void waitUntilSpinnerDisappear(int... timeoutInSeconds) { } } + protected void clickClearSearchFieldButton() { + clickByActions(searchFld.$x("./../span[@role='button']/*")); + waitUntilSpinnerDisappear(1); + } + protected void searchItem(String tag) { log.debug("\nsearchItem: {}", tag); + clickClearSearchFieldButton(); sendKeysAfterClear(searchFld, tag); searchFld.pressEnter().shouldHave(Condition.value(tag)); waitUntilSpinnerDisappear(1); @@ -102,28 +110,28 @@ protected ElementsCollection getDdlOptions() { protected String getAlertHeader() { log.debug("\ngetAlertHeader"); String result = alertHeader.shouldBe(Condition.visible).getText(); - log.debug("-> {}", result); + log.debug(LOG_RESULT, result); return result; } protected String getAlertMessage() { log.debug("\ngetAlertMessage"); String result = alertMessage.shouldBe(Condition.visible).getText(); - log.debug("-> {}", result); + log.debug(LOG_RESULT, result); return result; } protected boolean isAlertVisible(AlertHeader header) { - log.debug("\nisAlertVisible: {}", header.toString()); - boolean result = getAlertHeader().equals(header.toString()); - log.debug("-> {}", result); + log.debug("\nisAlertVisible: {}", header.getValue()); + boolean result = getAlertHeader().equals(header.getValue()); + log.debug(LOG_RESULT, result); return result; } protected boolean isAlertVisible(AlertHeader header, String message) { log.debug("\nisAlertVisible: {} {}", header, message); boolean result = isAlertVisible(header) && getAlertMessage().equals(message); - log.debug("-> {}", result); + log.debug(LOG_RESULT, result); return result; } @@ -141,6 +149,7 @@ protected boolean isConfirmationModalVisible() { return isVisible(confirmationMdl); } + @Getter public enum AlertHeader { SUCCESS("Success"), VALIDATION_ERROR("Validation Error"), @@ -151,9 +160,5 @@ public enum AlertHeader { AlertHeader(String value) { this.value = value; } - - public String toString() { - return value; - } } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersConfigTab.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersConfigTab.java similarity index 93% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersConfigTab.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersConfigTab.java index 36e11a841..f2801cae9 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersConfigTab.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersConfigTab.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.brokers; +package io.kafbat.ui.screens.brokers; import static com.codeborne.selenide.Selenide.$$x; import static com.codeborne.selenide.Selenide.$x; @@ -7,8 +7,8 @@ import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.util.ArrayList; import java.util.List; @@ -17,7 +17,6 @@ public class BrokersConfigTab extends BasePage { - protected List editBtn = $$x("//button[@aria-label='editAction']"); protected SelenideElement searchByKeyField = $x("//input[@placeholder='Search by Key or Value']"); protected SelenideElement sourceInfoIcon = $x("//div[text()='Source']/..//div/div[@class]"); protected SelenideElement sourceInfoTooltip = $x("//div[text()='Source']/..//div/div[@style]"); @@ -43,7 +42,7 @@ public String getSourceInfoTooltipText() { @Step public boolean isSearchByKeyVisible() { - return WebUtils.isVisible(searchFld); + return WebUtil.isVisible(searchFld); } @Step @@ -59,7 +58,8 @@ public List getColumnHeaders() { } public List getEditButtons() { - return editBtns; + return editBtns.asFixedIterable().stream() + .collect(Collectors.toList()); } @Step @@ -115,7 +115,7 @@ public String getValue() { @Step public BrokersConfigItem setValue(String value) { - WebUtils.sendKeysAfterClear(getValueFld(), value); + WebUtil.sendKeysAfterClear(getValueFld(), value); return this; } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersDetails.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersDetails.java similarity index 88% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersDetails.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersDetails.java index a99d0078a..19ccc4292 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersDetails.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersDetails.java @@ -1,15 +1,16 @@ -package io.kafbat.ui.pages.brokers; +package io.kafbat.ui.screens.brokers; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import io.qameta.allure.Step; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.Getter; public class BrokersDetails extends BasePage { @@ -18,13 +19,13 @@ public class BrokersDetails extends BasePage { @Step public BrokersDetails waitUntilScreenReady() { waitUntilSpinnerDisappear(); - $x(String.format(brokersTabLocator, DetailsTab.LOG_DIRECTORIES)).shouldBe(Condition.visible); + $x(String.format(brokersTabLocator, DetailsTab.LOG_DIRECTORIES.getValue())).shouldBe(Condition.visible); return this; } @Step public BrokersDetails openDetailsTab(DetailsTab menu) { - $x(String.format(brokersTabLocator, menu.toString())).shouldBe(Condition.enabled).click(); + $x(String.format(brokersTabLocator, menu.getValue())).shouldBe(Condition.enabled).click(); waitUntilSpinnerDisappear(); return this; } @@ -49,7 +50,7 @@ private List getVisibleSummaryCells() { private List getDetailsTabs() { return Stream.of(DetailsTab.values()) - .map(name -> $x(String.format(brokersTabLocator, name))) + .map(tab -> $x(String.format(brokersTabLocator, tab.getValue()))) .collect(Collectors.toList()); } @@ -68,6 +69,7 @@ public List getAllVisibleElements() { return visibleElements; } + @Getter public enum DetailsTab { LOG_DIRECTORIES("Log directories"), CONFIGS("Configs"), @@ -78,9 +80,5 @@ public enum DetailsTab { DetailsTab(String value) { this.value = value; } - - public String toString() { - return value; - } } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersList.java similarity index 95% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersList.java index 5f2324699..3de449d5a 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/brokers/BrokersList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/brokers/BrokersList.java @@ -1,12 +1,12 @@ -package io.kafbat.ui.pages.brokers; +package io.kafbat.ui.screens.brokers; import static com.codeborne.selenide.Selenide.$x; -import static io.kafbat.ui.pages.panels.enums.MenuItem.BROKERS; +import static io.kafbat.ui.screens.panels.enums.MenuItem.BROKERS; import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import io.qameta.allure.Step; import java.util.ArrayList; import java.util.List; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorCreateForm.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorCreateForm.java similarity index 94% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorCreateForm.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorCreateForm.java index f84d264a5..0fe7f67c7 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorCreateForm.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorCreateForm.java @@ -1,10 +1,10 @@ -package io.kafbat.ui.pages.connectors; +package io.kafbat.ui.screens.connectors; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import io.qameta.allure.Step; public class ConnectorCreateForm extends BasePage { diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorDetails.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorDetails.java similarity index 84% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorDetails.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorDetails.java index f79c7adae..f2107fdbb 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/ConnectorDetails.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/ConnectorDetails.java @@ -1,11 +1,11 @@ -package io.kafbat.ui.pages.connectors; +package io.kafbat.ui.screens.connectors; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; public class ConnectorDetails extends BasePage { @@ -27,14 +27,14 @@ public ConnectorDetails waitUntilScreenReady() { @Step public ConnectorDetails openConfigTab() { - WebUtils.clickByJavaScript(configTab); + WebUtil.clickByJavaScript(configTab); return this; } @Step public ConnectorDetails setConfig(String configJson) { configField.shouldBe(Condition.enabled).click(); - WebUtils.clearByKeyboard(contentTextArea); + WebUtil.clearByKeyboard(contentTextArea); contentTextArea.setValue(configJson); configField.shouldBe(Condition.enabled).click(); return this; @@ -48,13 +48,13 @@ public ConnectorDetails clickSubmitButton() { @Step public ConnectorDetails openDotMenu() { - WebUtils.clickByJavaScript(dotMenuBtn); + WebUtil.clickByJavaScript(dotMenuBtn); return this; } @Step public ConnectorDetails clickDeleteBtn() { - WebUtils.clickByJavaScript(deleteBtn); + WebUtil.clickByJavaScript(deleteBtn); return this; } @@ -75,7 +75,7 @@ public ConnectorDetails deleteConnector() { @Step public boolean isConnectorHeaderVisible(String connectorName) { - return WebUtils.isVisible($x(String.format(connectorHeaderLocator, connectorName))); + return WebUtil.isVisible($x(String.format(connectorHeaderLocator, connectorName))); } @Step diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/KafkaConnectList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/KafkaConnectList.java similarity index 76% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/KafkaConnectList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/KafkaConnectList.java index 7ffaea488..5045e99d0 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/connectors/KafkaConnectList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/connectors/KafkaConnectList.java @@ -1,12 +1,12 @@ -package io.kafbat.ui.pages.connectors; +package io.kafbat.ui.screens.connectors; import static com.codeborne.selenide.Selenide.$x; -import static io.kafbat.ui.pages.panels.enums.MenuItem.KAFKA_CONNECT; +import static io.kafbat.ui.screens.panels.enums.MenuItem.KAFKA_CONNECT; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; @@ -27,7 +27,7 @@ public KafkaConnectList waitUntilScreenReady() { @Step public KafkaConnectList clickCreateConnectorBtn() { - WebUtils.clickByJavaScript(createConnectorBtn); + WebUtil.clickByJavaScript(createConnectorBtn); return this; } @@ -40,6 +40,6 @@ public KafkaConnectList openConnector(String connectorName) { @Step public boolean isConnectorVisible(String connectorName) { tableGrid.shouldBe(Condition.visible); - return WebUtils.isVisible(getTableElement(connectorName)); + return WebUtil.isVisible(getTableElement(connectorName)); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersDetails.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersDetails.java similarity index 70% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersDetails.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersDetails.java index 5de3bbb78..1312eb83e 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersDetails.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersDetails.java @@ -1,10 +1,10 @@ -package io.kafbat.ui.pages.consumers; +package io.kafbat.ui.screens.consumers; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; public class ConsumersDetails extends BasePage { @@ -21,12 +21,12 @@ public ConsumersDetails waitUntilScreenReady() { @Step public boolean isRedirectedConsumerTitleVisible(String consumerGroupId) { - return WebUtils.isVisible($x(String.format(consumerIdHeaderLocator, consumerGroupId))); + return WebUtil.isVisible($x(String.format(consumerIdHeaderLocator, consumerGroupId))); } @Step public boolean isTopicInConsumersDetailsVisible(String topicName) { tableGrid.shouldBe(Condition.visible); - return WebUtils.isVisible($x(String.format(topicElementLocator, topicName))); + return WebUtil.isVisible($x(String.format(topicElementLocator, topicName))); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersList.java similarity index 67% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersList.java index 77b9fd203..554b080a4 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/consumers/ConsumersList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/consumers/ConsumersList.java @@ -1,9 +1,9 @@ -package io.kafbat.ui.pages.consumers; +package io.kafbat.ui.screens.consumers; -import static io.kafbat.ui.pages.panels.enums.MenuItem.CONSUMERS; +import static io.kafbat.ui.screens.panels.enums.MenuItem.CONSUMERS; import com.codeborne.selenide.Condition; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import io.qameta.allure.Step; public class ConsumersList extends BasePage { diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlDbList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlDbList.java similarity index 92% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlDbList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlDbList.java index 2d6666018..95617e771 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlDbList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlDbList.java @@ -1,16 +1,16 @@ -package io.kafbat.ui.pages.ksqldb; +package io.kafbat.ui.screens.ksqldb; import static com.codeborne.selenide.Condition.visible; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$x; -import static io.kafbat.ui.pages.panels.enums.MenuItem.KSQL_DB; +import static io.kafbat.ui.screens.panels.enums.MenuItem.KSQL_DB; import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.pages.ksqldb.enums.KsqlMenuTabs; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.screens.ksqldb.enums.KsqlMenuTabs; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.time.Duration; import java.util.ArrayList; @@ -31,13 +31,13 @@ public KsqlDbList waitUntilScreenReady() { @Step public KsqlDbList clickExecuteKsqlRequestBtn() { - WebUtils.clickByJavaScript(executeKsqlBtn); + WebUtil.clickByJavaScript(executeKsqlBtn); return this; } @Step public KsqlDbList openDetailsTab(KsqlMenuTabs menu) { - $(By.linkText(menu.toString())).shouldBe(Condition.visible).click(); + $(By.linkText(menu.getValue())).shouldBe(Condition.visible).click(); waitUntilSpinnerDisappear(); return this; } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlQueryForm.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlQueryForm.java similarity index 88% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlQueryForm.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlQueryForm.java index b35b48ac2..1ef49cbb4 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/KsqlQueryForm.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/KsqlQueryForm.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.ksqldb; +package io.kafbat.ui.screens.ksqldb; import static com.codeborne.selenide.Condition.visible; import static com.codeborne.selenide.Selenide.$$x; @@ -9,8 +9,8 @@ import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.time.Duration; import java.util.ArrayList; @@ -38,7 +38,7 @@ public KsqlQueryForm waitUntilScreenReady() { @Step public KsqlQueryForm clickClearBtn() { - WebUtils.clickByJavaScript(clearBtn); + WebUtil.clickByJavaScript(clearBtn); sleep(500); return this; } @@ -50,7 +50,7 @@ public String getEnteredQuery() { @Step public KsqlQueryForm clickExecuteBtn(String query) { - WebUtils.clickByActions(executeBtn); + WebUtil.clickByActions(executeBtn); if (query.contains("EMIT CHANGES")) { abortButton.shouldBe(Condition.visible); } else { @@ -61,42 +61,42 @@ public KsqlQueryForm clickExecuteBtn(String query) { @Step public boolean isAbortBtnVisible() { - return WebUtils.isVisible(abortButton); + return WebUtil.isVisible(abortButton); } @Step public KsqlQueryForm clickAbortBtn() { - WebUtils.clickByActions(abortButton); + WebUtil.clickByActions(abortButton); return this; } @Step public boolean isCancelledAlertVisible() { - return WebUtils.isVisible(cancelledAlert); + return WebUtil.isVisible(cancelledAlert); } @Step public boolean isClearResultsBtnEnabled() { - return WebUtils.isEnabled(clearResultsBtn); + return WebUtil.isEnabled(clearResultsBtn); } @Step public KsqlQueryForm clickClearResultsBtn() { - WebUtils.clickByActions(clearResultsBtn); + WebUtil.clickByActions(clearResultsBtn); waitUntilSpinnerDisappear(); return this; } @Step public KsqlQueryForm clickAddStreamProperty() { - WebUtils.clickByActions(addStreamPropertyBtn); + WebUtil.clickByActions(addStreamPropertyBtn); return this; } @Step public KsqlQueryForm setQuery(String query) { queryAreaValue.shouldBe(Condition.visible).click(); - WebUtils.sendKeysByActions(queryArea, query); + WebUtil.sendKeysByActions(queryArea, query); return this; } @@ -111,7 +111,7 @@ public KsqlQueryForm.KsqlResponseGridItem getItemByName(String name) { public boolean areResultsVisible() { boolean visible = false; try { - visible = initItems().size() > 0; + visible = !initItems().isEmpty(); } catch (Throwable ignored) { } return visible; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlMenuTabs.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlMenuTabs.java similarity index 64% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlMenuTabs.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlMenuTabs.java index 0cf532919..628b1959d 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlMenuTabs.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlMenuTabs.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.ksqldb.enums; +package io.kafbat.ui.screens.ksqldb.enums; +import lombok.Getter; + +@Getter public enum KsqlMenuTabs { TABLES("Table"), @@ -10,8 +13,4 @@ public enum KsqlMenuTabs { KsqlMenuTabs(String value) { this.value = value; } - - public String toString() { - return value; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlQueryConfig.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlQueryConfig.java similarity index 73% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlQueryConfig.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlQueryConfig.java index a423d2856..83be9aee3 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/enums/KsqlQueryConfig.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/enums/KsqlQueryConfig.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.ksqldb.enums; +package io.kafbat.ui.screens.ksqldb.enums; +import lombok.Getter; + +@Getter public enum KsqlQueryConfig { SHOW_TABLES("show tables;"), @@ -11,8 +14,4 @@ public enum KsqlQueryConfig { KsqlQueryConfig(String query) { this.query = query; } - - public String getQuery() { - return query; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Stream.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Stream.java similarity index 79% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Stream.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Stream.java index 238767996..d31ed708a 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Stream.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Stream.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.ksqldb.models; +package io.kafbat.ui.screens.ksqldb.models; import lombok.Data; import lombok.experimental.Accessors; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Table.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Table.java similarity index 77% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Table.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Table.java index 27bef87dc..127deeee0 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/ksqldb/models/Table.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/ksqldb/models/Table.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.ksqldb.models; +package io.kafbat.ui.screens.ksqldb.models; import lombok.Data; import lombok.experimental.Accessors; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/NaviSideBar.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/NaviSideBar.java similarity index 87% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/panels/NaviSideBar.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/panels/NaviSideBar.java index 3898ad4e6..068485748 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/NaviSideBar.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/NaviSideBar.java @@ -1,13 +1,13 @@ -package io.kafbat.ui.pages.panels; +package io.kafbat.ui.screens.panels; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.pages.panels.enums.MenuItem; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.screens.panels.enums.MenuItem; import io.kafbat.ui.settings.BaseSource; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.time.Duration; import java.util.List; @@ -23,7 +23,7 @@ public class NaviSideBar extends BasePage { private SelenideElement expandCluster(String clusterName) { SelenideElement clusterElement = $x(String.format(clusterElementLocator, clusterName)).shouldBe(Condition.visible); if (clusterElement.parent().$$x(".//ul").size() == 0) { - WebUtils.clickByActions(clusterElement); + WebUtil.clickByActions(clusterElement); } return clusterElement; } @@ -44,7 +44,7 @@ public String getPagePath(MenuItem menuItem) { @Step public NaviSideBar openSideMenu(String clusterName, MenuItem menuItem) { - WebUtils.clickByActions(expandCluster(clusterName).parent() + WebUtil.clickByActions(expandCluster(clusterName).parent() .$x(String.format(sideMenuOptionElementLocator, menuItem.getNaviTitle()))); return this; } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/TopPanel.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/TopPanel.java similarity index 90% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/panels/TopPanel.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/panels/TopPanel.java index 31215c27b..92937260b 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/TopPanel.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/TopPanel.java @@ -1,15 +1,15 @@ -package io.kafbat.ui.pages.panels; +package io.kafbat.ui.screens.panels; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import java.util.Arrays; import java.util.List; public class TopPanel extends BasePage { - protected SelenideElement kafkaLogo = $x("//a[contains(text(),'Kafbat UI')]"); + protected SelenideElement kafkaLogo = $x("//a[contains(text(), 'kafbat UI')]"); protected SelenideElement kafkaVersion = $x("//a[@title='Current commit']"); protected SelenideElement logOutBtn = $x("//button[contains(text(),'Log out')]"); protected SelenideElement gitBtn = $x("//a[@href='https://github.com/kafbat/kafka-ui']"); diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/enums/MenuItem.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/enums/MenuItem.java similarity index 75% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/panels/enums/MenuItem.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/panels/enums/MenuItem.java index e117a10ee..283d428a1 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/panels/enums/MenuItem.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/panels/enums/MenuItem.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.panels.enums; +package io.kafbat.ui.screens.panels.enums; +import lombok.Getter; + +@Getter public enum MenuItem { DASHBOARD("Dashboard", "Dashboard"), @@ -17,12 +20,4 @@ public enum MenuItem { this.naviTitle = naviTitle; this.pageTitle = pageTitle; } - - public String getNaviTitle() { - return naviTitle; - } - - public String getPageTitle() { - return pageTitle; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaCreateForm.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaCreateForm.java similarity index 92% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaCreateForm.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaCreateForm.java index fa5084b1f..cb734ba86 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaCreateForm.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaCreateForm.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.schemas; +package io.kafbat.ui.screens.schemas; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$$x; @@ -6,12 +6,13 @@ import static org.openqa.selenium.By.id; import com.codeborne.selenide.Condition; +import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; import com.codeborne.selenide.WebDriverRunner; import io.kafbat.ui.api.model.CompatibilityLevel; import io.kafbat.ui.api.model.SchemaType; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.util.List; import java.util.stream.Collectors; @@ -31,9 +32,9 @@ public class SchemaCreateForm extends BasePage { protected SelenideElement latestSchemaTextArea = $x("//div[@id='latestSchema']"); protected SelenideElement leftVersionDdl = $(id("left-select")); protected SelenideElement rightVersionDdl = $(id("right-select")); - protected List visibleMarkers = + protected ElementsCollection visibleMarkers = $$x("//div[@class='ace_scroller']//div[contains(@class,'codeMarker')]"); - protected List elementsCompareVersionDdl = $$x("//ul[@role='listbox']/ul/li"); + protected ElementsCollection elementsCompareVersionDdl = $$x("//ul[@role='listbox']/ul/li"); protected String ddlElementLocator = "//li[@value='%s']"; @Step @@ -126,7 +127,7 @@ public List getAllDetailsPageElements() { @Step public boolean isSubmitBtnEnabled() { - return WebUtils.isEnabled(submitBtn); + return WebUtil.isEnabled(submitBtn); } @Step diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaDetails.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaDetails.java similarity index 89% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaDetails.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaDetails.java index 979e080ab..ebfc34697 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaDetails.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaDetails.java @@ -1,11 +1,11 @@ -package io.kafbat.ui.pages.schemas; +package io.kafbat.ui.screens.schemas; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; public class SchemaDetails extends BasePage { @@ -34,7 +34,7 @@ public String getCompatibility() { @Step public boolean isSchemaHeaderVisible(String schemaName) { - return WebUtils.isVisible($x(String.format(schemaHeaderLocator, schemaName))); + return WebUtil.isVisible($x(String.format(schemaHeaderLocator, schemaName))); } @Step @@ -61,7 +61,7 @@ public SchemaDetails openCompareVersionMenu() { @Step public SchemaDetails removeSchema() { - WebUtils.clickByJavaScript(dotMenuBtn); + WebUtil.clickByJavaScript(dotMenuBtn); removeBtn.shouldBe(Condition.enabled).click(); schemaConfirmBtn.shouldBe(Condition.visible).click(); schemaConfirmBtn.shouldBe(Condition.disappear); diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaRegistryList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaRegistryList.java similarity index 74% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaRegistryList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaRegistryList.java index eed9fa669..6061a70c5 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/schemas/SchemaRegistryList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/schemas/SchemaRegistryList.java @@ -1,12 +1,12 @@ -package io.kafbat.ui.pages.schemas; +package io.kafbat.ui.screens.schemas; import static com.codeborne.selenide.Selenide.$x; -import static io.kafbat.ui.pages.panels.enums.MenuItem.SCHEMA_REGISTRY; +import static io.kafbat.ui.screens.panels.enums.MenuItem.SCHEMA_REGISTRY; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; public class SchemaRegistryList extends BasePage { @@ -22,7 +22,7 @@ public SchemaRegistryList waitUntilScreenReady() { @Step public SchemaRegistryList clickCreateSchema() { - WebUtils.clickByJavaScript(createSchemaBtn); + WebUtil.clickByJavaScript(createSchemaBtn); return this; } @@ -36,7 +36,7 @@ public SchemaRegistryList openSchema(String schemaName) { @Step public boolean isSchemaVisible(String schemaName) { tableGrid.shouldBe(Condition.visible); - return WebUtils.isVisible(getTableElement(schemaName)); + return WebUtil.isVisible(getTableElement(schemaName)); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/ProduceMessagePanel.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/ProduceMessagePanel.java similarity index 86% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/ProduceMessagePanel.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/ProduceMessagePanel.java index 7dbb845fe..678cdfa70 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/ProduceMessagePanel.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/ProduceMessagePanel.java @@ -1,12 +1,12 @@ -package io.kafbat.ui.pages.topics; +package io.kafbat.ui.screens.topics; import static com.codeborne.selenide.Selenide.$x; import static com.codeborne.selenide.Selenide.refresh; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.util.Arrays; @@ -29,14 +29,14 @@ public ProduceMessagePanel waitUntilScreenReady() { @Step public ProduceMessagePanel setKeyField(String value) { - WebUtils.clearByKeyboard(keyTextArea); + WebUtil.clearByKeyboard(keyTextArea); keyTextArea.setValue(value); return this; } @Step public ProduceMessagePanel setValueFiled(String value) { - WebUtils.clearByKeyboard(valueTextArea); + WebUtil.clearByKeyboard(valueTextArea); valueTextArea.setValue(value); return this; } @@ -49,7 +49,7 @@ public ProduceMessagePanel setHeadersFld(String value) { @Step public ProduceMessagePanel submitProduceMessage() { - WebUtils.clickByActions(submitProduceMessageBtn); + WebUtil.clickByActions(submitProduceMessageBtn); submitProduceMessageBtn.shouldBe(Condition.disappear); refresh(); return this; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicCreateEditForm.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicCreateEditForm.java similarity index 90% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicCreateEditForm.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicCreateEditForm.java index a2eaf138f..00a3f65dd 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicCreateEditForm.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicCreateEditForm.java @@ -1,4 +1,4 @@ -package io.kafbat.ui.pages.topics; +package io.kafbat.ui.screens.topics; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$$; @@ -10,12 +10,12 @@ import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.pages.topics.enums.CleanupPolicyValue; -import io.kafbat.ui.pages.topics.enums.CustomParameterType; -import io.kafbat.ui.pages.topics.enums.MaxSizeOnDisk; -import io.kafbat.ui.pages.topics.enums.TimeToRetain; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.screens.topics.enums.CleanupPolicyValue; +import io.kafbat.ui.screens.topics.enums.CustomParameterType; +import io.kafbat.ui.screens.topics.enums.MaxSizeOnDisk; +import io.kafbat.ui.screens.topics.enums.TimeToRetain; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; public class TopicCreateEditForm extends BasePage { @@ -36,6 +36,7 @@ public class TopicCreateEditForm extends BasePage { protected SelenideElement validationCustomParameterValueMsg = $x("//p[contains(text(),'Value is required')]"); protected String ddlElementLocator = "//li[@value='%s']"; protected String btnTimeToRetainLocator = "//button[@class][text()='%s']"; + protected String customParamsElmCss = "ul[role=listbox][name^=customParams][name$=name]"; @Step @@ -46,20 +47,20 @@ public TopicCreateEditForm waitUntilScreenReady() { } public boolean isCreateTopicButtonEnabled() { - return WebUtils.isEnabled(submitBtn); + return WebUtil.isEnabled(submitBtn); } public boolean isDeleteCustomParameterButtonEnabled() { - return WebUtils.isEnabled(deleteCustomParameterBtn); + return WebUtil.isEnabled(deleteCustomParameterBtn); } public boolean isNameFieldEnabled() { - return WebUtils.isEnabled(nameField); + return WebUtil.isEnabled(nameField); } @Step public TopicCreateEditForm setTopicName(String topicName) { - WebUtils.sendKeysAfterClear(nameField, topicName); + WebUtil.sendKeysAfterClear(nameField, topicName); return this; } @@ -115,7 +116,7 @@ public TopicCreateEditForm setCustomParameterType(CustomParameterType customPara @Step public TopicCreateEditForm clearCustomParameterValue() { - WebUtils.clearByKeyboard(customParameterValueField); + WebUtil.clearByKeyboard(customParameterValueField); return this; } @@ -158,8 +159,7 @@ public TopicCreateEditForm clickSaveTopicBtn() { @Step public TopicCreateEditForm addCustomParameter(String customParameterName, String customParameterValue) { - ElementsCollection customParametersElements = - $$("ul[role=listbox][name^=customParams][name$=name]"); + ElementsCollection customParametersElements = $$(customParamsElmCss); KafkaUiSelectElement kafkaUiSelectElement = null; if (customParametersElements.size() == 1) { if ("Select".equals(customParametersElements.first().getText())) { @@ -169,7 +169,7 @@ public TopicCreateEditForm addCustomParameter(String customParameterName, $$("button") .find(Condition.exactText("Add Custom Parameter")) .click(); - customParametersElements = $$("ul[role=listbox][name^=customParams][name$=name]"); + customParametersElements = $$(customParamsElmCss); kafkaUiSelectElement = new KafkaUiSelectElement(customParametersElements.last()); } if (kafkaUiSelectElement != null) { @@ -183,7 +183,7 @@ public TopicCreateEditForm addCustomParameter(String customParameterName, @Step public TopicCreateEditForm updateCustomParameter(String customParameterName, String customParameterValue) { - SelenideElement selenideElement = $$("ul[role=listbox][name^=customParams][name$=name]") + SelenideElement selenideElement = $$(customParamsElmCss) .find(Condition.exactText(customParameterName)); String name = selenideElement.getAttribute("name"); if (name != null) { @@ -226,7 +226,7 @@ public TopicCreateEditForm setMaxMessageBytes(String bytes) { @Step public boolean isValidationMessageCustomParameterValueVisible() { - return WebUtils.isVisible(validationCustomParameterValueMsg); + return WebUtil.isVisible(validationCustomParameterValueMsg); } @Step diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicDetails.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicDetails.java similarity index 80% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicDetails.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicDetails.java index 8303f497d..1bb2756d3 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicDetails.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicDetails.java @@ -1,17 +1,19 @@ -package io.kafbat.ui.pages.topics; +package io.kafbat.ui.screens.topics; +import static com.codeborne.selenide.Condition.enabled; import static com.codeborne.selenide.Selenide.$$x; import static com.codeborne.selenide.Selenide.$x; import static com.codeborne.selenide.Selenide.sleep; -import static io.kafbat.ui.pages.topics.TopicDetails.TopicMenu.OVERVIEW; -import static org.testcontainers.shaded.org.apache.commons.lang3.RandomUtils.nextInt; +import static io.kafbat.ui.screens.topics.TopicDetails.TopicMenu.OVERVIEW; +import static org.apache.commons.lang3.RandomUtils.nextInt; import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.screens.topics.enums.SeekType; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.time.LocalDate; import java.time.LocalDateTime; @@ -25,6 +27,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import lombok.Getter; public class TopicDetails extends BasePage { @@ -34,7 +37,7 @@ public class TopicDetails extends BasePage { protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]"); protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]"); protected SelenideElement seekTypeDdl = $x("//ul[@id='selectSeekType']//li"); - protected SelenideElement seekTypeField = $x("//label[text()='Seek Type']//..//div/input"); + protected SelenideElement seekTypeField = $x("//ul[@id='selectSeekType']/../..//input"); protected SelenideElement addFiltersBtn = $x("//button[text()='Add Filters']"); protected SelenideElement savedFiltersLink = $x("//div[text()='Saved Filters']"); protected SelenideElement addFilterCodeModalTitle = $x("//label[text()='Filter code']"); @@ -43,17 +46,15 @@ public class TopicDetails extends BasePage { protected SelenideElement saveThisFilterCheckBoxAddFilterMdl = $x("//input[@name='saveFilter']"); protected SelenideElement displayNameInputAddFilterMdl = $x("//input[@placeholder='Enter Name']"); protected SelenideElement cancelBtnAddFilterMdl = $x("//button[text()='Cancel']"); - protected SelenideElement addFilterBtnAddFilterMdl = $x("//button[text()='Add filter']"); - protected SelenideElement saveFilterBtnEditFilterMdl = $x("//button[text()='Save']"); + protected SelenideElement addFilterBtnAddFilterMdl = $x("//button[text()='Add Filter']"); + protected SelenideElement saveFilterBtnEditFilterMdl = $x("//button[text()='Edit Filter']"); protected SelenideElement addFiltersBtnMessages = $x("//button[text()='Add Filters']"); - protected SelenideElement selectFilterBtnAddFilterMdl = $x("//button[text()='Select filter']"); protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]"); protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]"); protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']"); protected SelenideElement contentMessageTab = $x("//html//div[@id='root']/div/main//table//p"); protected SelenideElement cleanUpPolicyField = $x("//div[contains(text(),'Clean Up Policy')]/../span/*"); protected SelenideElement partitionsField = $x("//div[contains(text(),'Partitions')]/../span"); - protected SelenideElement backToCreateFiltersLink = $x("//div[text()='Back To create filters']"); protected ElementsCollection messageGridItems = $$x("//tbody//tr"); protected SelenideElement actualCalendarDate = $x("//div[@class='react-datepicker__current-month']"); protected SelenideElement previousMonthButton = $x("//button[@aria-label='Previous Month']"); @@ -66,19 +67,19 @@ public class TopicDetails extends BasePage { protected String consumerIdLocator = "//a[@title='%s']"; protected String topicHeaderLocator = "//h1[contains(text(),'%s')]"; protected String activeFilterNameLocator = "//div[@data-testid='activeSmartFilter']/div[1][contains(text(),'%s')]"; - protected String editActiveFilterBtnLocator = "//div[text()='%s']/../div[@data-testid='editActiveSmartFilterBtn']"; + protected String editActiveFilterBtnLocator = "//div[text()='%s']/../button[1]"; protected String settingsGridValueLocator = "//tbody/tr/td/span[text()='%s']//ancestor::tr/td[2]/span"; @Step public TopicDetails waitUntilScreenReady() { waitUntilSpinnerDisappear(); - $x(String.format(detailsTabLtr, OVERVIEW)).shouldBe(Condition.visible); + $x(String.format(detailsTabLtr, OVERVIEW.getValue())).shouldBe(Condition.visible); return this; } @Step public TopicDetails openDetailsTab(TopicMenu menu) { - $x(String.format(detailsTabLtr, menu.toString())).shouldBe(Condition.enabled).click(); + $x(String.format(detailsTabLtr, menu.getValue())).shouldBe(enabled).click(); waitUntilSpinnerDisappear(); return this; } @@ -90,7 +91,7 @@ public String getSettingsGridValueByKey(String key) { @Step public TopicDetails openDotMenu() { - WebUtils.clickByJavaScript(dotMenuBtn); + WebUtil.clickByJavaScript(dotMenuBtn); return this; } @@ -141,7 +142,7 @@ public int getPartitions() { @Step public boolean isTopicHeaderVisible(String topicName) { - return WebUtils.isVisible($x(String.format(topicHeaderLocator, topicName))); + return WebUtil.isVisible($x(String.format(topicHeaderLocator, topicName))); } @Step @@ -158,40 +159,40 @@ public TopicDetails clickConfirmBtnMdl() { @Step public TopicDetails clickProduceMessageBtn() { - WebUtils.clickByJavaScript(produceMessageBtn); + WebUtil.clickByJavaScript(produceMessageBtn); return this; } @Step - public TopicDetails selectSeekTypeDdlMessagesTab(String seekTypeName) { - seekTypeDdl.shouldBe(Condition.enabled).click(); - $x(String.format(seekFilterDdlLocator, seekTypeName)).shouldBe(Condition.visible).click(); + public TopicDetails selectSeekTypeDdlMessagesTab(SeekType seekType) { + seekTypeDdl.shouldBe(enabled).click(); + $x(String.format(seekFilterDdlLocator, seekType.getValue())).shouldBe(Condition.visible).click(); return this; } @Step public TopicDetails setSeekTypeValueFldMessagesTab(String seekTypeValue) { - seekTypeField.shouldBe(Condition.enabled).sendKeys(seekTypeValue); + seekTypeField.shouldBe(enabled).sendKeys(seekTypeValue); return this; } @Step public TopicDetails clickSubmitFiltersBtnMessagesTab() { - WebUtils.clickByJavaScript(submitBtn); + WebUtil.clickByJavaScript(submitBtn); waitUntilSpinnerDisappear(); return this; } @Step public TopicDetails clickMessagesAddFiltersBtn() { - addFiltersBtn.shouldBe(Condition.enabled).click(); + addFiltersBtn.shouldBe(enabled).click(); return this; } @Step public TopicDetails clickEditActiveFilterBtn(String filterName) { $x(String.format(editActiveFilterBtnLocator, filterName)) - .shouldBe(Condition.enabled).click(); + .shouldBe(enabled).click(); return this; } @@ -202,28 +203,14 @@ public TopicDetails clickNextButton() { return this; } - @Step - public TopicDetails openSavedFiltersListMdl() { - savedFiltersLink.shouldBe(Condition.enabled).click(); - backToCreateFiltersLink.shouldBe(Condition.visible); - return this; - } - @Step public boolean isFilterVisibleAtSavedFiltersMdl(String filterName) { - return WebUtils.isVisible($x(String.format(savedFilterNameLocator, filterName))); + return WebUtil.isVisible($x(String.format(savedFilterNameLocator, filterName))); } @Step public TopicDetails selectFilterAtSavedFiltersMdl(String filterName) { - $x(String.format(savedFilterNameLocator, filterName)).shouldBe(Condition.enabled).click(); - return this; - } - - @Step - public TopicDetails clickSelectFilterBtnAtSavedFiltersMdl() { - selectFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click(); - addFilterCodeModalTitle.shouldBe(Condition.disappear); + $x(String.format(savedFilterNameLocator, filterName)).shouldBe(enabled).click(); return this; } @@ -235,13 +222,14 @@ public TopicDetails waitUntilAddFiltersMdlVisible() { @Step public TopicDetails setFilterCodeFldAddFilterMdl(String filterCode) { - addFilterCodeTextarea.shouldBe(Condition.enabled).setValue(filterCode); + addFilterCodeTextarea.shouldBe(enabled).clear(); + addFilterCodeTextarea.sendKeys(filterCode); return this; } @Step public String getFilterCodeValue() { - addFilterCodeEditor.shouldBe(Condition.enabled).click(); + addFilterCodeEditor.shouldBe(enabled).click(); String value = addFilterCodeTextarea.getValue(); if (value == null) { return null; @@ -252,29 +240,29 @@ public String getFilterCodeValue() { @Step public String getFilterNameValue() { - return displayNameInputAddFilterMdl.shouldBe(Condition.enabled).getValue(); + return displayNameInputAddFilterMdl.shouldBe(enabled).getValue(); } @Step public TopicDetails selectSaveThisFilterCheckboxMdl(boolean select) { - WebUtils.selectElement(saveThisFilterCheckBoxAddFilterMdl, select); + WebUtil.selectElement(saveThisFilterCheckBoxAddFilterMdl, select); return this; } @Step public boolean isSaveThisFilterCheckBoxSelected() { - return WebUtils.isSelected(saveThisFilterCheckBoxAddFilterMdl); + return WebUtil.isSelected(saveThisFilterCheckBoxAddFilterMdl); } @Step public TopicDetails setDisplayNameFldAddFilterMdl(String displayName) { - displayNameInputAddFilterMdl.shouldBe(Condition.enabled).setValue(displayName); + displayNameInputAddFilterMdl.shouldBe(enabled).setValue(displayName); return this; } @Step public TopicDetails clickAddFilterBtnAndCloseMdl(boolean closeModal) { - addFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click(); + addFilterBtnAddFilterMdl.shouldBe(enabled).click(); if (closeModal) { addFilterCodeModalTitle.shouldBe(Condition.hidden); } else { @@ -285,7 +273,7 @@ public TopicDetails clickAddFilterBtnAndCloseMdl(boolean closeModal) { @Step public TopicDetails clickSaveFilterBtnAndCloseMdl(boolean closeModal) { - saveFilterBtnEditFilterMdl.shouldBe(Condition.enabled).click(); + saveFilterBtnEditFilterMdl.shouldBe(enabled).click(); if (closeModal) { addFilterCodeModalTitle.shouldBe(Condition.hidden); } else { @@ -296,22 +284,22 @@ public TopicDetails clickSaveFilterBtnAndCloseMdl(boolean closeModal) { @Step public boolean isAddFilterBtnAddFilterMdlEnabled() { - return WebUtils.isEnabled(addFilterBtnAddFilterMdl); + return WebUtil.isEnabled(addFilterBtnAddFilterMdl); } @Step public boolean isBackButtonEnabled() { - return WebUtils.isEnabled(backBtn); + return WebUtil.isEnabled(backBtn); } @Step public boolean isNextButtonEnabled() { - return WebUtils.isEnabled(nextBtn); + return WebUtil.isEnabled(nextBtn); } @Step public boolean isActiveFilterVisible(String filterName) { - return WebUtils.isVisible($x(String.format(activeFilterNameLocator, filterName))); + return WebUtil.isVisible($x(String.format(activeFilterNameLocator, filterName))); } @Step @@ -340,7 +328,7 @@ public TopicDetails openConsumerGroup(String consumerId) { private void selectYear(int expectedYear) { while (getActualCalendarDate().getYear() > expectedYear) { - WebUtils.clickByJavaScript(previousMonthButton); + WebUtil.clickByJavaScript(previousMonthButton); sleep(1000); if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) { throw new IllegalArgumentException("Unable to select year"); @@ -350,7 +338,7 @@ private void selectYear(int expectedYear) { private void selectMonth(int expectedMonth) { while (getActualCalendarDate().getMonthValue() > expectedMonth) { - WebUtils.clickByJavaScript(previousMonthButton); + WebUtil.clickByJavaScript(previousMonthButton); sleep(1000); if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) { throw new IllegalArgumentException("Unable to select month"); @@ -359,13 +347,13 @@ private void selectMonth(int expectedMonth) { } private void selectDay(int expectedDay) { - Objects.requireNonNull($$x(String.format(dayCellLtr, expectedDay)).stream() + Objects.requireNonNull($$x(String.format(dayCellLtr, expectedDay)).asFixedIterable().stream() .filter(day -> !Objects.requireNonNull(day.getAttribute("class")).contains("outside-month")) - .findFirst().orElseThrow()).shouldBe(Condition.enabled).click(); + .findFirst().orElseThrow()).shouldBe(enabled).click(); } private void setTime(LocalDateTime dateTime) { - calendarTimeFld.shouldBe(Condition.enabled) + calendarTimeFld.shouldBe(enabled) .sendKeys(String.valueOf(dateTime.getHour()), String.valueOf(dateTime.getMinute())); } @@ -390,7 +378,7 @@ private LocalDate getActualCalendarDate() { @Step public TopicDetails openCalendarSeekType() { - seekTypeField.shouldBe(Condition.enabled).click(); + seekTypeField.shouldBe(enabled).click(); actualCalendarDate.shouldBe(Condition.visible); return this; } @@ -431,6 +419,7 @@ public TopicDetails.MessageGridItem getRandomMessage() { return getMessageByOffset(nextInt(0, initItems().size() - 1)); } + @Getter public enum TopicMenu { OVERVIEW("Overview"), MESSAGES("Messages"), @@ -442,10 +431,6 @@ public enum TopicMenu { TopicMenu(String value) { this.value = value; } - - public String toString() { - return value; - } } public static class MessageGridItem extends BasePage { @@ -458,7 +443,7 @@ private MessageGridItem(SelenideElement element) { @Step public MessageGridItem clickExpand() { - WebUtils.clickByJavaScript(element.$x("./td[1]/span")); + WebUtil.clickByJavaScript(element.$x("./td[1]/span")); return this; } @@ -503,14 +488,14 @@ public MessageGridItem openDotMenu() { @Step public MessageGridItem clickCopyToClipBoard() { - WebUtils.clickByJavaScript(element.$x("./td[7]//li[text() = 'Copy to clipboard']") + WebUtil.clickByJavaScript(element.$x("./td[7]//li[text() = 'Copy to clipboard']") .shouldBe(Condition.visible)); return this; } @Step public MessageGridItem clickSaveAsFile() { - WebUtils.clickByJavaScript(element.$x("./td[7]//li[text() = 'Save as a file']") + WebUtil.clickByJavaScript(element.$x("./td[7]//li[text() = 'Save as a file']") .shouldBe(Condition.visible)); return this; } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicSettingsTab.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicSettingsTab.java similarity index 95% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicSettingsTab.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicSettingsTab.java index 936694ca9..ef32fe352 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicSettingsTab.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicSettingsTab.java @@ -1,11 +1,11 @@ -package io.kafbat.ui.pages.topics; +package io.kafbat.ui.screens.topics; import static com.codeborne.selenide.Selenide.$x; import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; +import io.kafbat.ui.screens.BasePage; import io.qameta.allure.Step; import java.util.ArrayList; import java.util.List; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicsList.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicsList.java similarity index 90% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicsList.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicsList.java index faddc9328..46334a6b2 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/TopicsList.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/TopicsList.java @@ -1,14 +1,14 @@ -package io.kafbat.ui.pages.topics; +package io.kafbat.ui.screens.topics; import static com.codeborne.selenide.Condition.visible; import static com.codeborne.selenide.Selenide.$x; -import static io.kafbat.ui.pages.panels.enums.MenuItem.TOPICS; +import static io.kafbat.ui.screens.panels.enums.MenuItem.TOPICS; import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.WebUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.utilities.WebUtil; import io.qameta.allure.Step; import java.util.ArrayList; import java.util.Arrays; @@ -38,31 +38,31 @@ public TopicsList waitUntilScreenReady() { @Step public TopicsList clickAddTopicBtn() { - WebUtils.clickByJavaScript(addTopicBtn); + WebUtil.clickByJavaScript(addTopicBtn); return this; } @Step public boolean isTopicVisible(String topicName) { tableGrid.shouldBe(visible); - return WebUtils.isVisible(getTableElement(topicName)); + return WebUtil.isVisible(getTableElement(topicName)); } @Step public boolean isShowInternalRadioBtnSelected() { - return WebUtils.isSelected(showInternalRadioBtn); + return WebUtil.isSelected(showInternalRadioBtn); } @Step public TopicsList setShowInternalRadioButton(boolean select) { if (select) { if (!showInternalRadioBtn.isSelected()) { - WebUtils.clickByJavaScript(showInternalRadioBtn); + WebUtil.clickByJavaScript(showInternalRadioBtn); waitUntilSpinnerDisappear(1); } } else { if (showInternalRadioBtn.isSelected()) { - WebUtils.clickByJavaScript(showInternalRadioBtn); + WebUtil.clickByJavaScript(showInternalRadioBtn); waitUntilSpinnerDisappear(1); } } @@ -94,7 +94,7 @@ public TopicsList openDotMenuByTopicName(String topicName) { @Step public boolean isCopySelectedTopicBtnEnabled() { - return WebUtils.isEnabled(copySelectedTopicBtn); + return WebUtil.isEnabled(copySelectedTopicBtn); } @Step @@ -117,19 +117,19 @@ public TopicsList clickPurgeMessagesOfSelectedTopicsBtn() { @Step public TopicsList clickClearMessagesBtn() { - WebUtils.clickByJavaScript(clearMessagesBtn.shouldBe(visible)); + WebUtil.clickByJavaScript(clearMessagesBtn.shouldBe(visible)); return this; } @Step public TopicsList clickRecreateTopicBtn() { - WebUtils.clickByJavaScript(recreateTopicBtn.shouldBe(visible)); + WebUtil.clickByJavaScript(recreateTopicBtn.shouldBe(visible)); return this; } @Step public TopicsList clickRemoveTopicBtn() { - WebUtils.clickByJavaScript(removeTopicBtn.shouldBe(visible)); + WebUtil.clickByJavaScript(removeTopicBtn.shouldBe(visible)); return this; } @@ -234,7 +234,7 @@ public TopicGridItem(SelenideElement element) { @Step public TopicsList selectItem(boolean select) { - WebUtils.selectElement(element.$x("./td[1]/input"), select); + WebUtil.selectElement(element.$x("./td[1]/input"), select); return new TopicsList(); } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CleanupPolicyValue.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CleanupPolicyValue.java similarity index 68% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CleanupPolicyValue.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CleanupPolicyValue.java index 3cf893cf5..eaa887157 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CleanupPolicyValue.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CleanupPolicyValue.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.topics.enums; +package io.kafbat.ui.screens.topics.enums; +import lombok.Getter; + +@Getter public enum CleanupPolicyValue { DELETE("delete", "Delete"), @@ -13,13 +16,4 @@ public enum CleanupPolicyValue { this.optionValue = optionValue; this.visibleText = visibleText; } - - public String getOptionValue() { - return optionValue; - } - - public String getVisibleText() { - return visibleText; - } } - diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CustomParameterType.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CustomParameterType.java similarity index 92% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CustomParameterType.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CustomParameterType.java index f0f868829..2e6e7e0f8 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/CustomParameterType.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/CustomParameterType.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.topics.enums; +package io.kafbat.ui.screens.topics.enums; +import lombok.Getter; + +@Getter public enum CustomParameterType { COMPRESSION_TYPE("compression.type"), @@ -30,8 +33,4 @@ public enum CustomParameterType { CustomParameterType(String optionValue) { this.optionValue = optionValue; } - - public String getOptionValue() { - return optionValue; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/MaxSizeOnDisk.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/MaxSizeOnDisk.java similarity index 71% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/MaxSizeOnDisk.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/MaxSizeOnDisk.java index 043e0dcb0..415f071be 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/MaxSizeOnDisk.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/MaxSizeOnDisk.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.topics.enums; +package io.kafbat.ui.screens.topics.enums; +import lombok.Getter; + +@Getter public enum MaxSizeOnDisk { NOT_SET("-1", "Not Set"), @@ -15,13 +18,4 @@ public enum MaxSizeOnDisk { this.optionValue = optionValue; this.visibleText = visibleText; } - - public String getOptionValue() { - return optionValue; - } - - public String getVisibleText() { - return visibleText; - } } - diff --git a/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/SeekType.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/SeekType.java new file mode 100644 index 000000000..d731ea266 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/SeekType.java @@ -0,0 +1,21 @@ +package io.kafbat.ui.screens.topics.enums; + +import lombok.Getter; + +@Getter +public enum SeekType { + + OLDEST("Oldest"), + NEWEST("Newest"), + LIVE("Live"), + FROM_OFFSET("From offset"), + TO_OFFSET("To offset"), + SINCE_TIME("Since time"), + TO_TIME("To time"); + + private final String value; + + SeekType(String value) { + this.value = value; + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/TimeToRetain.java b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/TimeToRetain.java similarity index 72% rename from e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/TimeToRetain.java rename to e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/TimeToRetain.java index cedc94309..9ff0cc0c1 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/pages/topics/enums/TimeToRetain.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/screens/topics/enums/TimeToRetain.java @@ -1,5 +1,8 @@ -package io.kafbat.ui.pages.topics.enums; +package io.kafbat.ui.screens.topics.enums; +import lombok.Getter; + +@Getter public enum TimeToRetain { BTN_12_HOURS("12 hours", "43200000"), @@ -15,12 +18,4 @@ public enum TimeToRetain { this.button = button; this.value = value; } - - public String getButton() { - return button; - } - - public String getValue() { - return value; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/services/ApiService.java b/e2e-tests/src/main/java/io/kafbat/ui/services/ApiService.java index 09bfb7fde..4a4e8e1a2 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/services/ApiService.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/services/ApiService.java @@ -1,7 +1,7 @@ package io.kafbat.ui.services; import static com.codeborne.selenide.Selenide.sleep; -import static io.kafbat.ui.utilities.FileUtils.fileToString; +import static io.kafbat.ui.utilities.FileUtil.fileToString; import com.fasterxml.jackson.databind.ObjectMapper; import io.kafbat.ui.api.ApiClient; @@ -20,8 +20,8 @@ import io.kafbat.ui.models.Connector; import io.kafbat.ui.models.Schema; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.ksqldb.models.Stream; -import io.kafbat.ui.pages.ksqldb.models.Table; +import io.kafbat.ui.screens.ksqldb.models.Stream; +import io.kafbat.ui.screens.ksqldb.models.Table; import io.kafbat.ui.settings.BaseSource; import io.qameta.allure.Step; import java.util.HashMap; diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/BaseSource.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/BaseSource.java index 8aec487e1..12063750a 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/BaseSource.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/settings/BaseSource.java @@ -1,28 +1,18 @@ package io.kafbat.ui.settings; -import io.kafbat.ui.settings.configs.Config; -import io.kafbat.ui.variables.Browser; -import org.aeonbits.owner.ConfigFactory; +import static io.kafbat.ui.utilities.BooleanUtil.parseBoolean; +import static io.kafbat.ui.utilities.StringUtil.getOptionalString; +import static org.apache.commons.lang3.BooleanUtils.TRUE; public abstract class BaseSource { + public static final boolean HEADLESS = parseBoolean(getOptionalString(TRUE, System.getProperty("headless"))); + public static final boolean SELENOID = parseBoolean(getOptionalString(TRUE, System.getProperty("selenoid"))); public static final String CLUSTER_NAME = "local"; public static final String CONNECT_NAME = "first"; private static final String LOCAL_HOST = "localhost"; public static final String REMOTE_URL = String.format("http://%s:4444/wd/hub", LOCAL_HOST); public static final String BASE_API_URL = String.format("http://%s:8080", LOCAL_HOST); - private static Config config; - public static final String BROWSER = config().browser(); - public static final String BASE_HOST = BROWSER.equals(Browser.LOCAL) - ? LOCAL_HOST - : "host.docker.internal"; + public static final String BASE_HOST = SELENOID ? "host.docker.internal" : LOCAL_HOST; public static final String BASE_UI_URL = String.format("http://%s:8080", BASE_HOST); - public static final String SUITE_NAME = config().suite(); - - private static Config config() { - if (config == null) { - config = ConfigFactory.create(Config.class, System.getProperties()); - } - return config; - } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Config.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Config.java deleted file mode 100644 index f45da7f21..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Config.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.kafbat.ui.settings.configs; - -public interface Config extends Profiles { -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Profiles.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Profiles.java deleted file mode 100644 index fcae8c7c3..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/configs/Profiles.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.kafbat.ui.settings.configs; - -import io.kafbat.ui.variables.Browser; -import io.kafbat.ui.variables.Suite; -import org.aeonbits.owner.Config; - -public interface Profiles extends Config { - - @Key("browser") - @DefaultValue(Browser.CONTAINER) - String browser(); - - @Key("suite") - @DefaultValue(Suite.CUSTOM) - String suite(); -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/drivers/WebDriver.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/drivers/WebDriver.java index 7d9bdad34..d956a0dd4 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/drivers/WebDriver.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/settings/drivers/WebDriver.java @@ -9,7 +9,6 @@ import com.codeborne.selenide.WebDriverRunner; import com.codeborne.selenide.logevents.SelenideLogger; import io.kafbat.ui.settings.BaseSource; -import io.kafbat.ui.variables.Browser; import io.qameta.allure.Step; import io.qameta.allure.selenide.AllureSelenide; import java.util.HashMap; @@ -20,34 +19,40 @@ @Slf4j public abstract class WebDriver { + private static final String MAC_OS_CHROME_BIN_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; + private static final String SELENIDE_RESULTS_PATH = "target/selenide-results"; + @Step public static void browserSetup() { - Configuration.headless = false; + Configuration.headless = BaseSource.HEADLESS; Configuration.browser = "chrome"; Configuration.browserSize = "1920x1080"; + Configuration.pageLoadTimeout = 180000; + Configuration.savePageSource = true; Configuration.screenshots = true; - Configuration.savePageSource = false; - Configuration.pageLoadTimeout = 120000; + Configuration.downloadsFolder = String.format("%s/downloads", SELENIDE_RESULTS_PATH); + Configuration.reportsFolder = String.format("%s/reports", SELENIDE_RESULTS_PATH); ChromeOptions chromeOptions = new ChromeOptions() - .addArguments("--no-sandbox") - .addArguments("--verbose") - .addArguments("--remote-allow-origins=*") + //.addArguments("--remote-allow-origins=*") .addArguments("--disable-dev-shm-usage") + .addArguments("--disable-extensions") .addArguments("--disable-gpu") + .addArguments("--no-sandbox") .addArguments("--lang=en_US"); - switch (BaseSource.BROWSER) { - case (Browser.LOCAL) -> Configuration.browserCapabilities = chromeOptions; - case (Browser.CONTAINER) -> { - Configuration.remote = BaseSource.REMOTE_URL; - Configuration.remoteConnectionTimeout = 180000; - Map selenoidOptions = new HashMap<>(); - selenoidOptions.put("enableVNC", true); - selenoidOptions.put("enableVideo", false); - chromeOptions.setCapability("selenoid:options", selenoidOptions); - Configuration.browserCapabilities = chromeOptions; - } - default -> throw new IllegalStateException("Unexpected value: " + BaseSource.BROWSER); + if (BaseSource.SELENOID) { + Configuration.remote = BaseSource.REMOTE_URL; + Configuration.remoteConnectionTimeout = 180000; + Configuration.remoteReadTimeout = 180000; + Map selenoidOptions = new HashMap<>(); + selenoidOptions.put("enableVNC", true); + selenoidOptions.put("enableLog", true); + selenoidOptions.put("enableVideo", false); + selenoidOptions.put("sessionTimeout", "30m"); + chromeOptions.setCapability("selenoid:options", selenoidOptions); + } else if (System.getProperty("os.name").equals("Mac OS X")) { + Configuration.browserBinary = MAC_OS_CHROME_BIN_PATH; } + Configuration.browserCapabilities = chromeOptions; } private static org.openqa.selenium.WebDriver getWebDriver() { @@ -68,15 +73,14 @@ public static void openUrl(String url) { } } - @Step - public static void browserInit() { - getWebDriver(); - } - @Step public static void browserClear() { - clearBrowserLocalStorage(); - clearBrowserCookies(); + getWebDriver(); + try { + clearBrowserCookies(); + clearBrowserLocalStorage(); + } catch (Throwable ignored) { + } refresh(); } @@ -93,9 +97,9 @@ public static void browserQuit() { } @Step - public static void loggerSetup() { + public static void selenideLoggerSetup() { SelenideLogger.addListener("AllureSelenide", new AllureSelenide() - .screenshots(true) - .savePageSource(false)); + .savePageSource(true) + .screenshots(true)); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/AllureListener.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/AllureListener.java index 67e24fb90..6bab8f9f9 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/AllureListener.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/AllureListener.java @@ -1,29 +1,30 @@ package io.kafbat.ui.settings.listeners; import static java.nio.file.Files.newInputStream; +import static org.apache.commons.lang3.ObjectUtils.isEmpty; import com.codeborne.selenide.Screenshots; import io.qameta.allure.Allure; +import io.qameta.allure.AllureLifecycle; import io.qameta.allure.testng.AllureTestNg; import java.io.File; import java.io.IOException; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.LoggerFactory; import org.testng.ITestListener; import org.testng.ITestResult; -@Slf4j public class AllureListener extends AllureTestNg implements ITestListener { private void takeScreenshot() { File screenshot = Screenshots.takeScreenShotAsFile(); try { - if (screenshot != null) { + if (!isEmpty(screenshot)) { Allure.addAttachment(screenshot.getName(), newInputStream(screenshot.toPath())); } else { - log.warn("Unable to take screenshot"); + LoggerFactory.getLogger(AllureLifecycle.class).error("Could not take screenshot"); } - } catch (IOException e) { - throw new RuntimeException(e); + } catch (IOException exception) { + throw new RuntimeException(exception); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/LoggerListener.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/LoggerListener.java deleted file mode 100644 index e92b117e8..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/LoggerListener.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.kafbat.ui.settings.listeners; - -import lombok.extern.slf4j.Slf4j; -import org.testng.ITestResult; -import org.testng.TestListenerAdapter; - -@Slf4j -public class LoggerListener extends TestListenerAdapter { - - @Override - public void onTestStart(final ITestResult testResult) { - log.info(String.format("\n------------------------------------------------------------------------ " - + "\nTEST STARTED: %s.%s \n------------------------------------------------------------------------ \n", - testResult.getInstanceName(), testResult.getName())); - } - - @Override - public void onTestSuccess(final ITestResult testResult) { - log.info(String.format("\n------------------------------------------------------------------------ " - + "\nTEST PASSED: %s.%s \n------------------------------------------------------------------------ \n", - testResult.getInstanceName(), testResult.getName())); - } - - @Override - public void onTestFailure(final ITestResult testResult) { - log.info(String.format("\n------------------------------------------------------------------------ " - + "\nTEST FAILED: %s.%s \n------------------------------------------------------------------------ \n", - testResult.getInstanceName(), testResult.getName())); - } - - @Override - public void onTestSkipped(final ITestResult testResult) { - log.info(String.format("\n------------------------------------------------------------------------ " - + "\nTEST SKIPPED: %s.%s \n------------------------------------------------------------------------ \n", - testResult.getInstanceName(), testResult.getName())); - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseCreateListener.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseCreateListener.java deleted file mode 100644 index 7ae2709ae..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseCreateListener.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.kafbat.ui.settings.listeners; - -import static io.qase.api.utils.IntegrationUtils.getCaseTitle; - -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.annotations.Status; -import io.kafbat.ui.utilities.qase.annotations.Suite; -import io.qase.api.QaseClient; -import io.qase.api.StepStorage; -import io.qase.api.annotation.QaseId; -import io.qase.client.ApiClient; -import io.qase.client.api.CasesApi; -import io.qase.client.model.GetCasesFiltersParameter; -import io.qase.client.model.ResultCreateStepsInner; -import io.qase.client.model.TestCase; -import io.qase.client.model.TestCaseCreate; -import io.qase.client.model.TestCaseCreateStepsInner; -import io.qase.client.model.TestCaseListResponse; -import io.qase.client.model.TestCaseListResponseAllOfResult; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.testng.Assert; -import org.testng.ITestListener; -import org.testng.ITestResult; -import org.testng.TestListenerAdapter; - -@Slf4j -public class QaseCreateListener extends TestListenerAdapter implements ITestListener { - - private static final CasesApi QASE_API = getQaseApi(); - - private static CasesApi getQaseApi() { - ApiClient apiClient = QaseClient.getApiClient(); - apiClient.setApiKey(System.getProperty("QASEIO_API_TOKEN")); - return new CasesApi(apiClient); - } - - private static int getStatus(Method method) { - if (method.isAnnotationPresent(Status.class)) { - return method.getDeclaredAnnotation(Status.class).status().getValue(); - } - return 1; - } - - private static int getAutomation(Method method) { - if (method.isAnnotationPresent(Automation.class)) { - return method.getDeclaredAnnotation(Automation.class).state().getValue(); - } - return 0; - } - - @SneakyThrows - private static HashMap getCaseTitlesAndIdsFromQase() { - HashMap cases = new HashMap<>(); - boolean getCases = true; - int offSet = 0; - while (getCases) { - getCases = false; - TestCaseListResponse response = QASE_API.getCases(System.getProperty("QASE_PROJECT_CODE"), - new GetCasesFiltersParameter().status(GetCasesFiltersParameter.SERIALIZED_NAME_STATUS), 100, offSet); - TestCaseListResponseAllOfResult result = response.getResult(); - Assert.assertNotNull(result); - List entities = result.getEntities(); - Assert.assertNotNull(entities); - if (entities.size() > 0) { - for (TestCase testCase : entities) { - cases.put(testCase.getId(), testCase.getTitle()); - } - offSet = offSet + 100; - getCases = true; - } - } - return cases; - } - - private static boolean isCaseWithTitleExistInQase(Method method) { - HashMap cases = getCaseTitlesAndIdsFromQase(); - String title = getCaseTitle(method); - if (cases.containsValue(title)) { - for (Map.Entry map : cases.entrySet()) { - if (map.getValue().matches(title)) { - long id = map.getKey(); - log.warn(String.format("Test case with @QaseTitle='%s' already exists with @QaseId=%d. " - + "Please verify @QaseTitle annotation", title, id)); - return true; - } - } - } - return false; - } - - @Override - @SneakyThrows - public void onTestSuccess(final ITestResult testResult) { - Method method = testResult.getMethod() - .getConstructorOrMethod() - .getMethod(); - String title = getCaseTitle(method); - if (!method.isAnnotationPresent(QaseId.class)) { - if (title != null) { - if (!isCaseWithTitleExistInQase(method)) { - LinkedList resultSteps = StepStorage.stopSteps(); - LinkedList createSteps = new LinkedList<>(); - resultSteps.forEach(step -> { - TestCaseCreateStepsInner caseStep = new TestCaseCreateStepsInner(); - caseStep.setAction(step.getAction()); - caseStep.setExpectedResult(step.getExpectedResult()); - createSteps.add(caseStep); - }); - TestCaseCreate newCase = new TestCaseCreate(); - newCase.setTitle(title); - newCase.setStatus(getStatus(method)); - newCase.setAutomation(getAutomation(method)); - newCase.setSteps(createSteps); - if (method.isAnnotationPresent(Suite.class)) { - long suiteId = method.getDeclaredAnnotation(Suite.class).id(); - newCase.suiteId(suiteId); - } - Long id = Objects.requireNonNull(QASE_API.createCase(System.getProperty("QASE_PROJECT_CODE"), - newCase).getResult()).getId(); - log.info(String.format("New test case '%s' was created with @QaseId=%d", title, id)); - } - } else { - log.warn("To create new test case in Qase.io please add @QaseTitle annotation"); - } - } else { - log.warn("To create new test case in Qase.io please remove @QaseId annotation"); - } - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseResultListener.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseResultListener.java deleted file mode 100644 index 1ac6bd076..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/QaseResultListener.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.kafbat.ui.settings.listeners; - -import static io.qase.api.utils.IntegrationUtils.getCaseId; -import static io.qase.api.utils.IntegrationUtils.getCaseTitle; -import static io.qase.api.utils.IntegrationUtils.getStacktrace; -import static io.qase.client.model.ResultCreate.StatusEnum.FAILED; -import static io.qase.client.model.ResultCreate.StatusEnum.PASSED; -import static io.qase.client.model.ResultCreate.StatusEnum.SKIPPED; - -import io.qase.api.StepStorage; -import io.qase.api.config.QaseConfig; -import io.qase.api.services.QaseTestCaseListener; -import io.qase.client.model.ResultCreate; -import io.qase.client.model.ResultCreateCase; -import io.qase.client.model.ResultCreateStepsInner; -import io.qase.testng.guice.module.TestNgModule; -import java.lang.reflect.Method; -import java.util.LinkedList; -import java.util.Optional; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.testng.ITestContext; -import org.testng.ITestListener; -import org.testng.ITestResult; -import org.testng.TestListenerAdapter; - -@Slf4j -public class QaseResultListener extends TestListenerAdapter implements ITestListener { - - private static final String REPORTER_NAME = "TestNG"; - - static { - System.setProperty(QaseConfig.QASE_CLIENT_REPORTER_NAME_KEY, REPORTER_NAME); - } - - @Getter(lazy = true, value = AccessLevel.PRIVATE) - private final QaseTestCaseListener qaseTestCaseListener = createQaseListener(); - - private static QaseTestCaseListener createQaseListener() { - return TestNgModule.getInjector().getInstance(QaseTestCaseListener.class); - } - - @Override - public void onTestStart(ITestResult tr) { - getQaseTestCaseListener().onTestCaseStarted(); - super.onTestStart(tr); - } - - @Override - public void onTestSuccess(ITestResult tr) { - getQaseTestCaseListener() - .onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, PASSED)); - super.onTestSuccess(tr); - } - - @Override - public void onTestSkipped(ITestResult tr) { - getQaseTestCaseListener() - .onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, SKIPPED)); - super.onTestSuccess(tr); - } - - @Override - public void onTestFailure(ITestResult tr) { - getQaseTestCaseListener() - .onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, FAILED)); - super.onTestFailure(tr); - } - - @Override - public void onFinish(ITestContext testContext) { - getQaseTestCaseListener().onTestCasesSetFinished(); - super.onFinish(testContext); - } - - private void setupResultItem(ResultCreate resultCreate, ITestResult result, ResultCreate.StatusEnum status) { - Optional resultThrowable = Optional.ofNullable(result.getThrowable()); - String comment = resultThrowable - .flatMap(throwable -> Optional.of(throwable.toString())).orElse(null); - Boolean isDefect = resultThrowable - .flatMap(throwable -> Optional.of(throwable instanceof AssertionError)) - .orElse(false); - String stacktrace = resultThrowable - .flatMap(throwable -> Optional.of(getStacktrace(throwable))) - .orElse(null); - Method method = result.getMethod() - .getConstructorOrMethod() - .getMethod(); - Long caseId = getCaseId(method); - String caseTitle = null; - if (caseId == null) { - caseTitle = getCaseTitle(method); - } - LinkedList steps = StepStorage.stopSteps(); - resultCreate - ._case(caseTitle == null ? null : new ResultCreateCase().title(caseTitle)) - .caseId(caseId) - .status(status) - .comment(comment) - .stacktrace(stacktrace) - .steps(steps.isEmpty() ? null : steps) - .defect(isDefect); - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/ResultsLogger.java b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/ResultsLogger.java new file mode 100644 index 000000000..a6291eee0 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/settings/listeners/ResultsLogger.java @@ -0,0 +1,93 @@ +package io.kafbat.ui.settings.listeners; + +import static io.kafbat.ui.utilities.StringUtil.getDuplicates; + +import java.text.DecimalFormat; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.testng.ITestContext; +import org.testng.ITestResult; +import org.testng.TestListenerAdapter; + +@Slf4j +public class ResultsLogger extends TestListenerAdapter { + + private static final String PREFIX = "[INFO] "; + private static final String PASSED_STATUS = "PASSED"; + private static final String FAILED_STATUS = "FAILED"; + private static final String SKIPPED_STATUS = "SKIPPED"; + + private static void logTestStatus(String status, ITestResult result) { + String testStatus = String.format("\n[INFO] TEST %s: %s", + status, result.getMethod().getQualifiedName()); + int dashCount = testStatus.length() - PREFIX.length(); + String separator = String.format("\n%s%s", PREFIX, + getDuplicates("-", dashCount - 1)); + log.info("{}{}{}", separator, testStatus, separator); + } + + private static String alignTitleToLength(String title, int target) { + int indentDiff = (title.length() - target) / 2; + String result = title.substring(indentDiff, title.length() - indentDiff); + if (result.length() > target) { + result = result.substring(1); + } + return result; + } + + private static int appendResults(StringBuilder suiteResults, Set testResults, String testStatus) { + if (testResults.isEmpty()) { + return 0; + } + StringBuilder subResults = new StringBuilder(); + subResults.append(String.format("\n%s\n%s%s TESTS: %d", PREFIX, PREFIX, testStatus, testResults.size())); + if (!testStatus.equals(PASSED_STATUS)) { + testResults.forEach(result -> + subResults.append(String.format("\n%s%s", PREFIX, result.getMethod().getQualifiedName()))); + } + suiteResults.append(subResults); + return testResults.size(); + } + + @Override + public void onTestStart(final ITestResult testResult) { + logTestStatus("STARTED", testResult); + } + + @Override + public void onTestSuccess(final ITestResult testResult) { + logTestStatus(PASSED_STATUS, testResult); + } + + @Override + public void onTestFailure(final ITestResult testResult) { + logTestStatus(FAILED_STATUS, testResult); + } + + @Override + public void onTestSkipped(final ITestResult testResult) { + logTestStatus(SKIPPED_STATUS, testResult); + } + + @Override + public void onFinish(ITestContext context) { + String separator = getDuplicates("=", 72); + String titleIndent = getDuplicates("-", separator.length() / 2); + String header = String.format("%s< %s >%s", titleIndent, context.getName(), titleIndent); + header = alignTitleToLength(header, separator.length()); + String logHeader = String.format("\n%s%s\n%s%s", PREFIX, separator, PREFIX, header); + StringBuilder suiteResults = new StringBuilder(); + suiteResults.append(logHeader); + int testCount = 0; + Set passedResults = context.getPassedTests().getAllResults(); + testCount += appendResults(suiteResults, passedResults, PASSED_STATUS); + testCount += appendResults(suiteResults, context.getFailedTests().getAllResults(), FAILED_STATUS); + testCount += appendResults(suiteResults, context.getSkippedTests().getAllResults(), SKIPPED_STATUS); + String total = new DecimalFormat("##0.00%").format((double) passedResults.size() / testCount); + String footer = String.format("%s[ %s ]%s", titleIndent, total, titleIndent); + footer = alignTitleToLength(footer, separator.length()); + String logFooter = String.format("\n%s\n%s%s\n%s%s", PREFIX, PREFIX, footer, PREFIX, separator); + suiteResults.append(logFooter); + log.info(suiteResults.toString()); + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/BooleanUtil.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/BooleanUtil.java new file mode 100644 index 000000000..38b4179b5 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/BooleanUtil.java @@ -0,0 +1,28 @@ +package io.kafbat.ui.utilities; + +import static io.kafbat.ui.utilities.StringUtil.clearString; +import static org.apache.commons.lang3.BooleanUtils.FALSE; +import static org.apache.commons.lang3.BooleanUtils.TRUE; +import static org.apache.commons.lang3.ObjectUtils.isEmpty; + +import org.apache.commons.lang3.StringUtils; + +public class BooleanUtil { + + public static boolean getOptionalBoolean(boolean defaultValue, boolean... customValue) { + return !isEmpty(customValue) ? customValue[0] : defaultValue; + } + + public static boolean parseBoolean(String original) { + String cleanStr = clearString(original); + if (StringUtils.isEmpty(cleanStr)) { + throw new IllegalStateException("Unexpected value: " + original); + } else if (cleanStr.equalsIgnoreCase(TRUE)) { + return true; + } else if (cleanStr.equalsIgnoreCase(FALSE)) { + return false; + } else { + throw new IllegalStateException("Unexpected value: " + original); + } + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtil.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtil.java new file mode 100644 index 000000000..85c277158 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtil.java @@ -0,0 +1,26 @@ +package io.kafbat.ui.utilities; + +import static org.apache.kafka.common.utils.Utils.readFileAsString; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; + +public class FileUtil { + + public static String resourceToString(String resourcePath) { + try { + return IOUtils.resourceToString("/" + resourcePath, StandardCharsets.UTF_8); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + + public static String fileToString(String filePath) { + try { + return readFileAsString(filePath); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtils.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtils.java deleted file mode 100644 index d88a92d7d..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/FileUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.kafbat.ui.utilities; - -import static org.apache.kafka.common.utils.Utils.readFileAsString; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import org.testcontainers.shaded.org.apache.commons.io.IOUtils; - -public class FileUtils { - - public static String getResourceAsString(String resourceFileName) { - try { - return IOUtils.resourceToString("/" + resourceFileName, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static String fileToString(String path) { - try { - return readFileAsString(path); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/IntUtil.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/IntUtil.java new file mode 100644 index 000000000..d2d8c3064 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/IntUtil.java @@ -0,0 +1,22 @@ +package io.kafbat.ui.utilities; + +import static org.apache.commons.lang3.ObjectUtils.isEmpty; + +import java.util.Objects; + +public class IntUtil { + + public static int getIntegerFromString(String original, boolean validate) { + String cleanStr = isEmpty(original) ? null + : original.replaceAll("(\\D+)", ""); + int result = 0; + try { + result = Integer.parseInt(Objects.requireNonNull(cleanStr)); + } catch (Throwable throwable) { + if (validate) { + throw new IllegalArgumentException(String.format("Unable to parse string '%s'", original)); + } + } + return result; + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtil.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtil.java new file mode 100644 index 000000000..487832494 --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtil.java @@ -0,0 +1,44 @@ +package io.kafbat.ui.utilities; + +import static org.apache.commons.lang3.ObjectUtils.isEmpty; + +import java.util.stream.IntStream; + +public class StringUtil { + + public static String getOptionalString(String defaultValue, String... customValue) { + return !isEmpty(customValue) && !isEmpty(customValue[0]) ? customValue[0] : defaultValue; + } + + public static String clearString(String original) { + if (original != null) { + String cleanStr = original + .replaceAll("\n", " ") + .replaceAll("\r", " ") + .replaceAll("\t", " ") + .trim(); + while (cleanStr.contains(" ")) { + cleanStr = cleanStr.replace(" ", " "); + } + return cleanStr; + } else { + return null; + } + } + + public static String getMixedCase(String original) { + return IntStream.range(0, original.length()) + .mapToObj(i -> i % 2 == 0 ? Character.toUpperCase(original.charAt(i)) : original.charAt(i)) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } + + public static String getDuplicates(String toDuplicate, int count) { + StringBuilder result = new StringBuilder(); + while (count > 0) { + result.append(toDuplicate); + count--; + } + return result.toString(); + } +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtils.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtils.java deleted file mode 100644 index ba5f25480..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/StringUtils.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.kafbat.ui.utilities; - -import java.util.stream.IntStream; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class StringUtils { - - public static String getMixedCase(String original) { - return IntStream.range(0, original.length()) - .mapToObj(i -> i % 2 == 0 ? Character.toUpperCase(original.charAt(i)) : original.charAt(i)) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString(); - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtils.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtil.java similarity index 71% rename from e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtils.java rename to e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtil.java index f926ef6a8..99d03404e 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtils.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/TimeUtil.java @@ -1,16 +1,15 @@ package io.kafbat.ui.utilities; -import static com.codeborne.selenide.Selenide.sleep; - +import com.codeborne.selenide.Selenide; import java.time.LocalTime; import lombok.extern.slf4j.Slf4j; @Slf4j -public class TimeUtils { +public class TimeUtil { public static void waitUntilNewMinuteStarted() { int secondsLeft = 60 - LocalTime.now().getSecond(); log.debug("\nwaitUntilNewMinuteStarted: {}s", secondsLeft); - sleep(secondsLeft * 1000); + Selenide.sleep(secondsLeft * 1000); } } diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtils.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtil.java similarity index 65% rename from e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtils.java rename to e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtil.java index 6c02f658f..5c7ab20d6 100644 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtils.java +++ b/e2e-tests/src/main/java/io/kafbat/ui/utilities/WebUtil.java @@ -1,24 +1,23 @@ package io.kafbat.ui.utilities; -import static com.codeborne.selenide.Selenide.executeJavaScript; +import static io.kafbat.ui.variables.Common.LOG_RESULT; import com.codeborne.selenide.Condition; +import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; -import com.codeborne.selenide.WebDriverRunner; import java.time.Duration; import lombok.extern.slf4j.Slf4j; import org.openqa.selenium.Keys; -import org.openqa.selenium.interactions.Actions; @Slf4j -public class WebUtils { +public class WebUtil { - public static int getTimeout(int... timeoutInSeconds) { + private static int getTimeout(int... timeoutInSeconds) { return (timeoutInSeconds != null && timeoutInSeconds.length > 0) ? timeoutInSeconds[0] : 4; } public static void sendKeysAfterClear(SelenideElement element, String keys) { - log.debug("\nsendKeysAfterClear: {} \nsend keys '{}'", element.getSearchCriteria(), keys); + log.debug("\nsendKeysAfterClear: {{}} \nsend keys '{}'", element.getSearchCriteria(), keys); element.shouldBe(Condition.enabled).clear(); if (keys != null) { element.sendKeys(keys); @@ -26,76 +25,77 @@ public static void sendKeysAfterClear(SelenideElement element, String keys) { } public static void clickByActions(SelenideElement element) { - log.debug("\nclickByActions: {}", element.getSearchCriteria()); + log.debug("\nclickByActions: {{}}", element.getSearchCriteria()); element.shouldBe(Condition.enabled); - new Actions(WebDriverRunner.getWebDriver()) + Selenide.actions() .moveToElement(element) .click(element) .perform(); } public static void sendKeysByActions(SelenideElement element, String keys) { - log.debug("\nsendKeysByActions: {} \nsend keys '{}'", element.getSearchCriteria(), keys); + log.debug("\nsendKeysByActions: {{}} \nsend keys '{}'", element.getSearchCriteria(), keys); element.shouldBe(Condition.enabled); - new Actions(WebDriverRunner.getWebDriver()) + Selenide.actions() .moveToElement(element) .sendKeys(element, keys) .perform(); } public static void clickByJavaScript(SelenideElement element) { - log.debug("\nclickByJavaScript: {}", element.getSearchCriteria()); + log.debug("\nclickByJavaScript: {{}}", element.getSearchCriteria()); element.shouldBe(Condition.enabled); String script = "arguments[0].click();"; - executeJavaScript(script, element); + Selenide.executeJavaScript(script, element); } public static void clearByKeyboard(SelenideElement field) { - log.debug("\nclearByKeyboard: {}", field.getSearchCriteria()); + log.debug("\nclearByKeyboard: {{}}", field.getSearchCriteria()); field.shouldBe(Condition.enabled).sendKeys(Keys.END); field.sendKeys(Keys.chord(Keys.CONTROL + "a"), Keys.DELETE); } public static boolean isVisible(SelenideElement element, int... timeoutInSeconds) { - log.debug("\nisVisible: {}", element.getSearchCriteria()); + log.debug("\nisVisible: {{}}", element.getSearchCriteria()); boolean isVisible = false; try { element.shouldBe(Condition.visible, Duration.ofSeconds(getTimeout(timeoutInSeconds))); isVisible = true; - } catch (Throwable e) { - log.debug("{} is not visible", element.getSearchCriteria()); + } catch (Throwable ignored) { } + log.debug(LOG_RESULT, isVisible); return isVisible; } public static boolean isEnabled(SelenideElement element, int... timeoutInSeconds) { - log.debug("\nisEnabled: {}", element.getSearchCriteria()); + log.debug("\nisEnabled: {{}}", element.getSearchCriteria()); boolean isEnabled = false; try { element.shouldBe(Condition.enabled, Duration.ofSeconds(getTimeout(timeoutInSeconds))); isEnabled = true; - } catch (Throwable e) { - log.debug("{} is not enabled", element.getSearchCriteria()); + } catch (Throwable ignored) { } + log.debug(LOG_RESULT, isEnabled); return isEnabled; } public static boolean isSelected(SelenideElement element, int... timeoutInSeconds) { - log.debug("\nisSelected: {}", element.getSearchCriteria()); + log.debug("\nisSelected: {{}}", element.getSearchCriteria()); boolean isSelected = false; try { element.shouldBe(Condition.selected, Duration.ofSeconds(getTimeout(timeoutInSeconds))); isSelected = true; - } catch (Throwable e) { - log.debug("{} is not selected", element.getSearchCriteria()); + } catch (Throwable ignored) { } + log.debug(LOG_RESULT, isSelected); return isSelected; } public static void selectElement(SelenideElement element, boolean select) { + log.debug("\nselectElement: {{}} \nselect '{}'", element.getSearchCriteria(), select); if (select) { if (!element.isSelected()) { clickByJavaScript(element); diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/QaseSetup.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/QaseSetup.java deleted file mode 100644 index 405a9ecee..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/QaseSetup.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.kafbat.ui.utilities.qase; - -import static io.kafbat.ui.settings.BaseSource.SUITE_NAME; -import static org.apache.commons.lang3.BooleanUtils.FALSE; -import static org.apache.commons.lang3.BooleanUtils.TRUE; -import static org.apache.commons.lang3.StringUtils.isEmpty; - -import io.kafbat.ui.settings.BaseSource; -import io.kafbat.ui.variables.Suite; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class QaseSetup { - - public static void qaseIntegrationSetup() { - String qaseApiToken = System.getProperty("QASEIO_API_TOKEN"); - if (isEmpty(qaseApiToken)) { - log.warn("Integration with Qase is disabled due to run config or token wasn't defined."); - System.setProperty("QASE_ENABLE", FALSE); - } else { - log.warn("Integration with Qase is enabled. Find this run at https://app.qase.io/run/KAFKAUI."); - String automation = SUITE_NAME.equalsIgnoreCase(Suite.MANUAL) ? "" : "Automation "; - System.setProperty("QASE_ENABLE", TRUE); - System.setProperty("QASE_PROJECT_CODE", "KAFKAUI"); - System.setProperty("QASE_API_TOKEN", qaseApiToken); - System.setProperty("QASE_USE_BULK", TRUE); - System.setProperty("QASE_RUN_NAME", DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") - .format(OffsetDateTime.now(ZoneOffset.UTC)) + ": " + automation + SUITE_NAME.toUpperCase() + " suite"); - } - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Automation.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Automation.java deleted file mode 100644 index a384f70ee..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Automation.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.kafbat.ui.utilities.qase.annotations; - -import io.kafbat.ui.utilities.qase.enums.State; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Automation { - - State state(); -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Status.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Status.java deleted file mode 100644 index 446b2cfbb..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Status.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.kafbat.ui.utilities.qase.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Status { - - io.kafbat.ui.utilities.qase.enums.Status status(); -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Suite.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Suite.java deleted file mode 100644 index 2f13ace63..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/annotations/Suite.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.kafbat.ui.utilities.qase.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Suite { - - long id(); -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/State.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/State.java deleted file mode 100644 index b4f7bb814..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/State.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.kafbat.ui.utilities.qase.enums; - -public enum State { - - NOT_AUTOMATED(0), - TO_BE_AUTOMATED(1), - AUTOMATED(2); - - private final int value; - - State(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/Status.java b/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/Status.java deleted file mode 100644 index 2e98d7d1b..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/utilities/qase/enums/Status.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.kafbat.ui.utilities.qase.enums; - -public enum Status { - - ACTUAL(0), - DRAFT(1), - DEPRECATED(2); - - private final int value; - - Status(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/variables/Common.java b/e2e-tests/src/main/java/io/kafbat/ui/variables/Common.java new file mode 100644 index 000000000..33517d7dc --- /dev/null +++ b/e2e-tests/src/main/java/io/kafbat/ui/variables/Common.java @@ -0,0 +1,19 @@ +package io.kafbat.ui.variables; + +public interface Common { + + String LOG_RESULT = "-> {}"; + String BROKER_SOURCE_INFO_TOOLTIP = + """ + Dynamic topic config = dynamic topic config that is configured for a specific topic + Dynamic broker logger config = dynamic broker logger config that is configured for a specific broker + Dynamic broker config = dynamic broker config that is configured for a specific broker + Dynamic default broker config = dynamic broker config that is configured as default \ + for all brokers in the cluster + Static broker config = static broker config provided as broker properties at start up \ + (e.g. server.properties file) + Default config = built-in default configuration for configs that have a default value + Unknown = source unknown e.g. in the ConfigEntry used for alter requests where source is not set"""; + String FILTER_CODE_STRING = "has(record.keyAsText) && record.keyAsText.matches(\".*[Gg]roovy.*\")"; + String FILTER_CODE_JSON = "has(record.key.name.first) && record.key.name.first == 'user1'"; +} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/variables/Expected.java b/e2e-tests/src/main/java/io/kafbat/ui/variables/Expected.java deleted file mode 100644 index 3bd4ee7c0..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/variables/Expected.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.kafbat.ui.variables; - -public interface Expected { - - String BROKER_SOURCE_INFO_TOOLTIP = - "DYNAMIC_TOPIC_CONFIG = dynamic topic config that is configured for a specific topic\n" - + "DYNAMIC_BROKER_LOGGER_CONFIG = dynamic broker logger config that is configured for a specific broker\n" - + "DYNAMIC_BROKER_CONFIG = dynamic broker config that is configured for a specific broker\n" - + "DYNAMIC_DEFAULT_BROKER_CONFIG = dynamic broker config that is configured as default " - + "for all brokers in the cluster\n" - + "STATIC_BROKER_CONFIG = static broker config provided as broker properties at start up " - + "(e.g. server.properties file)\n" - + "DEFAULT_CONFIG = built-in default configuration for configs that have a default value\n" - + "UNKNOWN = source unknown e.g. in the ConfigEntry used for alter requests where source is not set"; -} diff --git a/e2e-tests/src/main/java/io/kafbat/ui/variables/Suite.java b/e2e-tests/src/main/java/io/kafbat/ui/variables/Suite.java deleted file mode 100644 index 635336f0b..000000000 --- a/e2e-tests/src/main/java/io/kafbat/ui/variables/Suite.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.kafbat.ui.variables; - -public interface Suite { - - String CUSTOM = "custom"; - String MANUAL = "manual"; - String REGRESSION = "regression"; - String SANITY = "sanity"; - String SMOKE = "smoke"; -} diff --git a/e2e-tests/src/main/resources/allure.properties b/e2e-tests/src/main/resources/allure.properties deleted file mode 100644 index bdab607a4..000000000 --- a/e2e-tests/src/main/resources/allure.properties +++ /dev/null @@ -1,2 +0,0 @@ -allure.results.directory=allure-results -allure.link.issue.pattern=https://github.com/kafbat/kafka-ui/issues/{} diff --git a/e2e-tests/src/main/resources/testData/connectors/config_for_create_connector.json b/e2e-tests/src/main/resources/testdata/connectors/create_config.json similarity index 100% rename from e2e-tests/src/main/resources/testData/connectors/config_for_create_connector.json rename to e2e-tests/src/main/resources/testdata/connectors/create_config.json diff --git a/e2e-tests/src/main/resources/testData/connectors/config_for_create_connector_via_api.json b/e2e-tests/src/main/resources/testdata/connectors/create_config_api.json similarity index 100% rename from e2e-tests/src/main/resources/testData/connectors/config_for_create_connector_via_api.json rename to e2e-tests/src/main/resources/testdata/connectors/create_config_api.json diff --git a/e2e-tests/src/main/resources/testData/connectors/delete_connector_config.json b/e2e-tests/src/main/resources/testdata/connectors/delete_config.json similarity index 100% rename from e2e-tests/src/main/resources/testData/connectors/delete_connector_config.json rename to e2e-tests/src/main/resources/testdata/connectors/delete_config.json diff --git a/e2e-tests/src/main/resources/testData/connectors/config_for_update_connector.json b/e2e-tests/src/main/resources/testdata/connectors/update_config.json similarity index 100% rename from e2e-tests/src/main/resources/testData/connectors/config_for_update_connector.json rename to e2e-tests/src/main/resources/testdata/connectors/update_config.json diff --git a/e2e-tests/src/main/resources/testData/schemas/schema_avro_for_update.json b/e2e-tests/src/main/resources/testdata/schemas/schema_avro_update.json similarity index 100% rename from e2e-tests/src/main/resources/testData/schemas/schema_avro_for_update.json rename to e2e-tests/src/main/resources/testdata/schemas/schema_avro_update.json diff --git a/e2e-tests/src/main/resources/testData/schemas/schema_avro_value.json b/e2e-tests/src/main/resources/testdata/schemas/schema_avro_value.json similarity index 100% rename from e2e-tests/src/main/resources/testData/schemas/schema_avro_value.json rename to e2e-tests/src/main/resources/testdata/schemas/schema_avro_value.json diff --git a/e2e-tests/src/main/resources/testData/schemas/schema_json_Value.json b/e2e-tests/src/main/resources/testdata/schemas/schema_json_value.json similarity index 100% rename from e2e-tests/src/main/resources/testData/schemas/schema_json_Value.json rename to e2e-tests/src/main/resources/testdata/schemas/schema_json_value.json diff --git a/e2e-tests/src/main/resources/testData/schemas/schema_protobuf_value.txt b/e2e-tests/src/main/resources/testdata/schemas/schema_protobuf_value.txt similarity index 100% rename from e2e-tests/src/main/resources/testData/schemas/schema_protobuf_value.txt rename to e2e-tests/src/main/resources/testdata/schemas/schema_protobuf_value.txt diff --git a/e2e-tests/src/main/resources/testData/topics/message_content_create_topic.json b/e2e-tests/src/main/resources/testdata/topics/create_topic_content.json similarity index 100% rename from e2e-tests/src/main/resources/testData/topics/message_content_create_topic.json rename to e2e-tests/src/main/resources/testdata/topics/create_topic_content.json diff --git a/e2e-tests/src/test/java/io/kafbat/ui/BaseTest.java b/e2e-tests/src/test/java/io/kafbat/ui/BaseTest.java index f7489c6cc..15e7a84ec 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/BaseTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/BaseTest.java @@ -1,15 +1,13 @@ package io.kafbat.ui; -import com.codeborne.selenide.Condition; -import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; -import io.kafbat.ui.pages.panels.enums.MenuItem; +import com.codeborne.selenide.WebElementCondition; +import io.github.bonigarcia.wdm.WebDriverManager; +import io.kafbat.ui.screens.panels.enums.MenuItem; import io.kafbat.ui.settings.BaseSource; import io.kafbat.ui.settings.drivers.WebDriver; import io.kafbat.ui.settings.listeners.AllureListener; -import io.kafbat.ui.settings.listeners.LoggerListener; -import io.kafbat.ui.settings.listeners.QaseResultListener; -import io.kafbat.ui.utilities.qase.QaseSetup; +import io.kafbat.ui.settings.listeners.ResultsLogger; import io.qameta.allure.Step; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -21,13 +19,13 @@ import org.testng.asserts.SoftAssert; @Slf4j -@Listeners({AllureListener.class, LoggerListener.class, QaseResultListener.class}) +@Listeners({AllureListener.class, ResultsLogger.class}) public abstract class BaseTest extends Facade { @BeforeSuite(alwaysRun = true) public void beforeSuite() { - QaseSetup.qaseIntegrationSetup(); - WebDriver.loggerSetup(); + WebDriverManager.chromedriver().setup(); + WebDriver.selenideLoggerSetup(); WebDriver.browserSetup(); } @@ -38,7 +36,7 @@ public void afterSuite() { @BeforeMethod(alwaysRun = true) public void beforeMethod() { - Selenide.open(BaseSource.BASE_UI_URL); + WebDriver.openUrl(BaseSource.BASE_UI_URL); naviSideBar.waitUntilScreenReady(); } @@ -57,10 +55,8 @@ protected void navigateToBrokers() { @Step protected void navigateToBrokersAndOpenDetails(int brokerId) { - naviSideBar - .openSideMenu(MenuItem.BROKERS); + navigateToBrokers(); brokersList - .waitUntilScreenReady() .openBroker(brokerId); brokersDetails .waitUntilScreenReady(); @@ -135,7 +131,7 @@ protected void navigateToKsqlDb() { } @Step - protected void verifyElementsCondition(List elementList, Condition expectedCondition) { + protected void verifyElementsCondition(List elementList, WebElementCondition expectedCondition) { SoftAssert softly = new SoftAssert(); elementList.forEach(element -> softly.assertTrue(element.is(expectedCondition), element.getSearchCriteria() + " is " + expectedCondition)); diff --git a/e2e-tests/src/test/java/io/kafbat/ui/Facade.java b/e2e-tests/src/test/java/io/kafbat/ui/Facade.java index 3b9433ee3..e740753e5 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/Facade.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/Facade.java @@ -1,25 +1,25 @@ package io.kafbat.ui; -import io.kafbat.ui.pages.brokers.BrokersConfigTab; -import io.kafbat.ui.pages.brokers.BrokersDetails; -import io.kafbat.ui.pages.brokers.BrokersList; -import io.kafbat.ui.pages.connectors.ConnectorCreateForm; -import io.kafbat.ui.pages.connectors.ConnectorDetails; -import io.kafbat.ui.pages.connectors.KafkaConnectList; -import io.kafbat.ui.pages.consumers.ConsumersDetails; -import io.kafbat.ui.pages.consumers.ConsumersList; -import io.kafbat.ui.pages.ksqldb.KsqlDbList; -import io.kafbat.ui.pages.ksqldb.KsqlQueryForm; -import io.kafbat.ui.pages.panels.NaviSideBar; -import io.kafbat.ui.pages.panels.TopPanel; -import io.kafbat.ui.pages.schemas.SchemaCreateForm; -import io.kafbat.ui.pages.schemas.SchemaDetails; -import io.kafbat.ui.pages.schemas.SchemaRegistryList; -import io.kafbat.ui.pages.topics.ProduceMessagePanel; -import io.kafbat.ui.pages.topics.TopicCreateEditForm; -import io.kafbat.ui.pages.topics.TopicDetails; -import io.kafbat.ui.pages.topics.TopicSettingsTab; -import io.kafbat.ui.pages.topics.TopicsList; +import io.kafbat.ui.screens.brokers.BrokersConfigTab; +import io.kafbat.ui.screens.brokers.BrokersDetails; +import io.kafbat.ui.screens.brokers.BrokersList; +import io.kafbat.ui.screens.connectors.ConnectorCreateForm; +import io.kafbat.ui.screens.connectors.ConnectorDetails; +import io.kafbat.ui.screens.connectors.KafkaConnectList; +import io.kafbat.ui.screens.consumers.ConsumersDetails; +import io.kafbat.ui.screens.consumers.ConsumersList; +import io.kafbat.ui.screens.ksqldb.KsqlDbList; +import io.kafbat.ui.screens.ksqldb.KsqlQueryForm; +import io.kafbat.ui.screens.panels.NaviSideBar; +import io.kafbat.ui.screens.panels.TopPanel; +import io.kafbat.ui.screens.schemas.SchemaCreateForm; +import io.kafbat.ui.screens.schemas.SchemaDetails; +import io.kafbat.ui.screens.schemas.SchemaRegistryList; +import io.kafbat.ui.screens.topics.ProduceMessagePanel; +import io.kafbat.ui.screens.topics.TopicCreateEditForm; +import io.kafbat.ui.screens.topics.TopicDetails; +import io.kafbat.ui.screens.topics.TopicSettingsTab; +import io.kafbat.ui.screens.topics.TopicsList; import io.kafbat.ui.services.ApiService; public abstract class Facade { diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/BaseManualTest.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/BaseManualTest.java deleted file mode 100644 index 8c46b682b..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/BaseManualTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.kafbat.ui.manualsuite; - -import io.kafbat.ui.settings.listeners.QaseResultListener; -import io.kafbat.ui.utilities.qase.QaseSetup; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.enums.State; -import java.lang.reflect.Method; -import org.testng.SkipException; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Listeners; - -@Listeners(QaseResultListener.class) -public abstract class BaseManualTest { - - @BeforeSuite - public void beforeSuite() { - QaseSetup.qaseIntegrationSetup(); - } - - @BeforeMethod - public void beforeMethod(Method method) { - if (method.getAnnotation(Automation.class).state().equals(State.NOT_AUTOMATED) - || method.getAnnotation(Automation.class).state().equals(State.TO_BE_AUTOMATED)) { - throw new SkipException("Skip test exception"); - } - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SanityBacklog.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SanityBacklog.java deleted file mode 100644 index a3c7e4963..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SanityBacklog.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.kafbat.ui.manualsuite.backlog; - -import io.kafbat.ui.manualsuite.BaseManualTest; - -public class SanityBacklog extends BaseManualTest { - -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SmokeBacklog.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SmokeBacklog.java deleted file mode 100644 index 6001f10fa..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/backlog/SmokeBacklog.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.kafbat.ui.manualsuite.backlog; - -import io.kafbat.ui.manualsuite.BaseManualTest; -import io.kafbat.ui.qasesuite.BaseQaseTest; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.annotations.Suite; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseId; -import org.testng.annotations.Test; - -public class SmokeBacklog extends BaseManualTest { - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.TOPICS_PROFILE_SUITE_ID) - @QaseId(335) - @Test - public void testCaseA() { - } - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.TOPICS_PROFILE_SUITE_ID) - @QaseId(336) - @Test - public void testCaseB() { - } - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.TOPICS_PROFILE_SUITE_ID) - @QaseId(343) - @Test - public void testCaseC() { - } - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.SCHEMAS_SUITE_ID) - @QaseId(345) - @Test - public void testCaseD() { - } - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.SCHEMAS_SUITE_ID) - @QaseId(346) - @Test - public void testCaseE() { - } - - @Automation(state = State.TO_BE_AUTOMATED) - @Suite(id = BaseQaseTest.TOPICS_PROFILE_SUITE_ID) - @QaseId(347) - @Test - public void testCaseF() { - } - - @Automation(state = State.NOT_AUTOMATED) - @Suite(id = BaseQaseTest.TOPICS_SUITE_ID) - @QaseId(50) - @Test - public void testCaseG() { - } - - @Automation(state = State.NOT_AUTOMATED) - @Suite(id = BaseQaseTest.SCHEMAS_SUITE_ID) - @QaseId(351) - @Test - public void testCaseH() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/DataMaskingTest.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/DataMaskingTest.java deleted file mode 100644 index 108dc84fd..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/DataMaskingTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.kafbat.ui.manualsuite.suite; - -import io.kafbat.ui.manualsuite.BaseManualTest; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseId; -import org.testng.annotations.Test; - -public class DataMaskingTest extends BaseManualTest { - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(262) - @Test - public void testCaseA() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(264) - @Test - public void testCaseB() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(265) - @Test - public void testCaseC() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/RbacTest.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/RbacTest.java deleted file mode 100644 index 4e84c24f2..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/RbacTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.kafbat.ui.manualsuite.suite; - -import io.kafbat.ui.manualsuite.BaseManualTest; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseId; -import org.testng.annotations.Test; - -public class RbacTest extends BaseManualTest { - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(249) - @Test - public void testCaseA() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(251) - @Test - public void testCaseB() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(257) - @Test - public void testCaseC() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(258) - @Test - public void testCaseD() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(259) - @Test - public void testCaseE() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(260) - @Test - public void testCaseF() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(261) - @Test - public void testCaseG() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/TopicsTest.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/TopicsTest.java deleted file mode 100644 index a6f548f9b..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/TopicsTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.kafbat.ui.manualsuite.suite; - -import io.kafbat.ui.manualsuite.BaseManualTest; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseId; -import org.testng.annotations.Test; - -public class TopicsTest extends BaseManualTest { - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(17) - @Test - public void testCaseA() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(18) - @Test - public void testCaseB() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(21) - @Test() - public void testCaseC() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(22) - @Test - public void testCaseD() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(47) - @Test - public void testCaseE() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(48) - @Test - public void testCaseF() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(49) - @Test - public void testCaseG() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(57) - @Test - public void testCaseH() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(58) - @Test - public void testCaseI() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(269) - @Test - public void testCaseJ() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(270) - @Test - public void testCaseK() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(271) - @Test - public void testCaseL() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(272) - @Test - public void testCaseM() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(337) - @Test - public void testCaseN() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(339) - @Test - public void testCaseO() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(341) - @Test - public void testCaseP() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(342) - @Test - public void testCaseQ() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/WizardTest.java b/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/WizardTest.java deleted file mode 100644 index 700351558..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/manualsuite/suite/WizardTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.kafbat.ui.manualsuite.suite; - -import io.kafbat.ui.manualsuite.BaseManualTest; -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseId; -import org.testng.annotations.Test; - -public class WizardTest extends BaseManualTest { - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(333) - @Test - public void testCaseA() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(338) - @Test - public void testCaseB() { - } - - @Automation(state = State.NOT_AUTOMATED) - @QaseId(340) - @Test - public void testCaseC() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/BaseQaseTest.java b/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/BaseQaseTest.java deleted file mode 100644 index 4424c7ab7..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/BaseQaseTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.kafbat.ui.qasesuite; - -import io.kafbat.ui.settings.listeners.QaseCreateListener; -import io.kafbat.ui.utilities.qase.QaseSetup; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Listeners; - -@Listeners(QaseCreateListener.class) -public abstract class BaseQaseTest { - - public static final long BROKERS_SUITE_ID = 1; - public static final long CONNECTORS_SUITE_ID = 10; - public static final long KSQL_DB_SUITE_ID = 8; - public static final long SANITY_SUITE_ID = 19; - public static final long SCHEMAS_SUITE_ID = 11; - public static final long TOPICS_SUITE_ID = 2; - public static final long TOPICS_CREATE_SUITE_ID = 4; - public static final long TOPICS_PROFILE_SUITE_ID = 5; - - @BeforeSuite - public void beforeSuite() { - QaseSetup.qaseIntegrationSetup(); - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/Template.java b/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/Template.java deleted file mode 100644 index 9903ee50e..000000000 --- a/e2e-tests/src/test/java/io/kafbat/ui/qasesuite/Template.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.kafbat.ui.qasesuite; - -import io.kafbat.ui.utilities.qase.annotations.Automation; -import io.kafbat.ui.utilities.qase.annotations.Status; -import io.kafbat.ui.utilities.qase.annotations.Suite; -import io.kafbat.ui.utilities.qase.enums.State; -import io.qase.api.annotation.QaseTitle; -import io.qase.api.annotation.Step; - -public class Template extends BaseQaseTest { - - /** - * this class is a kind of placeholder or example, use is as template to create new one - * copy Template into e2e-tests/src/test/java/io/kafbat/ui/qaseSuite/ - * place it into regarding folder and rename according to test case summary from Qase.io - * uncomment @Test and set all annotations according to e2e-tests/QASE.md - */ - - @Automation(state = State.NOT_AUTOMATED) - @QaseTitle("testCaseA title") - @Status(status = io.kafbat.ui.utilities.qase.enums.Status.DRAFT) - @Suite(id = 0) - // @org.testng.annotations.Test - public void testCaseA() { - stepA(); - stepB(); - stepC(); - stepD(); - stepE(); - stepF(); - } - - @Step("stepA action") - private void stepA() { - } - - @Step("stepB action") - private void stepB() { - } - - @Step("stepC action") - private void stepC() { - } - - @Step("stepD action") - private void stepD() { - } - - @Step("stepE action") - private void stepE() { - } - - @Step("stepF action") - private void stepF() { - } -} diff --git a/e2e-tests/src/test/java/io/kafbat/ui/sanitysuite/TopicsTest.java b/e2e-tests/src/test/java/io/kafbat/ui/sanitysuite/TopicsTest.java index d1cbb80fe..96ca8a90d 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/sanitysuite/TopicsTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/sanitysuite/TopicsTest.java @@ -1,11 +1,9 @@ package io.kafbat.ui.sanitysuite; -import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; - import io.kafbat.ui.BaseTest; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.topics.enums.CleanupPolicyValue; -import io.qase.api.annotation.QaseId; +import io.kafbat.ui.screens.topics.enums.CleanupPolicyValue; +import io.qameta.allure.Step; import java.util.ArrayList; import java.util.List; import org.testng.Assert; @@ -16,12 +14,9 @@ public class TopicsTest extends BaseTest { private static final List TOPIC_LIST = new ArrayList<>(); - @QaseId(285) @Test() public void verifyClearMessagesMenuStateAfterTopicUpdate() { - Topic topic = new Topic() - .setName("topic-" + randomAlphabetic(5)) - .setNumberOfPartitions(1) + Topic topic = Topic.createTopic() .setCleanupPolicyValue(CleanupPolicyValue.DELETE); navigateToTopics(); topicsList @@ -37,15 +32,16 @@ public void verifyClearMessagesMenuStateAfterTopicUpdate() { TOPIC_LIST.add(topic); topicDetails .openDotMenu(); - Assert.assertTrue(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled"); + Assert.assertTrue(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled()"); topic.setCleanupPolicyValue(CleanupPolicyValue.COMPACT); editCleanUpPolicyAndOpenDotMenu(topic); - Assert.assertFalse(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled"); + Assert.assertFalse(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled()"); topic.setCleanupPolicyValue(CleanupPolicyValue.DELETE); editCleanUpPolicyAndOpenDotMenu(topic); - Assert.assertTrue(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled"); + Assert.assertTrue(topicDetails.isClearMessagesMenuEnabled(), "isClearMessagesMenuEnabled()"); } + @Step private void editCleanUpPolicyAndOpenDotMenu(Topic topic) { topicDetails .clickEditSettingsMenu(); diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/SmokeTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/SmokeTest.java index 4fe2a6188..b70bce4f8 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/SmokeTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/SmokeTest.java @@ -1,6 +1,6 @@ package io.kafbat.ui.smokesuite; -import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static io.kafbat.ui.utilities.FileUtil.resourceToString; import com.codeborne.selenide.Condition; import com.codeborne.selenide.WebDriverRunner; @@ -8,12 +8,10 @@ import io.kafbat.ui.models.Connector; import io.kafbat.ui.models.Schema; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.panels.enums.MenuItem; +import io.kafbat.ui.screens.panels.enums.MenuItem; import io.kafbat.ui.settings.BaseSource; -import io.kafbat.ui.utilities.FileUtils; import io.kafbat.ui.variables.Url; import io.qameta.allure.Step; -import io.qase.api.annotation.QaseId; import java.util.stream.Collectors; import java.util.stream.Stream; import org.testng.Assert; @@ -25,12 +23,9 @@ public class SmokeTest extends BaseTest { private static final int BROKER_ID = 1; private static final Schema TEST_SCHEMA = Schema.createSchemaAvro(); - private static final Topic TEST_TOPIC = new Topic() - .setName("new-topic-" + randomAlphabetic(5)) - .setNumberOfPartitions(1); - private static final Connector TEST_CONNECTOR = new Connector() - .setName("new-connector-" + randomAlphabetic(5)) - .setConfig(FileUtils.getResourceAsString("testData/connectors/config_for_create_connector_via_api.json")); + private static final Topic TEST_TOPIC = Topic.createTopic(); + private static final Connector TEST_CONNECTOR = + Connector.createConnector(resourceToString("testdata/connectors/create_config_api.json")); @BeforeClass(alwaysRun = true) public void beforeClass() { @@ -40,9 +35,8 @@ public void beforeClass() { .createConnector(TEST_CONNECTOR); } - @QaseId(198) @Test - public void checkBasePageElements() { + public void basePageElementsCheck() { verifyElementsCondition( Stream.concat(topPanel.getAllVisibleElements().stream(), naviSideBar.getAllMenuButtons().stream()) .collect(Collectors.toList()), Condition.visible); @@ -51,9 +45,8 @@ public void checkBasePageElements() { .collect(Collectors.toList()), Condition.enabled); } - @QaseId(45) @Test - public void checkUrlWhileNavigating() { + public void urlWhileNavigationCheck() { navigateToBrokers(); verifyCurrentUrl(Url.BROKERS_LIST_URL); navigateToTopics(); @@ -68,9 +61,8 @@ public void checkUrlWhileNavigating() { verifyCurrentUrl(Url.KSQL_DB_LIST_URL); } - @QaseId(46) @Test - public void checkPathWhileNavigating() { + public void pathWhileNavigationCheck() { navigateToBrokersAndOpenDetails(BROKER_ID); verifyComponentsPath(MenuItem.BROKERS, String.format("Broker %d", BROKER_ID)); navigateToTopicsAndOpenDetails(TEST_TOPIC.getName()); @@ -93,7 +85,7 @@ private void verifyCurrentUrl(String expectedUrl) { @Step private void verifyComponentsPath(MenuItem menuItem, String expectedPath) { Assert.assertEquals(naviSideBar.getPagePath(menuItem), expectedPath, - String.format("getPagePath() for %s", menuItem.getPageTitle().toUpperCase())); + String.format("getPagePath()[%s]", menuItem.getPageTitle().toUpperCase())); } @AfterClass(alwaysRun = true) diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/brokers/BrokersTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/brokers/BrokersTest.java index 47a042694..2e41623ea 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/brokers/BrokersTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/brokers/BrokersTest.java @@ -1,13 +1,14 @@ package io.kafbat.ui.smokesuite.brokers; +import static io.kafbat.ui.utilities.IntUtil.getIntegerFromString; + import com.codeborne.selenide.Condition; import io.kafbat.ui.BaseTest; -import io.kafbat.ui.pages.brokers.BrokersConfigTab; -import io.kafbat.ui.pages.brokers.BrokersDetails; -import io.kafbat.ui.utilities.StringUtils; -import io.kafbat.ui.variables.Expected; +import io.kafbat.ui.screens.brokers.BrokersConfigTab; +import io.kafbat.ui.screens.brokers.BrokersDetails; +import io.kafbat.ui.utilities.StringUtil; +import io.kafbat.ui.variables.Common; import io.qameta.allure.Issue; -import io.qase.api.annotation.QaseId; import java.util.List; import org.testng.Assert; import org.testng.annotations.Ignore; @@ -18,20 +19,18 @@ public class BrokersTest extends BaseTest { public static final int DEFAULT_BROKER_ID = 1; - @QaseId(1) @Test - public void checkBrokersOverview() { + public void brokersOverviewCheck() { navigateToBrokers(); - Assert.assertTrue(brokersList.getAllBrokers().size() > 0, "getAllBrokers()"); + Assert.assertFalse(brokersList.getAllBrokers().isEmpty(), "getAllBrokers()"); verifyElementsCondition(brokersList.getAllVisibleElements(), Condition.visible); verifyElementsCondition(brokersList.getAllEnabledElements(), Condition.enabled); } - @QaseId(85) @Test - public void checkExistingBrokersInCluster() { + public void existingBrokersInClusterCheck() { navigateToBrokers(); - Assert.assertTrue(brokersList.getAllBrokers().size() > 0, "getAllBrokers()"); + Assert.assertFalse(brokersList.getAllBrokers().isEmpty(), "getAllBrokers().isEmpty()"); brokersList .openBroker(DEFAULT_BROKER_ID); brokersDetails @@ -43,13 +42,11 @@ public void checkExistingBrokersInCluster() { brokersConfigTab .waitUntilScreenReady(); verifyElementsCondition(brokersConfigTab.getColumnHeaders(), Condition.visible); - verifyElementsCondition(brokersConfigTab.getEditButtons(), Condition.enabled); Assert.assertTrue(brokersConfigTab.isSearchByKeyVisible(), "isSearchByKeyVisible()"); } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3347") - @QaseId(330) + @Issue("https://github.com/kafbat/kafka-ui/issues/209") @Test public void brokersConfigFirstPageSearchCheck() { navigateToBrokersAndOpenDetails(DEFAULT_BROKER_ID); @@ -64,18 +61,17 @@ public void brokersConfigFirstPageSearchCheck() { Assert.assertFalse(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeyFirstPage), - String.format("getAllConfigs().contains(%s)", anyConfigKeyFirstPage)); + String.format("getAllConfigs().contains()[%s]", anyConfigKeyFirstPage)); brokersConfigTab .searchConfig(anyConfigKeyFirstPage); Assert.assertTrue(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeyFirstPage), - String.format("getAllConfigs().contains(%s)", anyConfigKeyFirstPage)); + String.format("getAllConfigs().contains()[%s]", anyConfigKeyFirstPage)); } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3347") - @QaseId(350) + @Issue("https://github.com/kafbat/kafka-ui/issues/209") @Test public void brokersConfigSecondPageSearchCheck() { navigateToBrokersAndOpenDetails(DEFAULT_BROKER_ID); @@ -92,18 +88,17 @@ public void brokersConfigSecondPageSearchCheck() { Assert.assertFalse(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeySecondPage), - String.format("getAllConfigs().contains(%s)", anyConfigKeySecondPage)); + String.format("getAllConfigs().contains()[%s]", anyConfigKeySecondPage)); brokersConfigTab .searchConfig(anyConfigKeySecondPage); Assert.assertTrue(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeySecondPage), - String.format("getAllConfigs().contains(%s)", anyConfigKeySecondPage)); + String.format("getAllConfigs().contains()[%s]", anyConfigKeySecondPage)); } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3347") - @QaseId(348) + @Issue("https://github.com/kafbat/kafka-ui/issues/209") @Test public void brokersConfigCaseInsensitiveSearchCheck() { navigateToBrokersAndOpenDetails(DEFAULT_BROKER_ID); @@ -118,22 +113,21 @@ public void brokersConfigCaseInsensitiveSearchCheck() { Assert.assertFalse(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeyFirstPage), - String.format("getAllConfigs().contains(%s)", anyConfigKeyFirstPage)); + String.format("getAllConfigs().contains()[%s]", anyConfigKeyFirstPage)); SoftAssert softly = new SoftAssert(); List.of(anyConfigKeyFirstPage.toLowerCase(), anyConfigKeyFirstPage.toUpperCase(), - StringUtils.getMixedCase(anyConfigKeyFirstPage)) + StringUtil.getMixedCase(anyConfigKeyFirstPage)) .forEach(configCase -> { brokersConfigTab .searchConfig(configCase); softly.assertTrue(brokersConfigTab.getAllConfigs().stream() .map(BrokersConfigTab.BrokersConfigItem::getKey) .toList().contains(anyConfigKeyFirstPage), - String.format("getAllConfigs().contains(%s)", configCase)); + String.format("getAllConfigs().contains()[%s]", configCase)); }); softly.assertAll(); } - @QaseId(331) @Test public void brokersSourceInfoCheck() { navigateToBrokersAndOpenDetails(DEFAULT_BROKER_ID); @@ -142,10 +136,9 @@ public void brokersSourceInfoCheck() { String sourceInfoTooltip = brokersConfigTab .hoverOnSourceInfoIcon() .getSourceInfoTooltipText(); - Assert.assertEquals(sourceInfoTooltip, Expected.BROKER_SOURCE_INFO_TOOLTIP, "brokerSourceInfoTooltip"); + Assert.assertEquals(sourceInfoTooltip, Common.BROKER_SOURCE_INFO_TOOLTIP, "getSourceInfoTooltipText()"); } - @QaseId(332) @Test public void brokersConfigEditCheck() { navigateToBrokersAndOpenDetails(DEFAULT_BROKER_ID); @@ -155,7 +148,7 @@ public void brokersConfigEditCheck() { BrokersConfigTab.BrokersConfigItem configItem = brokersConfigTab .searchConfig(configKey) .getConfig(configKey); - int defaultValue = Integer.parseInt(configItem.getValue()); + int defaultValue = getIntegerFromString(configItem.getValue(), false); configItem .clickEditBtn(); SoftAssert softly = new SoftAssert(); @@ -167,7 +160,8 @@ public void brokersConfigEditCheck() { configItem .setValue(String.valueOf(newValue)) .clickCancelBtn(); - Assert.assertEquals(Integer.parseInt(configItem.getValue()), defaultValue, "getValue()"); + Assert.assertEquals(getIntegerFromString(configItem.getValue(), false), defaultValue, + "configItem.getValue()"); configItem .clickEditBtn() .setValue(String.valueOf(newValue)) @@ -179,7 +173,8 @@ public void brokersConfigEditCheck() { softly.assertFalse(configItem.getSaveBtn().isDisplayed(), "getSaveBtn().isDisplayed()"); softly.assertFalse(configItem.getCancelBtn().isDisplayed(), "getCancelBtn().isDisplayed()"); softly.assertTrue(configItem.getEditBtn().isDisplayed(), "getEditBtn().isDisplayed()"); - softly.assertEquals(Integer.parseInt(configItem.getValue()), newValue, "getValue()"); + softly.assertEquals(getIntegerFromString(configItem.getValue(), false), newValue, + "configItem.getValue()"); softly.assertAll(); } } diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/connectors/ConnectorsTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/connectors/ConnectorsTest.java index fdbf097bf..78cbc3ecf 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/connectors/ConnectorsTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/connectors/ConnectorsTest.java @@ -1,15 +1,12 @@ package io.kafbat.ui.smokesuite.connectors; -import static io.kafbat.ui.pages.BasePage.AlertHeader.SUCCESS; -import static io.kafbat.ui.utilities.FileUtils.getResourceAsString; -import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static io.kafbat.ui.models.Topic.createTopic; +import static io.kafbat.ui.screens.BasePage.AlertHeader.SUCCESS; +import static io.kafbat.ui.utilities.FileUtil.resourceToString; import io.kafbat.ui.BaseTest; import io.kafbat.ui.models.Connector; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.utilities.FileUtils; -import io.qase.api.annotation.QaseId; import java.util.ArrayList; import java.util.List; import org.testng.Assert; @@ -19,85 +16,73 @@ public class ConnectorsTest extends BaseTest { + private static final String MESSAGE_CONTENT = resourceToString("testdata/topics/create_topic_content.json"); + private static final Topic CREATE_TOPIC = createTopic().setMessageValue(MESSAGE_CONTENT); + private static final Topic DELETE_TOPIC = createTopic().setMessageValue(MESSAGE_CONTENT); + private static final Topic UPDATE_TOPIC = createTopic().setMessageValue(MESSAGE_CONTENT); + private static final Connector DELETE_CONNECTOR = + Connector.createConnector(resourceToString("testdata/connectors/delete_config.json")); + private static final Connector UPDATE_CONNECTOR = + Connector.createConnector(resourceToString("testdata/connectors/create_config_api.json")); private static final List TOPIC_LIST = new ArrayList<>(); private static final List CONNECTOR_LIST = new ArrayList<>(); - private static final String MESSAGE_CONTENT = "testData/topics/message_content_create_topic.json"; - private static final String MESSAGE_KEY = " "; - private static final Topic TOPIC_FOR_CREATE = new Topic() - .setName("topic-for-create-connector-" + randomAlphabetic(5)) - .setMessageValue(MESSAGE_CONTENT).setMessageKey(MESSAGE_KEY); - private static final Topic TOPIC_FOR_DELETE = new Topic() - .setName("topic-for-delete-connector-" + randomAlphabetic(5)) - .setMessageValue(MESSAGE_CONTENT).setMessageKey(MESSAGE_KEY); - private static final Topic TOPIC_FOR_UPDATE = new Topic() - .setName("topic-for-update-connector-" + randomAlphabetic(5)) - .setMessageValue(MESSAGE_CONTENT).setMessageKey(MESSAGE_KEY); - private static final Connector CONNECTOR_FOR_DELETE = new Connector() - .setName("connector-for-delete-" + randomAlphabetic(5)) - .setConfig(getResourceAsString("testData/connectors/delete_connector_config.json")); - private static final Connector CONNECTOR_FOR_UPDATE = new Connector() - .setName("connector-for-update-and-delete-" + randomAlphabetic(5)) - .setConfig(getResourceAsString("testData/connectors/config_for_create_connector_via_api.json")); @BeforeClass(alwaysRun = true) public void beforeClass() { - TOPIC_LIST.addAll(List.of(TOPIC_FOR_CREATE, TOPIC_FOR_DELETE, TOPIC_FOR_UPDATE)); + TOPIC_LIST.addAll(List.of(CREATE_TOPIC, DELETE_TOPIC, UPDATE_TOPIC)); TOPIC_LIST.forEach(topic -> apiService .createTopic(topic) - .sendMessage(topic) - ); - CONNECTOR_LIST.addAll(List.of(CONNECTOR_FOR_DELETE, CONNECTOR_FOR_UPDATE)); + .sendMessage(topic)); + CONNECTOR_LIST.addAll(List.of(DELETE_CONNECTOR, UPDATE_CONNECTOR)); CONNECTOR_LIST.forEach(connector -> apiService.createConnector(connector)); } - @QaseId(42) @Test - public void createConnector() { - Connector connectorForCreate = new Connector() - .setName("connector-for-create-" + randomAlphabetic(5)) - .setConfig(getResourceAsString("testData/connectors/config_for_create_connector.json")); + public void createConnectorCheck() { + Connector createConnector = + Connector.createConnector(resourceToString("testdata/connectors/create_config.json")); navigateToConnectors(); kafkaConnectList .clickCreateConnectorBtn(); connectorCreateForm .waitUntilScreenReady() - .setConnectorDetails(connectorForCreate.getName(), connectorForCreate.getConfig()) + .setConnectorDetails(createConnector.getName(), createConnector.getConfig()) .clickSubmitButton(); connectorDetails .waitUntilScreenReady(); - navigateToConnectorsAndOpenDetails(connectorForCreate.getName()); - Assert.assertTrue(connectorDetails.isConnectorHeaderVisible(connectorForCreate.getName()), - "isConnectorTitleVisible()"); + navigateToConnectorsAndOpenDetails(createConnector.getName()); + Assert.assertTrue(connectorDetails.isConnectorHeaderVisible(createConnector.getName()), + String.format("isConnectorHeaderVisible()[%s]", createConnector.getName())); navigateToConnectors(); - Assert.assertTrue(kafkaConnectList.isConnectorVisible(CONNECTOR_FOR_DELETE.getName()), "isConnectorVisible()"); - CONNECTOR_LIST.add(connectorForCreate); + Assert.assertTrue(kafkaConnectList.isConnectorVisible(DELETE_CONNECTOR.getName()), + String.format("isConnectorVisible()[%s]", DELETE_CONNECTOR.getName())); + CONNECTOR_LIST.add(createConnector); } - @QaseId(196) @Test - public void updateConnector() { - navigateToConnectorsAndOpenDetails(CONNECTOR_FOR_UPDATE.getName()); + public void updateConnectorCheck() { + navigateToConnectorsAndOpenDetails(UPDATE_CONNECTOR.getName()); connectorDetails .openConfigTab() - .setConfig(CONNECTOR_FOR_UPDATE.getConfig()) + .setConfig(UPDATE_CONNECTOR.getConfig()) .clickSubmitButton(); Assert.assertTrue(connectorDetails.isAlertWithMessageVisible(SUCCESS, "Config successfully updated."), "isAlertWithMessageVisible()"); navigateToConnectors(); - Assert.assertTrue(kafkaConnectList.isConnectorVisible(CONNECTOR_FOR_UPDATE.getName()), "isConnectorVisible()"); + Assert.assertTrue(kafkaConnectList.isConnectorVisible(UPDATE_CONNECTOR.getName()), + String.format("isConnectorVisible()[%s]", UPDATE_CONNECTOR.getName())); } - @QaseId(195) @Test - public void deleteConnector() { - navigateToConnectorsAndOpenDetails(CONNECTOR_FOR_DELETE.getName()); + public void deleteConnectorCheck() { + navigateToConnectorsAndOpenDetails(DELETE_CONNECTOR.getName()); connectorDetails .openDotMenu() .clickDeleteBtn() .clickConfirmBtn(); navigateToConnectors(); - Assert.assertFalse(kafkaConnectList.isConnectorVisible(CONNECTOR_FOR_DELETE.getName()), "isConnectorVisible()"); - CONNECTOR_LIST.remove(CONNECTOR_FOR_DELETE); + Assert.assertFalse(kafkaConnectList.isConnectorVisible(DELETE_CONNECTOR.getName()), + String.format("isConnectorVisible()[%s]", DELETE_CONNECTOR.getName())); } @AfterClass(alwaysRun = true) diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/ksqldb/KsqlDbTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/ksqldb/KsqlDbTest.java index ab9591da7..59277603f 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/ksqldb/KsqlDbTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/ksqldb/KsqlDbTest.java @@ -3,12 +3,11 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import io.kafbat.ui.BaseTest; -import io.kafbat.ui.pages.ksqldb.enums.KsqlMenuTabs; -import io.kafbat.ui.pages.ksqldb.enums.KsqlQueryConfig; -import io.kafbat.ui.pages.ksqldb.models.Stream; -import io.kafbat.ui.pages.ksqldb.models.Table; +import io.kafbat.ui.screens.ksqldb.enums.KsqlMenuTabs; +import io.kafbat.ui.screens.ksqldb.enums.KsqlQueryConfig; +import io.kafbat.ui.screens.ksqldb.models.Stream; +import io.kafbat.ui.screens.ksqldb.models.Table; import io.qameta.allure.Step; -import io.qase.api.annotation.QaseId; import java.util.ArrayList; import java.util.List; import org.testng.Assert; @@ -20,50 +19,50 @@ public class KsqlDbTest extends BaseTest { private static final Stream DEFAULT_STREAM = new Stream() - .setName("DEFAULT_STREAM_" + randomAlphabetic(4).toUpperCase()) - .setTopicName("DEFAULT_TOPIC_" + randomAlphabetic(4).toUpperCase()); + .setName("DEFAULT_STREAM_" + randomAlphabetic(5).toUpperCase()) + .setTopicName("DEFAULT_TOPIC_" + randomAlphabetic(5).toUpperCase()); private static final Table FIRST_TABLE = new Table() - .setName("FIRST_TABLE_" + randomAlphabetic(4).toUpperCase()) + .setName("FIRST_TABLE_" + randomAlphabetic(5).toUpperCase()) .setStreamName(DEFAULT_STREAM.getName()); private static final Table SECOND_TABLE = new Table() - .setName("SECOND_TABLE_" + randomAlphabetic(4).toUpperCase()) + .setName("SECOND_TABLE_" + randomAlphabetic(5).toUpperCase()) .setStreamName(DEFAULT_STREAM.getName()); - private static final List TOPIC_NAMES_LIST = new ArrayList<>(); + private static final List TOPIC_NAME_LIST = new ArrayList<>(); @BeforeClass(alwaysRun = true) public void beforeClass() { apiService .createStream(DEFAULT_STREAM) .createTables(FIRST_TABLE, SECOND_TABLE); - TOPIC_NAMES_LIST.addAll(List.of(DEFAULT_STREAM.getTopicName(), + TOPIC_NAME_LIST.addAll(List.of(DEFAULT_STREAM.getTopicName(), FIRST_TABLE.getName(), SECOND_TABLE.getName())); } - @QaseId(284) @Test(priority = 1) public void streamsAndTablesVisibilityCheck() { navigateToKsqlDb(); SoftAssert softly = new SoftAssert(); - softly.assertTrue(ksqlDbList.getTableByName(FIRST_TABLE.getName()).isVisible(), "getTableByName()"); - softly.assertTrue(ksqlDbList.getTableByName(SECOND_TABLE.getName()).isVisible(), "getTableByName()"); + softly.assertTrue(ksqlDbList.getTableByName(FIRST_TABLE.getName()).isVisible(), + String.format("getTableByName().isVisible()[%s]", FIRST_TABLE.getName())); + softly.assertTrue(ksqlDbList.getTableByName(SECOND_TABLE.getName()).isVisible(), + String.format("getTableByName().isVisible()[%s]", SECOND_TABLE.getName())); softly.assertAll(); ksqlDbList .openDetailsTab(KsqlMenuTabs.STREAMS) .waitUntilScreenReady(); - Assert.assertTrue(ksqlDbList.getStreamByName(DEFAULT_STREAM.getName()).isVisible(), "getStreamByName()"); + Assert.assertTrue(ksqlDbList.getStreamByName(DEFAULT_STREAM.getName()).isVisible(), + String.format("getStreamByName().isVisible()[%s]", DEFAULT_STREAM.getName())); } - @QaseId(276) @Test(priority = 2) public void clearEnteredQueryCheck() { navigateToKsqlDbAndExecuteRequest(KsqlQueryConfig.SHOW_TABLES.getQuery()); - Assert.assertFalse(ksqlQueryForm.getEnteredQuery().isEmpty(), "getEnteredQuery()"); + Assert.assertFalse(ksqlQueryForm.getEnteredQuery().isEmpty(), "getEnteredQuery().isEmpty()"); ksqlQueryForm .clickClearBtn(); - Assert.assertTrue(ksqlQueryForm.getEnteredQuery().isEmpty(), "getEnteredQuery()"); + Assert.assertTrue(ksqlQueryForm.getEnteredQuery().isEmpty(), "getEnteredQuery().isEmpty()"); } - @QaseId(344) @Test(priority = 3) public void clearResultsButtonCheck() { String notValidQuery = "some not valid request"; @@ -79,33 +78,30 @@ public void clearResultsButtonCheck() { Assert.assertFalse(ksqlQueryForm.isClearResultsBtnEnabled(), "isClearResultsBtnEnabled()"); } - @QaseId(41) @Test(priority = 4) - public void checkShowTablesRequestExecution() { + public void showTablesRequestExecutionCheck() { navigateToKsqlDbAndExecuteRequest(KsqlQueryConfig.SHOW_TABLES.getQuery()); SoftAssert softly = new SoftAssert(); softly.assertTrue(ksqlQueryForm.areResultsVisible(), "areResultsVisible()"); softly.assertTrue(ksqlQueryForm.getItemByName(FIRST_TABLE.getName()).isVisible(), - String.format("getItemByName(%s)", FIRST_TABLE.getName())); + String.format("getItemByName().isVisible()[%s]", FIRST_TABLE.getName())); softly.assertTrue(ksqlQueryForm.getItemByName(SECOND_TABLE.getName()).isVisible(), - String.format("getItemByName(%s)", SECOND_TABLE.getName())); + String.format("getItemByName().isVisible()[%s]", SECOND_TABLE.getName())); softly.assertAll(); } - @QaseId(278) @Test(priority = 5) - public void checkShowStreamsRequestExecution() { + public void showStreamsRequestExecutionCheck() { navigateToKsqlDbAndExecuteRequest(KsqlQueryConfig.SHOW_STREAMS.getQuery()); SoftAssert softly = new SoftAssert(); softly.assertTrue(ksqlQueryForm.areResultsVisible(), "areResultsVisible()"); softly.assertTrue(ksqlQueryForm.getItemByName(DEFAULT_STREAM.getName()).isVisible(), - String.format("getItemByName(%s)", FIRST_TABLE.getName())); + String.format("getItemByName().isVisible()[%s]", FIRST_TABLE.getName())); softly.assertAll(); } - @QaseId(86) @Test(priority = 6) - public void clearResultsForExecutedRequest() { + public void clearResultsOfExecutedRequestCheck() { navigateToKsqlDbAndExecuteRequest(KsqlQueryConfig.SHOW_TABLES.getQuery()); SoftAssert softly = new SoftAssert(); softly.assertTrue(ksqlQueryForm.areResultsVisible(), "areResultsVisible()"); @@ -116,7 +112,6 @@ public void clearResultsForExecutedRequest() { softly.assertAll(); } - @QaseId(277) @Test(priority = 7) public void stopQueryFunctionalCheck() { navigateToKsqlDbAndExecuteRequest(String.format(KsqlQueryConfig.SELECT_ALL_FROM.getQuery(), FIRST_TABLE.getName())); @@ -128,7 +123,7 @@ public void stopQueryFunctionalCheck() { @AfterClass(alwaysRun = true) public void afterClass() { - TOPIC_NAMES_LIST.forEach(topicName -> apiService.deleteTopic(topicName)); + TOPIC_NAME_LIST.forEach(topicName -> apiService.deleteTopic(topicName)); } @Step diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/schemas/SchemasTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/schemas/SchemasTest.java index d174ff591..3b140018e 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/schemas/SchemasTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/schemas/SchemasTest.java @@ -4,8 +4,7 @@ import io.kafbat.ui.BaseTest; import io.kafbat.ui.api.model.CompatibilityLevel; import io.kafbat.ui.models.Schema; -import io.kafbat.ui.utilities.FileUtils; -import io.qase.api.annotation.QaseId; +import io.kafbat.ui.utilities.FileUtil; import java.util.ArrayList; import java.util.List; import org.testng.Assert; @@ -16,48 +15,48 @@ public class SchemasTest extends BaseTest { + private static final Schema AVRO_SCHEMA = Schema.createSchemaAvro(); + private static final Schema JSON_SCHEMA = Schema.createSchemaJson(); + private static final Schema PROTOBUF_SCHEMA = Schema.createSchemaProtobuf(); private static final List SCHEMA_LIST = new ArrayList<>(); - private static final Schema AVRO_API = Schema.createSchemaAvro(); - private static final Schema JSON_API = Schema.createSchemaJson(); - private static final Schema PROTOBUF_API = Schema.createSchemaProtobuf(); @BeforeClass(alwaysRun = true) public void beforeClass() { - SCHEMA_LIST.addAll(List.of(AVRO_API, JSON_API, PROTOBUF_API)); + SCHEMA_LIST.addAll(List.of(AVRO_SCHEMA, JSON_SCHEMA, PROTOBUF_SCHEMA)); SCHEMA_LIST.forEach(schema -> apiService.createSchema(schema)); } - @QaseId(43) @Test(priority = 1) - public void createSchemaAvro() { + public void createSchemaAvroCheck() { Schema schemaAvro = Schema.createSchemaAvro(); navigateToSchemaRegistry(); schemaRegistryList .clickCreateSchema(); schemaCreateForm .setSubjectName(schemaAvro.getName()) - .setSchemaField(FileUtils.fileToString(schemaAvro.getValuePath())) + .setSchemaField(FileUtil.fileToString(schemaAvro.getValuePath())) .selectSchemaTypeFromDropdown(schemaAvro.getType()) .clickSubmitButton(); schemaDetails .waitUntilScreenReady(); SoftAssert softly = new SoftAssert(); - softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaAvro.getName()), "isSchemaHeaderVisible()"); + softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaAvro.getName()), + String.format("isSchemaHeaderVisible()[%s]", schemaAvro.getName())); softly.assertEquals(schemaDetails.getSchemaType(), schemaAvro.getType().getValue(), "getSchemaType()"); softly.assertEquals(schemaDetails.getCompatibility(), CompatibilityLevel.CompatibilityEnum.BACKWARD.getValue(), "getCompatibility()"); softly.assertAll(); navigateToSchemaRegistry(); - Assert.assertTrue(schemaRegistryList.isSchemaVisible(AVRO_API.getName()), "isSchemaVisible()"); + Assert.assertTrue(schemaRegistryList.isSchemaVisible(AVRO_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", AVRO_SCHEMA.getName())); SCHEMA_LIST.add(schemaAvro); } - @QaseId(186) @Test(priority = 2) - public void updateSchemaAvro() { - AVRO_API.setValuePath( - System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_avro_for_update.json"); - navigateToSchemaRegistryAndOpenDetails(AVRO_API.getName()); + public void updateSchemaAvroCheck() { + AVRO_SCHEMA.setValuePath( + System.getProperty("user.dir") + "/src/main/resources/testdata/schemas/schema_avro_update.json"); + navigateToSchemaRegistryAndOpenDetails(AVRO_SCHEMA.getName()); schemaDetails .openEditSchema(); schemaCreateForm @@ -69,7 +68,7 @@ public void updateSchemaAvro() { softly.assertAll(); schemaCreateForm .selectCompatibilityLevelFromDropdown(CompatibilityLevel.CompatibilityEnum.NONE) - .setNewSchemaValue(FileUtils.fileToString(AVRO_API.getValuePath())) + .setNewSchemaValue(FileUtil.fileToString(AVRO_SCHEMA.getValuePath())) .clickSubmitButton(); schemaDetails .waitUntilScreenReady(); @@ -77,10 +76,9 @@ public void updateSchemaAvro() { "getCompatibility()"); } - @QaseId(44) @Test(priority = 3) - public void compareVersionsOperation() { - navigateToSchemaRegistryAndOpenDetails(AVRO_API.getName()); + public void compareVersionsCheck() { + navigateToSchemaRegistryAndOpenDetails(AVRO_SCHEMA.getName()); int latestVersion = schemaDetails .waitUntilScreenReady() .getLatestVersion(); @@ -93,93 +91,95 @@ public void compareVersionsOperation() { Assert.assertEquals(versionsNumberFromDdl, latestVersion, "Versions number is not matched"); schemaCreateForm .selectVersionFromDropDown(1); - Assert.assertEquals(schemaCreateForm.getMarkedLinesNumber(), 42, "getAllMarkedLines()"); + Assert.assertEquals(schemaCreateForm.getMarkedLinesNumber(), 42, "getMarkedLinesNumber()"); } - @QaseId(187) @Test(priority = 4) - public void deleteSchemaAvro() { - navigateToSchemaRegistryAndOpenDetails(AVRO_API.getName()); + public void deleteSchemaAvroCheck() { + navigateToSchemaRegistryAndOpenDetails(AVRO_SCHEMA.getName()); schemaDetails .removeSchema(); schemaRegistryList .waitUntilScreenReady(); - Assert.assertFalse(schemaRegistryList.isSchemaVisible(AVRO_API.getName()), "isSchemaVisible()"); - SCHEMA_LIST.remove(AVRO_API); + Assert.assertFalse(schemaRegistryList.isSchemaVisible(AVRO_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", AVRO_SCHEMA.getName())); + SCHEMA_LIST.remove(AVRO_SCHEMA); } - @QaseId(89) @Test(priority = 5) - public void createSchemaJson() { + public void createSchemaJsonCheck() { Schema schemaJson = Schema.createSchemaJson(); navigateToSchemaRegistry(); schemaRegistryList .clickCreateSchema(); schemaCreateForm .setSubjectName(schemaJson.getName()) - .setSchemaField(FileUtils.fileToString(schemaJson.getValuePath())) + .setSchemaField(FileUtil.fileToString(schemaJson.getValuePath())) .selectSchemaTypeFromDropdown(schemaJson.getType()) .clickSubmitButton(); schemaDetails .waitUntilScreenReady(); SoftAssert softly = new SoftAssert(); - softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaJson.getName()), "isSchemaHeaderVisible()"); + softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaJson.getName()), + String.format("isSchemaHeaderVisible()[%s]", schemaJson.getName())); softly.assertEquals(schemaDetails.getSchemaType(), schemaJson.getType().getValue(), "getSchemaType()"); softly.assertEquals(schemaDetails.getCompatibility(), CompatibilityLevel.CompatibilityEnum.BACKWARD.getValue(), "getCompatibility()"); softly.assertAll(); navigateToSchemaRegistry(); - Assert.assertTrue(schemaRegistryList.isSchemaVisible(JSON_API.getName()), "isSchemaVisible()"); + Assert.assertTrue(schemaRegistryList.isSchemaVisible(JSON_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", JSON_SCHEMA.getName())); SCHEMA_LIST.add(schemaJson); } - @QaseId(189) @Test(priority = 6) - public void deleteSchemaJson() { - navigateToSchemaRegistryAndOpenDetails(JSON_API.getName()); + public void deleteSchemaJsonCheck() { + navigateToSchemaRegistryAndOpenDetails(JSON_SCHEMA.getName()); schemaDetails .removeSchema(); schemaRegistryList .waitUntilScreenReady(); - Assert.assertFalse(schemaRegistryList.isSchemaVisible(JSON_API.getName()), "isSchemaVisible()"); - SCHEMA_LIST.remove(JSON_API); + Assert.assertFalse(schemaRegistryList.isSchemaVisible(JSON_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", JSON_SCHEMA.getName())); + SCHEMA_LIST.remove(JSON_SCHEMA); } - @QaseId(91) @Test(priority = 7) - public void createSchemaProtobuf() { + public void createSchemaProtobufCheck() { Schema schemaProtobuf = Schema.createSchemaProtobuf(); navigateToSchemaRegistry(); schemaRegistryList .clickCreateSchema(); schemaCreateForm .setSubjectName(schemaProtobuf.getName()) - .setSchemaField(FileUtils.fileToString(schemaProtobuf.getValuePath())) + .setSchemaField(FileUtil.fileToString(schemaProtobuf.getValuePath())) .selectSchemaTypeFromDropdown(schemaProtobuf.getType()) .clickSubmitButton(); schemaDetails .waitUntilScreenReady(); SoftAssert softly = new SoftAssert(); - softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaProtobuf.getName()), "isSchemaHeaderVisible()"); + softly.assertTrue(schemaDetails.isSchemaHeaderVisible(schemaProtobuf.getName()), + String.format("isSchemaHeaderVisible()[%s]", schemaProtobuf.getName())); softly.assertEquals(schemaDetails.getSchemaType(), schemaProtobuf.getType().getValue(), "getSchemaType()"); softly.assertEquals(schemaDetails.getCompatibility(), CompatibilityLevel.CompatibilityEnum.BACKWARD.getValue(), "getCompatibility()"); softly.assertAll(); navigateToSchemaRegistry(); - Assert.assertTrue(schemaRegistryList.isSchemaVisible(PROTOBUF_API.getName()), "isSchemaVisible()"); + Assert.assertTrue(schemaRegistryList.isSchemaVisible(PROTOBUF_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", PROTOBUF_SCHEMA.getName())); SCHEMA_LIST.add(schemaProtobuf); } - @QaseId(223) @Test(priority = 8) - public void deleteSchemaProtobuf() { - navigateToSchemaRegistryAndOpenDetails(PROTOBUF_API.getName()); + public void deleteSchemaProtobufCheck() { + navigateToSchemaRegistryAndOpenDetails(PROTOBUF_SCHEMA.getName()); schemaDetails .removeSchema(); schemaRegistryList .waitUntilScreenReady(); - Assert.assertFalse(schemaRegistryList.isSchemaVisible(PROTOBUF_API.getName()), "isSchemaVisible()"); - SCHEMA_LIST.remove(PROTOBUF_API); + Assert.assertFalse(schemaRegistryList.isSchemaVisible(PROTOBUF_SCHEMA.getName()), + String.format("isSchemaVisible()[%s]", PROTOBUF_SCHEMA.getName())); + SCHEMA_LIST.remove(PROTOBUF_SCHEMA); } @AfterClass(alwaysRun = true) diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/MessagesTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/MessagesTest.java index d15e9bad6..9622d9ef4 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/MessagesTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/MessagesTest.java @@ -1,15 +1,15 @@ package io.kafbat.ui.smokesuite.topics; -import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static io.kafbat.ui.models.Topic.createTopic; import io.kafbat.ui.BaseTest; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.pages.topics.TopicDetails; -import io.kafbat.ui.utilities.TimeUtils; +import io.kafbat.ui.screens.BasePage; +import io.kafbat.ui.screens.topics.TopicDetails; +import io.kafbat.ui.screens.topics.enums.SeekType; +import io.kafbat.ui.utilities.TimeUtil; import io.qameta.allure.Issue; import io.qameta.allure.Step; -import io.qase.api.annotation.QaseId; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -23,58 +23,41 @@ public class MessagesTest extends BaseTest { - private static final Topic TOPIC_FOR_MESSAGES = new Topic() - .setName("topic-with-clean-message-attribute-" + randomAlphabetic(5)) - .setMessageKey(randomAlphabetic(5)) - .setMessageValue(randomAlphabetic(10)); - private static final Topic TOPIC_TO_CLEAR_AND_PURGE_MESSAGES = new Topic() - .setName("topic-to-clear-and-purge-messages-" + randomAlphabetic(5)) - .setMessageKey(randomAlphabetic(5)) - .setMessageValue(randomAlphabetic(10)); - private static final Topic TOPIC_FOR_CHECK_FILTERS = new Topic() - .setName("topic-for-check-filters-" + randomAlphabetic(5)) - .setMessageKey(randomAlphabetic(5)) - .setMessageValue(randomAlphabetic(10)); - private static final Topic TOPIC_TO_RECREATE = new Topic() - .setName("topic-to-recreate-attribute-" + randomAlphabetic(5)) - .setMessageKey(randomAlphabetic(5)) - .setMessageValue(randomAlphabetic(10)); - private static final Topic TOPIC_FOR_CHECK_MESSAGES_COUNT = new Topic() - .setName("topic-for-check-messages-count" + randomAlphabetic(5)) - .setMessageKey(randomAlphabetic(5)) - .setMessageValue(randomAlphabetic(10)); + private static final Topic PRODUCE_MESSAGE_TOPIC = createTopic(); + private static final Topic CLEAR_MESSAGE_TOPIC = createTopic(); + private static final Topic CHECK_FILTERS_TOPIC = createTopic(); + private static final Topic RECREATE_TOPIC = createTopic(); + private static final Topic MESSAGES_COUNT_TOPIC = createTopic(); private static final List TOPIC_LIST = new ArrayList<>(); @BeforeClass(alwaysRun = true) public void beforeClass() { - TOPIC_LIST.addAll(List.of(TOPIC_FOR_MESSAGES, TOPIC_FOR_CHECK_FILTERS, TOPIC_TO_CLEAR_AND_PURGE_MESSAGES, - TOPIC_TO_RECREATE, TOPIC_FOR_CHECK_MESSAGES_COUNT)); + TOPIC_LIST.addAll(List.of(PRODUCE_MESSAGE_TOPIC, CHECK_FILTERS_TOPIC, CLEAR_MESSAGE_TOPIC, + RECREATE_TOPIC, MESSAGES_COUNT_TOPIC)); TOPIC_LIST.forEach(topic -> apiService.createTopic(topic)); - IntStream.range(1, 3).forEach(i -> apiService.sendMessage(TOPIC_FOR_CHECK_FILTERS)); - TimeUtils.waitUntilNewMinuteStarted(); - IntStream.range(1, 3).forEach(i -> apiService.sendMessage(TOPIC_FOR_CHECK_FILTERS)); - IntStream.range(1, 110).forEach(i -> apiService.sendMessage(TOPIC_FOR_CHECK_MESSAGES_COUNT)); + IntStream.range(1, 3).forEach(i -> apiService.sendMessage(CHECK_FILTERS_TOPIC)); + TimeUtil.waitUntilNewMinuteStarted(); + IntStream.range(1, 3).forEach(i -> apiService.sendMessage(CHECK_FILTERS_TOPIC)); + IntStream.range(1, 110).forEach(i -> apiService.sendMessage(MESSAGES_COUNT_TOPIC)); } - @QaseId(222) @Test(priority = 1) public void produceMessageCheck() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_MESSAGES.getName()); + navigateToTopicsAndOpenDetails(PRODUCE_MESSAGE_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES); - produceMessage(TOPIC_FOR_MESSAGES); - Assert.assertEquals(topicDetails.getMessageByKey(TOPIC_FOR_MESSAGES.getMessageKey()).getValue(), - TOPIC_FOR_MESSAGES.getMessageValue(), "message.getValue()"); + produceMessage(PRODUCE_MESSAGE_TOPIC); + Assert.assertEquals(topicDetails.getMessageByKey(PRODUCE_MESSAGE_TOPIC.getMessageKey()).getValue(), + PRODUCE_MESSAGE_TOPIC.getMessageValue(), "message.getValue()"); } - @QaseId(19) @Test(priority = 2) public void clearMessageCheck() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_MESSAGES.getName()); + navigateToTopicsAndOpenDetails(PRODUCE_MESSAGE_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW); int messageAmount = topicDetails.getMessageCountAmount(); - produceMessage(TOPIC_FOR_MESSAGES); + produceMessage(PRODUCE_MESSAGE_TOPIC); Assert.assertEquals(topicDetails.getMessageCountAmount(), messageAmount + 1, "getMessageCountAmount()"); topicDetails .openDotMenu() @@ -84,44 +67,42 @@ public void clearMessageCheck() { Assert.assertEquals(topicDetails.getMessageCountAmount(), 0, "getMessageCountAmount()"); } - @QaseId(239) @Test(priority = 3) - public void checkClearTopicMessage() { - navigateToTopicsAndOpenDetails(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()); + public void clearTopicMessageCheck() { + navigateToTopicsAndOpenDetails(CLEAR_MESSAGE_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW); - produceMessage(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES); + produceMessage(CLEAR_MESSAGE_TOPIC); navigateToTopics(); - Assert.assertEquals(topicsList.getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()).getNumberOfMessages(), 1, + Assert.assertEquals(topicsList.getTopicItem(CLEAR_MESSAGE_TOPIC.getName()).getNumberOfMessages(), 1, "getNumberOfMessages()"); topicsList - .openDotMenuByTopicName(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()) + .openDotMenuByTopicName(CLEAR_MESSAGE_TOPIC.getName()) .clickClearMessagesBtn() .clickConfirmBtnMdl(); SoftAssert softly = new SoftAssert(); softly.assertTrue(topicsList.isAlertWithMessageVisible(BasePage.AlertHeader.SUCCESS, - String.format("%s messages have been successfully cleared!", TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName())), + String.format("%s messages have been successfully cleared!", CLEAR_MESSAGE_TOPIC.getName())), "isAlertWithMessageVisible()"); - softly.assertEquals(topicsList.getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()).getNumberOfMessages(), 0, + softly.assertEquals(topicsList.getTopicItem(CLEAR_MESSAGE_TOPIC.getName()).getNumberOfMessages(), 0, "getNumberOfMessages()"); softly.assertAll(); } - @QaseId(10) @Test(priority = 4) - public void checkPurgeMessagePossibility() { + public void purgeMessagePossibilityCheck() { navigateToTopics(); - int messageAmount = topicsList.getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()).getNumberOfMessages(); + int messageAmount = topicsList.getTopicItem(CLEAR_MESSAGE_TOPIC.getName()).getNumberOfMessages(); topicsList - .openTopic(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()); + .openTopic(CLEAR_MESSAGE_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW); - produceMessage(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES); + produceMessage(CLEAR_MESSAGE_TOPIC); navigateToTopics(); - Assert.assertEquals(topicsList.getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()).getNumberOfMessages(), + Assert.assertEquals(topicsList.getTopicItem(CLEAR_MESSAGE_TOPIC.getName()).getNumberOfMessages(), messageAmount + 1, "getNumberOfMessages()"); topicsList - .getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()) + .getTopicItem(CLEAR_MESSAGE_TOPIC.getName()) .selectItem(true) .clickPurgeMessagesOfSelectedTopicsBtn(); Assert.assertTrue(topicsList.isConfirmationMdlVisible(), "isConfirmationMdlVisible()"); @@ -131,23 +112,22 @@ public void checkPurgeMessagePossibility() { .clickConfirmBtnMdl(); SoftAssert softly = new SoftAssert(); softly.assertTrue(topicsList.isAlertWithMessageVisible(BasePage.AlertHeader.SUCCESS, - String.format("%s messages have been successfully cleared!", TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName())), + String.format("%s messages have been successfully cleared!", CLEAR_MESSAGE_TOPIC.getName())), "isAlertWithMessageVisible()"); - softly.assertEquals(topicsList.getTopicItem(TOPIC_TO_CLEAR_AND_PURGE_MESSAGES.getName()).getNumberOfMessages(), 0, + softly.assertEquals(topicsList.getTopicItem(CLEAR_MESSAGE_TOPIC.getName()).getNumberOfMessages(), 0, "getNumberOfMessages()"); softly.assertAll(); } - @QaseId(15) @Test(priority = 6) - public void checkMessageFilteringByOffset() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + public void messageFilteringByOffsetCheck() { + navigateToTopicsAndOpenDetails(CHECK_FILTERS_TOPIC.getName()); int nextOffset = topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) + .selectSeekTypeDdlMessagesTab(SeekType.FROM_OFFSET) .getAllMessages().stream() .findFirst().orElseThrow().getOffset() + 1; topicDetails - .selectSeekTypeDdlMessagesTab("Offset") .setSeekTypeValueFldMessagesTab(String.valueOf(nextOffset)) .clickSubmitFiltersBtnMessagesTab(); SoftAssert softly = new SoftAssert(); @@ -158,12 +138,11 @@ public void checkMessageFilteringByOffset() { } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3215") - @Issue("https://github.com/kafbat/kafka-ui/issues/2345") - @QaseId(16) + @Issue("https://github.com/kafbat/kafka-ui/issues/281") + @Issue("https://github.com/kafbat/kafka-ui/issues/282") @Test(priority = 7) - public void checkMessageFilteringByTimestamp() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + public void messageFilteringByTimestampCheck() { + navigateToTopicsAndOpenDetails(CHECK_FILTERS_TOPIC.getName()); LocalDateTime firstTimestamp = topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) .getMessageByOffset(0).getTimestamp(); @@ -171,7 +150,7 @@ public void checkMessageFilteringByTimestamp() { .filter(message -> message.getTimestamp().getMinute() != firstTimestamp.getMinute()) .findFirst().orElseThrow().getTimestamp(); topicDetails - .selectSeekTypeDdlMessagesTab("Timestamp") + .selectSeekTypeDdlMessagesTab(SeekType.SINCE_TIME) .openCalendarSeekType() .selectDateAndTimeByCalendar(nextTimestamp) .clickSubmitFiltersBtnMessagesTab(); @@ -182,10 +161,9 @@ public void checkMessageFilteringByTimestamp() { softly.assertAll(); } - @QaseId(246) @Test(priority = 8) - public void checkClearTopicMessageFromOverviewTab() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + public void clearTopicMessageFromOverviewTabCheck() { + navigateToTopicsAndOpenDetails(CHECK_FILTERS_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW) .openDotMenu() @@ -193,42 +171,39 @@ public void checkClearTopicMessageFromOverviewTab() { .clickConfirmBtnMdl(); SoftAssert softly = new SoftAssert(); softly.assertTrue(topicDetails.isAlertWithMessageVisible(BasePage.AlertHeader.SUCCESS, - String.format("%s messages have been successfully cleared!", TOPIC_FOR_CHECK_FILTERS.getName())), + String.format("%s messages have been successfully cleared!", CHECK_FILTERS_TOPIC.getName())), "isAlertWithMessageVisible()"); - softly.assertEquals(topicDetails.getMessageCountAmount(), 0, - "getMessageCountAmount()= " + topicDetails.getMessageCountAmount()); + softly.assertEquals(topicDetails.getMessageCountAmount(), 0, "getMessageCountAmount()"); softly.assertAll(); } - @QaseId(240) @Test(priority = 9) - public void checkRecreateTopic() { - navigateToTopicsAndOpenDetails(TOPIC_TO_RECREATE.getName()); + public void recreateTopicCheck() { + navigateToTopicsAndOpenDetails(RECREATE_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW); - produceMessage(TOPIC_TO_RECREATE); + produceMessage(RECREATE_TOPIC); navigateToTopics(); - Assert.assertEquals(topicsList.getTopicItem(TOPIC_TO_RECREATE.getName()).getNumberOfMessages(), 1, + Assert.assertEquals(topicsList.getTopicItem(RECREATE_TOPIC.getName()).getNumberOfMessages(), 1, "getNumberOfMessages()"); topicsList - .openDotMenuByTopicName(TOPIC_TO_RECREATE.getName()) + .openDotMenuByTopicName(RECREATE_TOPIC.getName()) .clickRecreateTopicBtn() .clickConfirmBtnMdl(); SoftAssert softly = new SoftAssert(); softly.assertTrue(topicDetails.isAlertWithMessageVisible(BasePage.AlertHeader.SUCCESS, - String.format("Topic %s successfully recreated!", TOPIC_TO_RECREATE.getName())), + String.format("Topic %s successfully recreated!", RECREATE_TOPIC.getName())), "isAlertWithMessageVisible()"); - softly.assertEquals(topicsList.getTopicItem(TOPIC_TO_RECREATE.getName()).getNumberOfMessages(), 0, + softly.assertEquals(topicsList.getTopicItem(RECREATE_TOPIC.getName()).getNumberOfMessages(), 0, "getNumberOfMessages()"); softly.assertAll(); } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3129") - @QaseId(267) + @Issue("https://github.com/kafbat/kafka-ui/issues/270") @Test(priority = 10) - public void checkMessagesCountPerPageWithinTopic() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_MESSAGES_COUNT.getName()); + public void messagesCountPerPageCheck() { + navigateToTopicsAndOpenDetails(MESSAGES_COUNT_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES); int messagesPerPage = topicDetails.getAllMessages().size(); @@ -242,7 +217,7 @@ public void checkMessagesCountPerPageWithinTopic() { topicDetails .clickNextButton(); softly.assertEquals(topicDetails.getAllMessages().stream().findFirst().orElseThrow().getOffset(), - lastOffsetOnPage + 1, "findFirst().getOffset()"); + lastOffsetOnPage - 1, "getAllMessages().findFirst().getOffset()"); softly.assertTrue(topicDetails.isBackButtonEnabled(), "isBackButtonEnabled()"); softly.assertFalse(topicDetails.isNextButtonEnabled(), "isNextButtonEnabled()"); softly.assertAll(); diff --git a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/TopicsTest.java b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/TopicsTest.java index 1b4b0f40d..1fa5d0b3d 100644 --- a/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/TopicsTest.java +++ b/e2e-tests/src/test/java/io/kafbat/ui/smokesuite/topics/TopicsTest.java @@ -1,20 +1,20 @@ package io.kafbat.ui.smokesuite.topics; -import static io.kafbat.ui.pages.BasePage.AlertHeader.SUCCESS; +import static io.kafbat.ui.screens.BasePage.AlertHeader.SUCCESS; +import static io.kafbat.ui.variables.Common.FILTER_CODE_JSON; +import static io.kafbat.ui.variables.Common.FILTER_CODE_STRING; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.apache.commons.lang3.RandomUtils.nextInt; import com.codeborne.selenide.Condition; import io.kafbat.ui.BaseTest; import io.kafbat.ui.models.Topic; -import io.kafbat.ui.pages.BasePage; -import io.kafbat.ui.pages.topics.TopicDetails; -import io.kafbat.ui.pages.topics.enums.CleanupPolicyValue; -import io.kafbat.ui.pages.topics.enums.CustomParameterType; -import io.kafbat.ui.pages.topics.enums.MaxSizeOnDisk; -import io.kafbat.ui.pages.topics.enums.TimeToRetain; +import io.kafbat.ui.screens.topics.TopicDetails; +import io.kafbat.ui.screens.topics.enums.CleanupPolicyValue; +import io.kafbat.ui.screens.topics.enums.CustomParameterType; +import io.kafbat.ui.screens.topics.enums.MaxSizeOnDisk; +import io.kafbat.ui.screens.topics.enums.TimeToRetain; import io.qameta.allure.Issue; -import io.qase.api.annotation.QaseId; import java.util.ArrayList; import java.util.List; import org.testng.Assert; @@ -26,82 +26,73 @@ public class TopicsTest extends BaseTest { - private static final Topic TOPIC_TO_CREATE = new Topic() - .setName("new-topic-" + randomAlphabetic(5)) - .setNumberOfPartitions(1) + private static final Topic CREATE_TOPIC = Topic.createTopic() .setCustomParameterType(CustomParameterType.COMPRESSION_TYPE) .setCustomParameterValue("producer") .setCleanupPolicyValue(CleanupPolicyValue.DELETE); - private static final Topic TOPIC_TO_UPDATE_AND_DELETE = new Topic() - .setName("topic-to-update-and-delete-" + randomAlphabetic(5)) - .setNumberOfPartitions(1) + private static final Topic UPDATE_TOPIC = Topic.createTopic() .setCleanupPolicyValue(CleanupPolicyValue.DELETE) .setTimeToRetain(TimeToRetain.BTN_7_DAYS) .setMaxSizeOnDisk(MaxSizeOnDisk.NOT_SET) .setMaxMessageBytes("1048588") .setMessageKey(randomAlphabetic(5)) .setMessageValue(randomAlphabetic(10)); - private static final Topic TOPIC_TO_CHECK_SETTINGS = new Topic() - .setName("new-topic-" + randomAlphabetic(5)) - .setNumberOfPartitions(1) + private static final Topic DELETE_TOPIC = Topic.createTopic(); + private static final Topic SETTINGS_TOPIC = Topic.createTopic() .setMaxMessageBytes("1000012") .setMaxSizeOnDisk(MaxSizeOnDisk.NOT_SET); - private static final Topic TOPIC_FOR_CHECK_FILTERS = new Topic() - .setName("topic-for-check-filters-" + randomAlphabetic(5)); - private static final Topic TOPIC_FOR_DELETE = new Topic() - .setName("topic-to-delete-" + randomAlphabetic(5)); + private static final Topic FILTERS_TOPIC = Topic.createTopic(); private static final List TOPIC_LIST = new ArrayList<>(); @BeforeClass(alwaysRun = true) public void beforeClass() { - TOPIC_LIST.addAll(List.of(TOPIC_TO_UPDATE_AND_DELETE, TOPIC_FOR_DELETE, TOPIC_FOR_CHECK_FILTERS)); + TOPIC_LIST.addAll(List.of(UPDATE_TOPIC, DELETE_TOPIC, FILTERS_TOPIC)); TOPIC_LIST.forEach(topic -> apiService.createTopic(topic)); } - @QaseId(199) @Test(priority = 1) - public void createTopic() { + public void createTopicCheck() { navigateToTopics(); topicsList .clickAddTopicBtn(); topicCreateEditForm .waitUntilScreenReady() - .setTopicName(TOPIC_TO_CREATE.getName()) - .setNumberOfPartitions(TOPIC_TO_CREATE.getNumberOfPartitions()) - .selectCleanupPolicy(TOPIC_TO_CREATE.getCleanupPolicyValue()) + .setTopicName(CREATE_TOPIC.getName()) + .setNumberOfPartitions(CREATE_TOPIC.getNumberOfPartitions()) + .selectCleanupPolicy(CREATE_TOPIC.getCleanupPolicyValue()) .clickSaveTopicBtn(); - navigateToTopicsAndOpenDetails(TOPIC_TO_CREATE.getName()); + navigateToTopicsAndOpenDetails(CREATE_TOPIC.getName()); SoftAssert softly = new SoftAssert(); - softly.assertTrue(topicDetails.isTopicHeaderVisible(TOPIC_TO_CREATE.getName()), "isTopicHeaderVisible()"); - softly.assertEquals(topicDetails.getCleanUpPolicy(), TOPIC_TO_CREATE.getCleanupPolicyValue().toString(), + softly.assertTrue(topicDetails.isTopicHeaderVisible(CREATE_TOPIC.getName()), + String.format("isTopicHeaderVisible()[%s]", CREATE_TOPIC.getName())); + softly.assertEquals(topicDetails.getCleanUpPolicy(), CREATE_TOPIC.getCleanupPolicyValue().toString(), "getCleanUpPolicy()"); - softly.assertEquals(topicDetails.getPartitions(), TOPIC_TO_CREATE.getNumberOfPartitions(), "getPartitions()"); + softly.assertEquals(topicDetails.getPartitions(), CREATE_TOPIC.getNumberOfPartitions(), "getPartitions()"); softly.assertAll(); navigateToTopics(); - Assert.assertTrue(topicsList.isTopicVisible(TOPIC_TO_CREATE.getName()), "isTopicVisible()"); - TOPIC_LIST.add(TOPIC_TO_CREATE); + Assert.assertTrue(topicsList.isTopicVisible(CREATE_TOPIC.getName()), + String.format("isTopicVisible()[%s]", CREATE_TOPIC.getName())); + TOPIC_LIST.add(CREATE_TOPIC); } - @QaseId(7) @Test(priority = 2) - void checkAvailableOperations() { + void availableOperationsCheck() { navigateToTopics(); topicsList - .getTopicItem(TOPIC_TO_UPDATE_AND_DELETE.getName()) + .getTopicItem(UPDATE_TOPIC.getName()) .selectItem(true); verifyElementsCondition(topicsList.getActionButtons(), Condition.enabled); topicsList - .getTopicItem(TOPIC_FOR_CHECK_FILTERS.getName()) + .getTopicItem(FILTERS_TOPIC.getName()) .selectItem(true); Assert.assertFalse(topicsList.isCopySelectedTopicBtnEnabled(), "isCopySelectedTopicBtnEnabled()"); } @Ignore - @Issue("https://github.com/kafbat/kafka-ui/issues/3071") - @QaseId(268) + @Issue("https://github.com/kafbat/kafka-ui/issues/279") @Test(priority = 3) - public void checkCustomParametersWithinEditExistingTopic() { - navigateToTopicsAndOpenDetails(TOPIC_TO_UPDATE_AND_DELETE.getName()); + public void customParametersWithinEditExistingTopicCheck() { + navigateToTopicsAndOpenDetails(UPDATE_TOPIC.getName()); topicDetails .openDotMenu() .clickEditSettingsMenu(); @@ -111,16 +102,16 @@ public void checkCustomParametersWithinEditExistingTopic() { .clickAddCustomParameterTypeButton() .openCustomParameterTypeDdl() .getAllDdlOptions() + .asFixedIterable() .forEach(option -> softly.assertTrue(!option.is(Condition.attribute("disabled")), option.getText() + " is enabled:")); softly.assertAll(); } - @QaseId(197) @Test(priority = 4) - public void updateTopic() { - navigateToTopicsAndOpenDetails(TOPIC_TO_UPDATE_AND_DELETE.getName()); + public void updateTopicCheck() { + navigateToTopicsAndOpenDetails(UPDATE_TOPIC.getName()); topicDetails .openDotMenu() .clickEditSettingsMenu(); @@ -128,77 +119,75 @@ public void updateTopic() { .waitUntilScreenReady(); SoftAssert softly = new SoftAssert(); softly.assertEquals(topicCreateEditForm.getCleanupPolicy(), - TOPIC_TO_UPDATE_AND_DELETE.getCleanupPolicyValue().getVisibleText(), "getCleanupPolicy()"); + UPDATE_TOPIC.getCleanupPolicyValue().getVisibleText(), "getCleanupPolicy()"); softly.assertEquals(topicCreateEditForm.getTimeToRetain(), - TOPIC_TO_UPDATE_AND_DELETE.getTimeToRetain().getValue(), "getTimeToRetain()"); + UPDATE_TOPIC.getTimeToRetain().getValue(), "getTimeToRetain()"); softly.assertEquals(topicCreateEditForm.getMaxSizeOnDisk(), - TOPIC_TO_UPDATE_AND_DELETE.getMaxSizeOnDisk().getVisibleText(), "getMaxSizeOnDisk()"); + UPDATE_TOPIC.getMaxSizeOnDisk().getVisibleText(), "getMaxSizeOnDisk()"); softly.assertEquals(topicCreateEditForm.getMaxMessageBytes(), - TOPIC_TO_UPDATE_AND_DELETE.getMaxMessageBytes(), "getMaxMessageBytes()"); + UPDATE_TOPIC.getMaxMessageBytes(), "getMaxMessageBytes()"); softly.assertAll(); - TOPIC_TO_UPDATE_AND_DELETE + UPDATE_TOPIC .setCleanupPolicyValue(CleanupPolicyValue.COMPACT) .setTimeToRetain(TimeToRetain.BTN_2_DAYS) .setMaxSizeOnDisk(MaxSizeOnDisk.SIZE_50_GB).setMaxMessageBytes("1048589"); topicCreateEditForm - .selectCleanupPolicy((TOPIC_TO_UPDATE_AND_DELETE.getCleanupPolicyValue())) - .setTimeToRetainDataByButtons(TOPIC_TO_UPDATE_AND_DELETE.getTimeToRetain()) - .setMaxSizeOnDiskInGB(TOPIC_TO_UPDATE_AND_DELETE.getMaxSizeOnDisk()) - .setMaxMessageBytes(TOPIC_TO_UPDATE_AND_DELETE.getMaxMessageBytes()) + .selectCleanupPolicy((UPDATE_TOPIC.getCleanupPolicyValue())) + .setTimeToRetainDataByButtons(UPDATE_TOPIC.getTimeToRetain()) + .setMaxSizeOnDiskInGB(UPDATE_TOPIC.getMaxSizeOnDisk()) + .setMaxMessageBytes(UPDATE_TOPIC.getMaxMessageBytes()) .clickSaveTopicBtn(); softly.assertTrue(topicDetails.isAlertWithMessageVisible(SUCCESS, "Topic successfully updated."), "isAlertWithMessageVisible()"); - softly.assertTrue(topicDetails.isTopicHeaderVisible(TOPIC_TO_UPDATE_AND_DELETE.getName()), - "isTopicHeaderVisible()"); + softly.assertTrue(topicDetails.isTopicHeaderVisible(UPDATE_TOPIC.getName()), + String.format("isTopicHeaderVisible()[%s]", UPDATE_TOPIC.getName())); softly.assertAll(); topicDetails .waitUntilScreenReady(); - navigateToTopicsAndOpenDetails(TOPIC_TO_UPDATE_AND_DELETE.getName()); + navigateToTopicsAndOpenDetails(UPDATE_TOPIC.getName()); topicDetails .openDotMenu() .clickEditSettingsMenu(); softly.assertFalse(topicCreateEditForm.isNameFieldEnabled(), "isNameFieldEnabled()"); softly.assertEquals(topicCreateEditForm.getCleanupPolicy(), - TOPIC_TO_UPDATE_AND_DELETE.getCleanupPolicyValue().getVisibleText(), "getCleanupPolicy()"); + UPDATE_TOPIC.getCleanupPolicyValue().getVisibleText(), "getCleanupPolicy()"); softly.assertEquals(topicCreateEditForm.getTimeToRetain(), - TOPIC_TO_UPDATE_AND_DELETE.getTimeToRetain().getValue(), "getTimeToRetain()"); + UPDATE_TOPIC.getTimeToRetain().getValue(), "getTimeToRetain()"); softly.assertEquals(topicCreateEditForm.getMaxSizeOnDisk(), - TOPIC_TO_UPDATE_AND_DELETE.getMaxSizeOnDisk().getVisibleText(), "getMaxSizeOnDisk()"); + UPDATE_TOPIC.getMaxSizeOnDisk().getVisibleText(), "getMaxSizeOnDisk()"); softly.assertEquals(topicCreateEditForm.getMaxMessageBytes(), - TOPIC_TO_UPDATE_AND_DELETE.getMaxMessageBytes(), "getMaxMessageBytes()"); + UPDATE_TOPIC.getMaxMessageBytes(), "getMaxMessageBytes()"); softly.assertAll(); } - @QaseId(242) @Test(priority = 5) - public void removeTopicFromTopicList() { + public void removeTopicFromListCheck() { navigateToTopics(); topicsList - .openDotMenuByTopicName(TOPIC_TO_UPDATE_AND_DELETE.getName()) + .openDotMenuByTopicName(UPDATE_TOPIC.getName()) .clickRemoveTopicBtn() .clickConfirmBtnMdl(); Assert.assertTrue(topicsList.isAlertWithMessageVisible(SUCCESS, - String.format("Topic %s successfully deleted!", TOPIC_TO_UPDATE_AND_DELETE.getName())), + String.format("Topic %s successfully deleted!", UPDATE_TOPIC.getName())), "isAlertWithMessageVisible()"); - TOPIC_LIST.remove(TOPIC_TO_UPDATE_AND_DELETE); + TOPIC_LIST.remove(UPDATE_TOPIC); } - @QaseId(207) @Test(priority = 6) - public void deleteTopic() { - navigateToTopicsAndOpenDetails(TOPIC_FOR_DELETE.getName()); + public void deleteTopicCheck() { + navigateToTopicsAndOpenDetails(DELETE_TOPIC.getName()); topicDetails .openDotMenu() .clickDeleteTopicMenu() .clickConfirmBtnMdl(); navigateToTopics(); - Assert.assertFalse(topicsList.isTopicVisible(TOPIC_FOR_DELETE.getName()), "isTopicVisible"); - TOPIC_LIST.remove(TOPIC_FOR_DELETE); + Assert.assertFalse(topicsList.isTopicVisible(DELETE_TOPIC.getName()), + String.format("isTopicVisible()[%s]", DELETE_TOPIC.getName())); + TOPIC_LIST.remove(DELETE_TOPIC); } - @QaseId(20) @Test(priority = 7) - public void redirectToConsumerFromTopic() { + public void redirectToConsumerFromTopicCheck() { String topicName = "source-activities"; String consumerGroupId = "connect-sink_postgres_activities"; navigateToTopicsAndOpenDetails(topicName); @@ -215,9 +204,8 @@ public void redirectToConsumerFromTopic() { softly.assertAll(); } - @QaseId(4) @Test(priority = 8) - public void checkTopicCreatePossibility() { + public void createTopicPossibilityCheck() { navigateToTopics(); topicsList .clickAddTopicBtn(); @@ -236,48 +224,45 @@ public void checkTopicCreatePossibility() { Assert.assertTrue(topicCreateEditForm.isCreateTopicButtonEnabled(), "isCreateTopicButtonEnabled()"); } - @QaseId(266) @Test(priority = 9) - public void checkTimeToRetainDataCustomValueWithEditingTopic() { - Topic topicToRetainData = new Topic() - .setName("topic-to-retain-data-" + randomAlphabetic(5)) + public void timeToRetainDataCustomValueWithEditingTopicCheck() { + Topic retainDataTopic = Topic.createTopic() .setTimeToRetainData("86400000"); navigateToTopics(); topicsList .clickAddTopicBtn(); topicCreateEditForm .waitUntilScreenReady() - .setTopicName(topicToRetainData.getName()) + .setTopicName(retainDataTopic.getName()) .setNumberOfPartitions(1) .setTimeToRetainDataInMs("604800000"); Assert.assertEquals(topicCreateEditForm.getTimeToRetain(), "604800000", "getTimeToRetain()"); topicCreateEditForm - .setTimeToRetainDataInMs(topicToRetainData.getTimeToRetainData()) + .setTimeToRetainDataInMs(retainDataTopic.getTimeToRetainData()) .clickSaveTopicBtn(); topicDetails .waitUntilScreenReady() .openDotMenu() .clickEditSettingsMenu(); - Assert.assertEquals(topicCreateEditForm.getTimeToRetain(), topicToRetainData.getTimeToRetainData(), + Assert.assertEquals(topicCreateEditForm.getTimeToRetain(), retainDataTopic.getTimeToRetainData(), "getTimeToRetain()"); topicDetails .openDetailsTab(TopicDetails.TopicMenu.SETTINGS); - Assert.assertEquals(topicDetails.getSettingsGridValueByKey("retention.ms"), topicToRetainData.getTimeToRetainData(), - "getSettingsGridValueByKey()"); - TOPIC_LIST.add(topicToRetainData); + Assert.assertEquals(topicDetails.getSettingsGridValueByKey("retention.ms"), retainDataTopic.getTimeToRetainData(), + "getTimeToRetainData()"); + TOPIC_LIST.add(retainDataTopic); } - @QaseId(6) @Test(priority = 10) - public void checkCustomParametersWithinCreateNewTopic() { + public void customParametersWithinCreateNewTopicCheck() { navigateToTopics(); topicsList .clickAddTopicBtn(); topicCreateEditForm .waitUntilScreenReady() - .setTopicName(TOPIC_TO_CREATE.getName()) + .setTopicName(CREATE_TOPIC.getName()) .clickAddCustomParameterTypeButton() - .setCustomParameterType(TOPIC_TO_CREATE.getCustomParameterType()); + .setCustomParameterType(CREATE_TOPIC.getCustomParameterType()); Assert.assertTrue(topicCreateEditForm.isDeleteCustomParameterButtonEnabled(), "isDeleteCustomParameterButtonEnabled()"); topicCreateEditForm @@ -286,19 +271,17 @@ public void checkCustomParametersWithinCreateNewTopic() { "isValidationMessageCustomParameterValueVisible()"); } - @QaseId(2) @Test(priority = 11) - public void checkTopicListElements() { + public void topicListElementsCheck() { navigateToTopics(); verifyElementsCondition(topicsList.getAllVisibleElements(), Condition.visible); verifyElementsCondition(topicsList.getAllEnabledElements(), Condition.enabled); } - @QaseId(12) @Test(priority = 12) - public void addNewFilterWithinTopic() { + public void addNewFilterWithinTopicCheck() { String filterName = randomAlphabetic(5); - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + navigateToTopicsAndOpenDetails(FILTERS_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) .clickMessagesAddFiltersBtn() @@ -308,18 +291,19 @@ public void addNewFilterWithinTopic() { verifyElementsCondition(topicDetails.getAllAddFilterModalDisabledElements(), Condition.disabled); Assert.assertFalse(topicDetails.isSaveThisFilterCheckBoxSelected(), "isSaveThisFilterCheckBoxSelected()"); topicDetails - .setFilterCodeFldAddFilterMdl(filterName); + .setFilterCodeFldAddFilterMdl(FILTER_CODE_STRING) + .setDisplayNameFldAddFilterMdl(filterName); Assert.assertTrue(topicDetails.isAddFilterBtnAddFilterMdlEnabled(), "isAddFilterBtnAddFilterMdlEnabled()"); topicDetails.clickAddFilterBtnAndCloseMdl(true); - Assert.assertTrue(topicDetails.isActiveFilterVisible(filterName), "isActiveFilterVisible()"); + Assert.assertTrue(topicDetails.isActiveFilterVisible(filterName), + String.format("isActiveFilterVisible()[%s]", filterName)); } - @QaseId(352) @Test(priority = 13) public void editActiveSmartFilterCheck() { + String filterCode = FILTER_CODE_STRING; String filterName = randomAlphabetic(5); - String filterCode = randomAlphabetic(5); - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + navigateToTopicsAndOpenDetails(FILTERS_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) .clickMessagesAddFiltersBtn() @@ -334,77 +318,69 @@ public void editActiveSmartFilterCheck() { softly.assertEquals(topicDetails.getFilterNameValue(), filterName, "getFilterNameValue()"); softly.assertAll(); String newFilterName = randomAlphabetic(5); - String newFilterCode = randomAlphabetic(5); topicDetails - .setFilterCodeFldAddFilterMdl(newFilterCode) + .setFilterCodeFldAddFilterMdl(FILTER_CODE_JSON) .setDisplayNameFldAddFilterMdl(newFilterName) .clickSaveFilterBtnAndCloseMdl(true); - softly.assertTrue(topicDetails.isActiveFilterVisible(newFilterName), "isActiveFilterVisible()"); - softly.assertEquals(topicDetails.getSearchFieldValue(), newFilterCode, "getSearchFieldValue()"); - softly.assertAll(); + Assert.assertTrue(topicDetails.isActiveFilterVisible(newFilterName), + String.format("isActiveFilterVisible()[%s]", newFilterName)); } - @QaseId(13) @Test(priority = 14) - public void checkFilterSavingWithinSavedFilters() { - String displayName = randomAlphabetic(5); - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + public void saveSmartFilterCheck() { + String filterName = randomAlphabetic(5); + navigateToTopicsAndOpenDetails(FILTERS_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) .clickMessagesAddFiltersBtn() .waitUntilAddFiltersMdlVisible() - .setFilterCodeFldAddFilterMdl(randomAlphabetic(4)) + .setFilterCodeFldAddFilterMdl(FILTER_CODE_JSON) .selectSaveThisFilterCheckboxMdl(true) - .setDisplayNameFldAddFilterMdl(displayName); + .setDisplayNameFldAddFilterMdl(filterName); Assert.assertTrue(topicDetails.isAddFilterBtnAddFilterMdlEnabled(), "isAddFilterBtnAddFilterMdlEnabled()"); topicDetails - .clickAddFilterBtnAndCloseMdl(false) - .openSavedFiltersListMdl(); - Assert.assertTrue(topicDetails.isFilterVisibleAtSavedFiltersMdl(displayName), - "isFilterVisibleAtSavedFiltersMdl()"); + .clickAddFilterBtnAndCloseMdl(false); + Assert.assertTrue(topicDetails.isFilterVisibleAtSavedFiltersMdl(filterName), + String.format("isFilterVisibleAtSavedFiltersMdl()[%s]", filterName)); } - @QaseId(14) @Test(priority = 15) - public void checkApplyingSavedFilterWithinTopicMessages() { - String displayName = randomAlphabetic(5); - navigateToTopicsAndOpenDetails(TOPIC_FOR_CHECK_FILTERS.getName()); + public void applySavedFilterWithinTopicMessagesCheck() { + String filterName = randomAlphabetic(5); + navigateToTopicsAndOpenDetails(FILTERS_TOPIC.getName()); topicDetails .openDetailsTab(TopicDetails.TopicMenu.MESSAGES) .clickMessagesAddFiltersBtn() .waitUntilAddFiltersMdlVisible() - .setFilterCodeFldAddFilterMdl(randomAlphabetic(4)) + .setFilterCodeFldAddFilterMdl(FILTER_CODE_JSON) .selectSaveThisFilterCheckboxMdl(true) - .setDisplayNameFldAddFilterMdl(displayName) + .setDisplayNameFldAddFilterMdl(filterName) .clickAddFilterBtnAndCloseMdl(false) - .openSavedFiltersListMdl() - .selectFilterAtSavedFiltersMdl(displayName) - .clickSelectFilterBtnAtSavedFiltersMdl(); - Assert.assertTrue(topicDetails.isActiveFilterVisible(displayName), "isActiveFilterVisible()"); + .selectFilterAtSavedFiltersMdl(filterName); + Assert.assertTrue(topicDetails.isActiveFilterVisible(filterName), + String.format("isActiveFilterVisible()[%s]", filterName)); } - @QaseId(11) @Test(priority = 16) - public void checkShowInternalTopicsButton() { + public void showInternalTopicsButtonCheck() { navigateToTopics(); topicsList .setShowInternalRadioButton(true); - Assert.assertTrue(topicsList.getInternalTopics().size() > 0, "getInternalTopics()"); + Assert.assertFalse(topicsList.getInternalTopics().isEmpty(), "getInternalTopics().isEmpty()"); topicsList .goToLastPage(); - Assert.assertTrue(topicsList.getNonInternalTopics().size() > 0, "getNonInternalTopics()"); + Assert.assertFalse(topicsList.getNonInternalTopics().isEmpty(), "getNonInternalTopics().isEmpty()"); topicsList .setShowInternalRadioButton(false); SoftAssert softly = new SoftAssert(); - softly.assertEquals(topicsList.getInternalTopics().size(), 0, "getInternalTopics()"); - softly.assertTrue(topicsList.getNonInternalTopics().size() > 0, "getNonInternalTopics()"); + softly.assertEquals(topicsList.getInternalTopics().size(), 0, "getInternalTopics().size()"); + softly.assertTrue(!topicsList.getNonInternalTopics().isEmpty(), "getNonInternalTopics().isEmpty()"); softly.assertAll(); } - @QaseId(334) @Test(priority = 17) - public void checkInternalTopicsNaming() { + public void internalTopicsNamingCheck() { navigateToTopics(); SoftAssert softly = new SoftAssert(); topicsList @@ -415,32 +391,31 @@ public void checkInternalTopicsNaming() { softly.assertAll(); } - @QaseId(56) @Test(priority = 18) - public void checkRetentionBytesAccordingToMaxSizeOnDisk() { + public void retentionBytesAccordingToMaxSizeOnDiskCheck() { navigateToTopics(); topicsList .clickAddTopicBtn(); topicCreateEditForm .waitUntilScreenReady() - .setTopicName(TOPIC_TO_CHECK_SETTINGS.getName()) - .setNumberOfPartitions(TOPIC_TO_CHECK_SETTINGS.getNumberOfPartitions()) - .setMaxMessageBytes(TOPIC_TO_CHECK_SETTINGS.getMaxMessageBytes()) + .setTopicName(SETTINGS_TOPIC.getName()) + .setNumberOfPartitions(SETTINGS_TOPIC.getNumberOfPartitions()) + .setMaxMessageBytes(SETTINGS_TOPIC.getMaxMessageBytes()) .clickSaveTopicBtn(); topicDetails .waitUntilScreenReady(); - TOPIC_LIST.add(TOPIC_TO_CHECK_SETTINGS); + TOPIC_LIST.add(SETTINGS_TOPIC); topicDetails .openDetailsTab(TopicDetails.TopicMenu.SETTINGS); topicSettingsTab .waitUntilScreenReady(); SoftAssert softly = new SoftAssert(); softly.assertEquals(topicSettingsTab.getValueByKey("retention.bytes"), - TOPIC_TO_CHECK_SETTINGS.getMaxSizeOnDisk().getOptionValue(), "getValueOfKey(retention.bytes)"); + SETTINGS_TOPIC.getMaxSizeOnDisk().getOptionValue(), "getValueOfKey(retention.bytes)"); softly.assertEquals(topicSettingsTab.getValueByKey("max.message.bytes"), - TOPIC_TO_CHECK_SETTINGS.getMaxMessageBytes(), "getValueOfKey(max.message.bytes)"); + SETTINGS_TOPIC.getMaxMessageBytes(), "getValueOfKey(max.message.bytes)"); softly.assertAll(); - TOPIC_TO_CHECK_SETTINGS + SETTINGS_TOPIC .setMaxSizeOnDisk(MaxSizeOnDisk.SIZE_1_GB) .setMaxMessageBytes("1000056"); topicDetails @@ -448,8 +423,8 @@ public void checkRetentionBytesAccordingToMaxSizeOnDisk() { .clickEditSettingsMenu(); topicCreateEditForm .waitUntilScreenReady() - .setMaxSizeOnDiskInGB(TOPIC_TO_CHECK_SETTINGS.getMaxSizeOnDisk()) - .setMaxMessageBytes(TOPIC_TO_CHECK_SETTINGS.getMaxMessageBytes()) + .setMaxSizeOnDiskInGB(SETTINGS_TOPIC.getMaxSizeOnDisk()) + .setMaxMessageBytes(SETTINGS_TOPIC.getMaxMessageBytes()) .clickSaveTopicBtn(); topicDetails .waitUntilScreenReady() @@ -457,29 +432,26 @@ public void checkRetentionBytesAccordingToMaxSizeOnDisk() { topicSettingsTab .waitUntilScreenReady(); softly.assertEquals(topicSettingsTab.getValueByKey("retention.bytes"), - TOPIC_TO_CHECK_SETTINGS.getMaxSizeOnDisk().getOptionValue(), "getValueOfKey(retention.bytes)"); + SETTINGS_TOPIC.getMaxSizeOnDisk().getOptionValue(), "getMaxSizeOnDisk()"); softly.assertEquals(topicSettingsTab.getValueByKey("max.message.bytes"), - TOPIC_TO_CHECK_SETTINGS.getMaxMessageBytes(), "getValueOfKey(max.message.bytes)"); + SETTINGS_TOPIC.getMaxMessageBytes(), "getMaxMessageBytes()"); softly.assertAll(); } - @QaseId(247) @Test(priority = 19) - public void recreateTopicFromTopicProfile() { - Topic topicToRecreate = new Topic() - .setName("topic-to-recreate-" + randomAlphabetic(5)) - .setNumberOfPartitions(1); + public void recreateTopicFromTopicProfileCheck() { + Topic recreateTopic = Topic.createTopic(); navigateToTopics(); topicsList .clickAddTopicBtn(); topicCreateEditForm .waitUntilScreenReady() - .setTopicName(topicToRecreate.getName()) - .setNumberOfPartitions(topicToRecreate.getNumberOfPartitions()) + .setTopicName(recreateTopic.getName()) + .setNumberOfPartitions(recreateTopic.getNumberOfPartitions()) .clickSaveTopicBtn(); topicDetails .waitUntilScreenReady(); - TOPIC_LIST.add(topicToRecreate); + TOPIC_LIST.add(recreateTopic); topicDetails .openDotMenu() .clickRecreateTopicMenu(); @@ -487,16 +459,13 @@ public void recreateTopicFromTopicProfile() { topicDetails .clickConfirmBtnMdl(); Assert.assertTrue(topicDetails.isAlertWithMessageVisible(SUCCESS, - String.format("Topic %s successfully recreated!", topicToRecreate.getName())), + String.format("Topic %s successfully recreated!", recreateTopic.getName())), "isAlertWithMessageVisible()"); } - @QaseId(8) @Test(priority = 20) - public void checkCopyTopicPossibility() { - Topic topicToCopy = new Topic() - .setName("topic-to-copy-" + randomAlphabetic(5)) - .setNumberOfPartitions(1); + public void copyTopicPossibilityCheck() { + Topic copyTopic = Topic.createTopic(); navigateToTopics(); topicsList .getAnyNonInternalTopic() @@ -506,13 +475,14 @@ public void checkCopyTopicPossibility() { .waitUntilScreenReady(); Assert.assertFalse(topicCreateEditForm.isCreateTopicButtonEnabled(), "isCreateTopicButtonEnabled()"); topicCreateEditForm - .setTopicName(topicToCopy.getName()) - .setNumberOfPartitions(topicToCopy.getNumberOfPartitions()) + .setTopicName(copyTopic.getName()) + .setNumberOfPartitions(copyTopic.getNumberOfPartitions()) .clickSaveTopicBtn(); topicDetails .waitUntilScreenReady(); - TOPIC_LIST.add(topicToCopy); - Assert.assertTrue(topicDetails.isTopicHeaderVisible(topicToCopy.getName()), "isTopicHeaderVisible()"); + TOPIC_LIST.add(copyTopic); + Assert.assertTrue(topicDetails.isTopicHeaderVisible(copyTopic.getName()), + String.format("isTopicHeaderVisible()[%s]", copyTopic.getName())); } @AfterClass(alwaysRun = true) diff --git a/e2e-tests/src/test/resources/manual.xml b/e2e-tests/src/test/resources/manual.xml deleted file mode 100644 index f39b81c0a..000000000 --- a/e2e-tests/src/test/resources/manual.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/e2e-tests/src/test/resources/qase.xml b/e2e-tests/src/test/resources/qase.xml deleted file mode 100644 index b30184f46..000000000 --- a/e2e-tests/src/test/resources/qase.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/e2e-tests/src/test/resources/regression.xml b/e2e-tests/src/test/resources/regression.xml index 443744224..65977da9f 100644 --- a/e2e-tests/src/test/resources/regression.xml +++ b/e2e-tests/src/test/resources/regression.xml @@ -1,10 +1,9 @@ - + - diff --git a/e2e-tests/src/test/resources/sanity.xml b/e2e-tests/src/test/resources/sanity.xml index 5b219a2fa..c4c4ac5a2 100644 --- a/e2e-tests/src/test/resources/sanity.xml +++ b/e2e-tests/src/test/resources/sanity.xml @@ -1,6 +1,6 @@ - + diff --git a/e2e-tests/src/test/resources/smoke.xml b/e2e-tests/src/test/resources/smoke.xml index e2a9d9b70..0916aa18d 100644 --- a/e2e-tests/src/test/resources/smoke.xml +++ b/e2e-tests/src/test/resources/smoke.xml @@ -1,6 +1,6 @@ - + diff --git a/frontend/package.json b/frontend/package.json index 26be7c951..61cb4e7be 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,19 +8,13 @@ "@hookform/error-message": "^2.0.0", "@hookform/resolvers": "^2.7.1", "@microsoft/fetch-event-source": "^2.0.1", - "@reduxjs/toolkit": "^1.8.3", "@szhsin/react-menu": "^3.5.3", "@tanstack/react-query": "^4.0.5", "@tanstack/react-table": "^8.5.10", - "@testing-library/react": "^14.0.0", - "@types/testing-library__jest-dom": "^5.14.5", "ace-builds": "^1.7.1", "ajv": "^8.6.3", "ajv-formats": "^2.1.1", "classnames": "^2.2.6", - "fetch-mock": "^9.11.0", - "jest": "^29.4.3", - "jest-watch-typeahead": "^2.2.2", "json-schema-faker": "^0.5.6", "jsonpath-plus": "^7.2.0", "lodash": "^4.17.21", @@ -35,17 +29,13 @@ "react-hot-toast": "^2.4.0", "react-is": "^18.2.0", "react-multi-select-component": "^4.3.3", - "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", - "redux": "^4.2.0", "sass": "^1.52.3", "styled-components": "^5.3.1", "use-debounce": "^9.0.3", - "vite": "^4.5.2", - "vite-tsconfig-paths": "^4.0.2", - "whatwg-fetch": "^3.6.2", "yup": "^1.0.0", - "zustand": "^4.1.1" + "zustand": "^4.1.1", + "@tanstack/react-query-devtools": "4.36.1" }, "scripts": { "start": "vite", @@ -82,6 +72,8 @@ "@types/styled-components": "^5.1.13", "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", + "vite": "^4.5.2", + "vite-tsconfig-paths": "^4.0.2", "@vitejs/plugin-react-swc": "^3.0.0", "dotenv": "^16.0.1", "eslint": "^8.3.0", @@ -96,6 +88,10 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.5.0", + "@testing-library/react": "^14.0.0", + "@types/testing-library__jest-dom": "^5.14.5", + "jest": "^29.4.3", + "jest-watch-typeahead": "^2.2.2", "jest-environment-jsdom": "^29.4.3", "jest-sonar-reporter": "^2.0.0", "jest-styled-components": "^7.1.1", @@ -105,7 +101,9 @@ "ts-prune": "^0.10.3", "typescript": "^4.7.4", "vite-plugin-checker": "^0.6.4", - "vite-plugin-ejs": "^1.6.4" + "vite-plugin-ejs": "^1.6.4", + "fetch-mock": "^9.11.0", + "whatwg-fetch": "^3.6.2" }, "engines": { "node": "v18.17.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 2a8b0ac28..3bd4445b1 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -28,24 +28,18 @@ dependencies: '@microsoft/fetch-event-source': specifier: ^2.0.1 version: 2.0.1 - '@reduxjs/toolkit': - specifier: ^1.8.3 - version: 1.8.3(react-redux@8.0.2)(react@18.2.0) '@szhsin/react-menu': specifier: ^3.5.3 version: 3.5.3(react-dom@18.1.0)(react@18.2.0) '@tanstack/react-query': specifier: ^4.0.5 version: 4.0.5(react-dom@18.1.0)(react@18.2.0) + '@tanstack/react-query-devtools': + specifier: 4.36.1 + version: 4.36.1(@tanstack/react-query@4.0.5)(react-dom@18.1.0)(react@18.2.0) '@tanstack/react-table': specifier: ^8.5.10 version: 8.5.10(react-dom@18.1.0)(react@18.2.0) - '@testing-library/react': - specifier: ^14.0.0 - version: 14.0.0(react-dom@18.1.0)(react@18.2.0) - '@types/testing-library__jest-dom': - specifier: ^5.14.5 - version: 5.14.5 ace-builds: specifier: ^1.7.1 version: 1.7.1 @@ -58,15 +52,6 @@ dependencies: classnames: specifier: ^2.2.6 version: 2.3.1 - fetch-mock: - specifier: ^9.11.0 - version: 9.11.0 - jest: - specifier: ^29.4.3 - version: 29.6.4(@types/node@20.11.17)(ts-node@10.9.1) - jest-watch-typeahead: - specifier: ^2.2.2 - version: 2.2.2(jest@29.6.4) json-schema-faker: specifier: ^0.5.6 version: 0.5.6 @@ -109,15 +94,9 @@ dependencies: react-multi-select-component: specifier: ^4.3.3 version: 4.3.3(react-dom@18.1.0)(react@18.2.0) - react-redux: - specifier: ^8.0.2 - version: 8.0.2(@types/react-dom@18.0.5)(@types/react@18.2.21)(react-dom@18.1.0)(react@18.2.0)(redux@4.2.0) react-router-dom: specifier: ^6.3.0 version: 6.15.0(react-dom@18.1.0)(react@18.2.0) - redux: - specifier: ^4.2.0 - version: 4.2.0 sass: specifier: ^1.52.3 version: 1.66.1 @@ -127,15 +106,6 @@ dependencies: use-debounce: specifier: ^9.0.3 version: 9.0.4(react@18.2.0) - vite: - specifier: ^4.5.2 - version: 4.5.2(@types/node@20.11.17)(sass@1.66.1) - vite-tsconfig-paths: - specifier: ^4.0.2 - version: 4.3.1(typescript@4.7.4)(vite@4.5.2) - whatwg-fetch: - specifier: ^3.6.2 - version: 3.6.2 yup: specifier: ^1.0.0 version: 1.3.3 @@ -162,6 +132,9 @@ devDependencies: '@testing-library/jest-dom': specifier: ^5.16.5 version: 5.16.5 + '@testing-library/react': + specifier: ^14.0.0 + version: 14.0.0(react-dom@18.1.0)(react@18.2.0) '@testing-library/user-event': specifier: ^14.4.3 version: 14.4.3(@testing-library/dom@9.3.1) @@ -192,6 +165,9 @@ devDependencies: '@types/styled-components': specifier: ^5.1.13 version: 5.1.18 + '@types/testing-library__jest-dom': + specifier: ^5.14.5 + version: 5.14.5 '@typescript-eslint/eslint-plugin': specifier: ^5.29.0 version: 5.29.0(@typescript-eslint/parser@5.62.0)(eslint@8.48.0)(typescript@4.7.4) @@ -200,7 +176,7 @@ devDependencies: version: 5.62.0(eslint@8.48.0)(typescript@4.7.4) '@vitejs/plugin-react-swc': specifier: ^3.0.0 - version: 3.0.0(vite@4.5.2) + version: 3.0.0(vite@4.5.3) dotenv: specifier: ^16.0.1 version: 16.4.2 @@ -240,6 +216,12 @@ devDependencies: eslint-plugin-react-hooks: specifier: ^4.5.0 version: 4.6.0(eslint@8.48.0) + fetch-mock: + specifier: ^9.11.0 + version: 9.11.0 + jest: + specifier: ^29.4.3 + version: 29.6.4(@types/node@20.11.17)(ts-node@10.9.1) jest-environment-jsdom: specifier: ^29.4.3 version: 29.6.4 @@ -249,6 +231,9 @@ devDependencies: jest-styled-components: specifier: ^7.1.1 version: 7.1.1(styled-components@5.3.1) + jest-watch-typeahead: + specifier: ^2.2.2 + version: 2.2.2(jest@29.6.4) prettier: specifier: ^2.8.4 version: 2.8.4 @@ -264,12 +249,21 @@ devDependencies: typescript: specifier: ^4.7.4 version: 4.7.4 + vite: + specifier: ^4.5.2 + version: 4.5.3(@types/node@20.11.17)(sass@1.66.1) vite-plugin-checker: specifier: ^0.6.4 - version: 0.6.4(eslint@8.48.0)(typescript@4.7.4)(vite@4.5.2) + version: 0.6.4(eslint@8.48.0)(typescript@4.7.4)(vite@4.5.3) vite-plugin-ejs: specifier: ^1.6.4 version: 1.6.4 + vite-tsconfig-paths: + specifier: ^4.0.2 + version: 4.3.1(typescript@4.7.4)(vite@4.5.3) + whatwg-fetch: + specifier: ^3.6.2 + version: 3.6.2 packages: @@ -288,13 +282,14 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.19 - dev: false + dev: true /@babel/code-frame@7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 + dev: true /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} @@ -306,7 +301,7 @@ packages: /@babel/compat-data@7.18.8: resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} engines: {node: '>=6.9.0'} - dev: false + dev: true /@babel/core@7.18.2: resolution: {integrity: sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==} @@ -329,7 +324,7 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: false + dev: true /@babel/core@7.18.9: resolution: {integrity: sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==} @@ -352,7 +347,7 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: false + dev: true /@babel/generator@7.18.9: resolution: {integrity: sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==} @@ -361,7 +356,7 @@ packages: '@babel/types': 7.18.9 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 - dev: false + dev: true /@babel/generator@7.23.6: resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} @@ -389,7 +384,7 @@ packages: '@babel/helper-validator-option': 7.18.6 browserslist: 4.20.4 semver: 6.3.1 - dev: false + dev: true /@babel/helper-compilation-targets@7.18.9(@babel/core@7.18.9): resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} @@ -402,12 +397,12 @@ packages: '@babel/helper-validator-option': 7.18.6 browserslist: 4.20.4 semver: 6.3.1 - dev: false + dev: true /@babel/helper-environment-visitor@7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} - dev: false + dev: true /@babel/helper-environment-visitor@7.22.20: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} @@ -452,26 +447,26 @@ packages: '@babel/types': 7.18.9 transitivePeerDependencies: - supports-color - dev: false + dev: true /@babel/helper-plugin-utils@7.18.6: resolution: {integrity: sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==} engines: {node: '>=6.9.0'} - dev: false + dev: true /@babel/helper-simple-access@7.18.6: resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.9 - dev: false + dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.9 - dev: false + dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} @@ -494,7 +489,7 @@ packages: /@babel/helper-validator-option@7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} engines: {node: '>=6.9.0'} - dev: false + dev: true /@babel/helpers@7.18.9: resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==} @@ -505,7 +500,7 @@ packages: '@babel/types': 7.18.9 transitivePeerDependencies: - supports-color - dev: false + dev: true /@babel/highlight@7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} @@ -514,6 +509,7 @@ packages: '@babel/helper-validator-identifier': 7.18.6 chalk: 2.4.2 js-tokens: 4.0.0 + dev: true /@babel/highlight@7.23.4: resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} @@ -529,7 +525,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.18.9 - dev: false + dev: true /@babel/parser@7.23.9: resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} @@ -545,7 +541,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -554,7 +550,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.18.9): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -563,7 +559,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.18.9): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} @@ -572,7 +568,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -581,7 +577,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-jsx@7.18.6(@babel/core@7.18.9): resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} @@ -591,7 +587,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.18.9): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -600,7 +596,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -609,7 +605,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.18.9): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -618,7 +614,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -627,7 +623,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -636,7 +632,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.18.9): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -645,7 +641,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.18.9): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} @@ -655,7 +651,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/plugin-syntax-typescript@7.17.12(@babel/core@7.18.9): resolution: {integrity: sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==} @@ -665,7 +661,7 @@ packages: dependencies: '@babel/core': 7.18.9 '@babel/helper-plugin-utils': 7.18.6 - dev: false + dev: true /@babel/runtime@7.17.9: resolution: {integrity: sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==} @@ -678,6 +674,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 + dev: true /@babel/template@7.18.6: resolution: {integrity: sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==} @@ -686,7 +683,7 @@ packages: '@babel/code-frame': 7.23.5 '@babel/parser': 7.18.9 '@babel/types': 7.18.9 - dev: false + dev: true /@babel/template@7.23.9: resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==} @@ -730,13 +727,14 @@ packages: /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: false + dev: true /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 + dev: true /@emotion/is-prop-valid@0.8.8: resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} @@ -758,6 +756,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm@0.18.20: @@ -766,6 +765,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-x64@0.18.20: @@ -774,6 +774,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/darwin-arm64@0.18.20: @@ -782,6 +783,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-x64@0.18.20: @@ -790,6 +792,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-arm64@0.18.20: @@ -798,6 +801,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-x64@0.18.20: @@ -806,6 +810,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm64@0.18.20: @@ -814,6 +819,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm@0.18.20: @@ -822,6 +828,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ia32@0.18.20: @@ -830,6 +837,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64@0.18.20: @@ -838,6 +846,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-mips64el@0.18.20: @@ -846,6 +855,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ppc64@0.18.20: @@ -854,6 +864,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-riscv64@0.18.20: @@ -862,6 +873,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.18.20: @@ -870,6 +882,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-x64@0.18.20: @@ -878,6 +891,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/netbsd-x64@0.18.20: @@ -886,6 +900,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /@esbuild/openbsd-x64@0.18.20: @@ -894,6 +909,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /@esbuild/sunos-x64@0.18.20: @@ -902,6 +918,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /@esbuild/win32-arm64@0.18.20: @@ -910,6 +927,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-ia32@0.18.20: @@ -918,6 +936,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-x64@0.18.20: @@ -926,6 +945,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): @@ -1050,12 +1070,12 @@ packages: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 - dev: false + dev: true /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - dev: false + dev: true /@jest/console@29.6.4: resolution: {integrity: sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==} @@ -1067,7 +1087,7 @@ packages: jest-message-util: 29.6.3 jest-util: 29.6.3 slash: 3.0.0 - dev: false + dev: true /@jest/core@29.6.4(ts-node@10.9.1): resolution: {integrity: sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==} @@ -1110,7 +1130,7 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: false + dev: true /@jest/create-cache-key-function@29.7.0: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} @@ -1127,12 +1147,14 @@ packages: '@jest/types': 29.6.3 '@types/node': 16.11.7 jest-mock: 29.6.3 + dev: true /@jest/expect-utils@29.6.4: resolution: {integrity: sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-get-type: 29.6.3 + dev: true /@jest/expect@29.6.4: resolution: {integrity: sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==} @@ -1142,7 +1164,7 @@ packages: jest-snapshot: 29.6.4 transitivePeerDependencies: - supports-color - dev: false + dev: true /@jest/fake-timers@29.6.4: resolution: {integrity: sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==} @@ -1154,6 +1176,7 @@ packages: jest-message-util: 29.6.3 jest-mock: 29.6.3 jest-util: 29.6.3 + dev: true /@jest/globals@29.6.4: resolution: {integrity: sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==} @@ -1165,7 +1188,7 @@ packages: jest-mock: 29.6.3 transitivePeerDependencies: - supports-color - dev: false + dev: true /@jest/reporters@29.6.4: resolution: {integrity: sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==} @@ -1202,13 +1225,14 @@ packages: v8-to-istanbul: 9.0.1 transitivePeerDependencies: - supports-color - dev: false + dev: true /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.27.8 + dev: true /@jest/source-map@29.6.3: resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} @@ -1217,7 +1241,7 @@ packages: '@jridgewell/trace-mapping': 0.3.19 callsites: 3.1.0 graceful-fs: 4.2.10 - dev: false + dev: true /@jest/test-result@29.6.4: resolution: {integrity: sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==} @@ -1227,7 +1251,7 @@ packages: '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.3 collect-v8-coverage: 1.0.1 - dev: false + dev: true /@jest/test-sequencer@29.6.4: resolution: {integrity: sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==} @@ -1237,7 +1261,7 @@ packages: graceful-fs: 4.2.10 jest-haste-map: 29.6.4 slash: 3.0.0 - dev: false + dev: true /@jest/transform@29.6.4: resolution: {integrity: sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==} @@ -1260,7 +1284,7 @@ packages: write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - dev: false + dev: true /@jest/types@29.6.3: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} @@ -1272,6 +1296,7 @@ packages: '@types/node': 16.11.7 '@types/yargs': 17.0.10 chalk: 4.1.2 + dev: true /@jridgewell/gen-mapping@0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} @@ -1279,7 +1304,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - dev: false + dev: true /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} @@ -1311,6 +1336,7 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + dev: true /@lukeed/csprng@1.1.0: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} @@ -1459,25 +1485,6 @@ packages: /@popperjs/core@2.9.2: resolution: {integrity: sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==} - /@reduxjs/toolkit@1.8.3(react-redux@8.0.2)(react@18.2.0): - resolution: {integrity: sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.0.2 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - dependencies: - immer: 9.0.12 - react: 18.2.0 - react-redux: 8.0.2(@types/react-dom@18.0.5)(@types/react@18.2.21)(react-dom@18.1.0)(react@18.2.0)(redux@4.2.0) - redux: 4.2.0 - redux-thunk: 2.4.1(redux@4.2.0) - reselect: 4.1.5 - dev: false - /@remix-run/router@1.8.0: resolution: {integrity: sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==} engines: {node: '>=14.0.0'} @@ -1485,16 +1492,19 @@ packages: /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true /@sinonjs/commons@2.0.0: resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} dependencies: type-detect: 4.0.8 + dev: true /@sinonjs/fake-timers@10.0.2: resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==} dependencies: '@sinonjs/commons': 2.0.0 + dev: true /@swc/core-darwin-arm64@1.3.38: resolution: {integrity: sha512-4ZTJJ/cR0EsXW5UxFCifZoGfzQ07a8s4ayt1nLvLQ5QoB1GTAf9zsACpvWG8e7cmCR0L76R5xt8uJuyr+noIXA==} @@ -1502,6 +1512,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@swc/core-darwin-x64@1.3.38: @@ -1510,6 +1521,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm-gnueabihf@1.3.38: @@ -1518,6 +1530,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm64-gnu@1.3.38: @@ -1526,6 +1539,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm64-musl@1.3.38: @@ -1534,6 +1548,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-x64-gnu@1.3.38: @@ -1542,6 +1557,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-x64-musl@1.3.38: @@ -1550,6 +1566,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-win32-arm64-msvc@1.3.38: @@ -1558,6 +1575,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core-win32-ia32-msvc@1.3.38: @@ -1566,6 +1584,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core-win32-x64-msvc@1.3.38: @@ -1574,6 +1593,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core@1.3.38: @@ -1591,6 +1611,7 @@ packages: '@swc/core-win32-arm64-msvc': 1.3.38 '@swc/core-win32-ia32-msvc': 1.3.38 '@swc/core-win32-x64-msvc': 1.3.38 + dev: true /@swc/counter@0.1.3: resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1620,10 +1641,32 @@ packages: react-transition-state: 1.1.5(react-dom@18.1.0)(react@18.2.0) dev: false + /@tanstack/match-sorter-utils@8.15.1: + resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==} + engines: {node: '>=12'} + dependencies: + remove-accents: 0.5.0 + dev: false + /@tanstack/query-core@4.0.5: resolution: {integrity: sha512-QOJ2gLbwlf8p0487pMey6vv8EF5X2ib1zINayaD7mb9/LibUtXmZ12uJgTqcnjgNY/4tWZn5qJnEk2ePG5AVGA==} dev: false + /@tanstack/react-query-devtools@4.36.1(@tanstack/react-query@4.0.5)(react-dom@18.1.0)(react@18.2.0): + resolution: {integrity: sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw==} + peerDependencies: + '@tanstack/react-query': ^4.36.1 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/match-sorter-utils': 8.15.1 + '@tanstack/react-query': 4.0.5(react-dom@18.1.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.1.0(react@18.2.0) + superjson: 1.13.3 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + /@tanstack/react-query@4.0.5(react-dom@18.1.0)(react@18.2.0): resolution: {integrity: sha512-tIggVlhoFevVpY/LkZroPmrERFHN8tw4aZLtgwSArzHmMJ03WQcaNvbbHy6GERidXtaMdUz+IeQryrE7cO7WPQ==} peerDependencies: @@ -1686,6 +1729,7 @@ packages: dom-accessibility-api: 0.5.10 lz-string: 1.5.0 pretty-format: 27.5.1 + dev: true /@testing-library/jest-dom@5.16.5: resolution: {integrity: sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==} @@ -1714,7 +1758,7 @@ packages: '@types/react-dom': 18.0.5 react: 18.2.0 react-dom: 18.1.0(react@18.2.0) - dev: false + dev: true /@testing-library/user-event@14.4.3(@testing-library/dom@9.3.1): resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} @@ -1741,15 +1785,19 @@ packages: /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true /@types/aria-query@4.2.2: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} @@ -1757,6 +1805,7 @@ packages: /@types/aria-query@5.0.1: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} + dev: true /@types/babel__core@7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} @@ -1766,26 +1815,26 @@ packages: '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.17.1 - dev: false + dev: true /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: '@babel/types': 7.18.9 - dev: false + dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.18.9 '@babel/types': 7.18.9 - dev: false + dev: true /@types/babel__traverse@7.17.1: resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: '@babel/types': 7.18.9 - dev: false + dev: true /@types/eventsource@1.1.11: resolution: {integrity: sha512-L7wLDZlWm5mROzv87W0ofIYeQP5K2UhoFnnUyEWLKM6UBb0ZNRgAqp98qE5DkgfBXdWfc2kYmw9KZm4NLjRbsw==} @@ -1795,7 +1844,7 @@ packages: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: '@types/node': 16.11.7 - dev: false + dev: true /@types/history@4.7.11: resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} @@ -1806,25 +1855,30 @@ packages: dependencies: '@types/react': 18.2.21 hoist-non-react-statics: 3.3.2 + dev: true /@types/istanbul-lib-coverage@2.0.3: resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==} + dev: true /@types/istanbul-lib-report@3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.3 + dev: true /@types/istanbul-reports@3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 + dev: true /@types/jest@29.0.1: resolution: {integrity: sha512-CAZrjLRZs4xEdIrfrdV74xK1Vo/BKQZwUcjJv3gp6gMeV3BsVxMnXTcgtYOKyphT4DPPo7jxVEVhuwJTQn3oPQ==} dependencies: expect: 29.6.4 pretty-format: 29.6.3 + dev: true /@types/jsdom@20.0.0: resolution: {integrity: sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA==} @@ -1852,11 +1906,13 @@ packages: /@types/node@16.11.7: resolution: {integrity: sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==} + dev: true /@types/node@20.11.17: resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==} dependencies: undici-types: 5.26.5 + dev: true /@types/parse-json@4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} @@ -1880,6 +1936,7 @@ packages: resolution: {integrity: sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==} dependencies: '@types/react': 18.2.21 + dev: true /@types/react-router-dom@5.3.3: resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} @@ -1908,6 +1965,7 @@ packages: /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true /@types/styled-components@5.1.18: resolution: {integrity: sha512-xPTYmWP7Mxk5TAD3pYsqjwA9G5fAI8e/S51QUJEl7EQD1siKCdiYXIWiH2lzoHRl+QqbQCJMcGv3YTF3OmyPdQ==} @@ -1921,6 +1979,7 @@ packages: resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} dependencies: '@types/jest': 29.0.1 + dev: true /@types/tough-cookie@4.0.2: resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} @@ -1932,11 +1991,13 @@ packages: /@types/yargs-parser@20.2.0: resolution: {integrity: sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==} + dev: true /@types/yargs@17.0.10: resolution: {integrity: sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==} dependencies: '@types/yargs-parser': 20.2.0 + dev: true /@typescript-eslint/eslint-plugin@5.29.0(@typescript-eslint/parser@5.62.0)(eslint@8.48.0)(typescript@4.7.4): resolution: {integrity: sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==} @@ -2106,13 +2167,13 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@vitejs/plugin-react-swc@3.0.0(vite@4.5.2): + /@vitejs/plugin-react-swc@3.0.0(vite@4.5.3): resolution: {integrity: sha512-vYlodz/mjYRbxMGbHzDgR8aPR+z8n7K/enWkyBGH096xrL2DIPCuTvQVRYPTXGyy6wO7OFiMxZ3r4nKQD1sH0A==} peerDependencies: vite: '>=4.5.2' dependencies: '@swc/core': 1.3.38 - vite: 4.5.2(@types/node@20.11.17)(sass@1.66.1) + vite: 4.5.3(@types/node@20.11.17)(sass@1.66.1) dev: true /abab@2.0.6: @@ -2146,6 +2207,7 @@ packages: /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} + dev: true /acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} @@ -2163,6 +2225,7 @@ packages: resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} @@ -2207,22 +2270,24 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 + dev: true /ansi-escapes@6.0.0: resolution: {integrity: sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==} engines: {node: '>=14.16'} dependencies: type-fest: 3.6.1 - dev: false + dev: true /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: false + dev: true /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -2235,10 +2300,12 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 + dev: true /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + dev: true /anymatch@3.1.2: resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} @@ -2249,12 +2316,12 @@ packages: /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 - dev: false /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2284,6 +2351,7 @@ packages: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} dependencies: deep-equal: 2.2.2 + dev: true /aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2296,6 +2364,7 @@ packages: dependencies: call-bind: 1.0.2 is-array-buffer: 3.0.2 + dev: true /array-includes@3.1.5: resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} @@ -2413,6 +2482,7 @@ packages: /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} + dev: true /axe-core@4.7.2: resolution: {integrity: sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==} @@ -2451,7 +2521,7 @@ packages: slash: 3.0.0 transitivePeerDependencies: - supports-color - dev: false + dev: true /babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} @@ -2464,7 +2534,7 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - dev: false + dev: true /babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} @@ -2474,7 +2544,7 @@ packages: '@babel/types': 7.18.9 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.17.1 - dev: false + dev: true /babel-plugin-styled-components@1.13.2(styled-components@5.3.1): resolution: {integrity: sha512-Vb1R3d4g+MUfPQPVDMCGjm3cDocJEUTR7Xq7QS95JWWeksN1wdFRYpD2kulDgI3Huuaf1CZd+NK4KQmqUFh5dA==} @@ -2508,7 +2578,7 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.18.9) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.18.9) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.18.9) - dev: false + dev: true /babel-preset-jest@29.6.3(@babel/core@7.18.9): resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} @@ -2519,10 +2589,11 @@ packages: '@babel/core': 7.18.9 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.18.9) - dev: false + dev: true /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2545,6 +2616,7 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + dev: true /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -2572,17 +2644,17 @@ packages: escalade: 3.1.1 node-releases: 2.0.5 picocolors: 1.0.0 - dev: false + dev: true /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 - dev: false + dev: true /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false + dev: true /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -2596,6 +2668,7 @@ packages: dependencies: function-bind: 1.1.2 get-intrinsic: 1.2.1 + dev: true /call-me-maybe@1.0.1: resolution: {integrity: sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==} @@ -2604,23 +2677,24 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + dev: true /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - dev: false + dev: true /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - dev: false + dev: true /camelize@1.0.0: resolution: {integrity: sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==} /caniuse-lite@1.0.30001352: resolution: {integrity: sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==} - dev: false + dev: true /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -2644,21 +2718,22 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + dev: true /chalk@5.2.0: resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false + dev: true /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - dev: false + dev: true /char-regex@2.0.1: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} engines: {node: '>=12.20'} - dev: false + dev: true /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -2680,10 +2755,11 @@ packages: /ci-info@3.3.1: resolution: {integrity: sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==} + dev: true /cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - dev: false + dev: true /classnames@2.3.1: resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==} @@ -2712,6 +2788,7 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -2722,7 +2799,7 @@ packages: /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: false + dev: true /code-block-writer@11.0.1: resolution: {integrity: sha512-0ch9DeCY8v/BWA9n1/Qu1ALG3lpesel4PYL2eNlGLgvGl+J7k74i+dSXSF3wLvF5SYII8/GUT/Ic+fycBR/DUQ==} @@ -2730,7 +2807,7 @@ packages: /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - dev: false + dev: true /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -2742,12 +2819,14 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} @@ -2772,6 +2851,7 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true /concurrently@6.5.1: resolution: {integrity: sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==} @@ -2807,17 +2887,24 @@ packages: resolution: {integrity: sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==} dependencies: safe-buffer: 5.1.2 - dev: false + dev: true /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + dependencies: + is-what: 4.1.16 dev: false /core-js@3.14.0: resolution: {integrity: sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. requiresBuild: true - dev: false + dev: true /cosmiconfig@7.0.1: resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} @@ -2832,6 +2919,7 @@ packages: /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -2840,6 +2928,7 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dev: true /css-color-keywords@1.0.0: resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} @@ -2929,7 +3018,7 @@ packages: peerDependenciesMeta: babel-plugin-macros: optional: true - dev: false + dev: true /deep-equal@2.2.2: resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==} @@ -2952,6 +3041,7 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.1 which-typed-array: 1.1.11 + dev: true /deep-is@0.1.3: resolution: {integrity: sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==} @@ -2960,7 +3050,7 @@ packages: /deepmerge@4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} - dev: false + dev: true /defaults@1.0.3: resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==} @@ -2975,6 +3065,7 @@ packages: dependencies: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + dev: true /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} @@ -2989,7 +3080,7 @@ packages: /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - dev: false + dev: true /diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} @@ -2998,10 +3089,12 @@ packages: /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + dev: true /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -3026,6 +3119,7 @@ packages: /dom-accessibility-api@0.5.10: resolution: {integrity: sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g==} + dev: true /domexception@4.0.0: resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} @@ -3055,20 +3149,21 @@ packages: /electron-to-chromium@1.4.151: resolution: {integrity: sha512-XaG2LpZi9fdiWYOqJh0dJy4SlVywCvpgYXhzOlZTp4JqSKqxn5URqOjbm9OMYB3aInA2GuHQiem1QUOc1yT0Pw==} - dev: false + dev: true /emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} engines: {node: '>=12'} - dev: false + dev: true /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - dev: false + dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -3091,6 +3186,7 @@ packages: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 + dev: true /es-abstract@1.22.1: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} @@ -3149,6 +3245,7 @@ packages: is-string: 1.0.7 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 + dev: true /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} @@ -3202,10 +3299,12 @@ packages: '@esbuild/win32-arm64': 0.18.20 '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 + dev: true /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} + dev: true /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} @@ -3214,6 +3313,7 @@ packages: /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} + dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -3614,12 +3714,12 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: false + dev: true /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - dev: false + dev: true /expect@29.6.4: resolution: {integrity: sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==} @@ -3630,6 +3730,7 @@ packages: jest-matcher-utils: 29.6.4 jest-message-util: 29.6.3 jest-util: 29.6.3 + dev: true /external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} @@ -3660,6 +3761,7 @@ packages: /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} @@ -3679,7 +3781,7 @@ packages: resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==} dependencies: bser: 2.1.1 - dev: false + dev: true /fetch-mock@9.11.0: resolution: {integrity: sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==} @@ -3702,7 +3804,7 @@ packages: whatwg-url: 6.5.0 transitivePeerDependencies: - supports-color - dev: false + dev: true /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} @@ -3736,7 +3838,7 @@ packages: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: false + dev: true /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -3772,6 +3874,7 @@ packages: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 + dev: true /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} @@ -3806,6 +3909,7 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} @@ -3816,6 +3920,7 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true /function.prototype.name@1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} @@ -3833,15 +3938,17 @@ packages: /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - dev: false + dev: true /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + dev: true /get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} @@ -3850,16 +3957,17 @@ packages: has: 1.0.3 has-proto: 1.0.1 has-symbols: 1.0.3 + dev: true /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - dev: false + dev: true /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: false + dev: true /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} @@ -3890,7 +3998,7 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - dev: false + dev: true /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -3912,6 +4020,7 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: true /glob@9.2.1: resolution: {integrity: sha512-Pxxgq3W0HyA3XUvSXcFhRSs+43Jsx0ddxcFrbjxNGkL2Ak5BAUBxLqI5G6ADDeCHLfzzXFhe0b1yYcctGmytMA==} @@ -3955,7 +4064,7 @@ packages: /globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: false + dev: true /goober@2.1.10(csstype@3.1.2): resolution: {integrity: sha512-7PpuQMH10jaTWm33sQgBQvz45pHR8N4l3Cu3WMGEWmHShAcTuuP7I+5/DwKo39fwti5A80WAjvqgz6SSlgWmGA==} @@ -3969,9 +4078,11 @@ packages: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: get-intrinsic: 1.2.1 + dev: true /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -3988,37 +4099,44 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: get-intrinsic: 1.2.1 + dev: true /has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} + dev: true /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + dev: true /has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 + dev: true /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.2 + dev: true /hasown@2.0.0: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 + dev: true /hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -4034,7 +4152,7 @@ packages: /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: false + dev: true /http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} @@ -4060,7 +4178,7 @@ packages: /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: false + dev: true /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -4085,10 +4203,6 @@ packages: engines: {node: '>= 4'} dev: true - /immer@9.0.12: - resolution: {integrity: sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==} - dev: false - /immutable@4.0.0: resolution: {integrity: sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==} @@ -4107,11 +4221,12 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - dev: false + dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + dev: true /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} @@ -4123,9 +4238,11 @@ packages: dependencies: once: 1.4.0 wrappy: 1.0.2 + dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true /inquirer@8.2.5: resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} @@ -4155,6 +4272,7 @@ packages: get-intrinsic: 1.2.1 has: 1.0.3 side-channel: 1.0.4 + dev: true /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} @@ -4162,6 +4280,7 @@ packages: dependencies: call-bind: 1.0.2 has-tostringtag: 1.0.0 + dev: true /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} @@ -4169,12 +4288,15 @@ packages: call-bind: 1.0.2 get-intrinsic: 1.2.1 is-typed-array: 1.1.12 + dev: true /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true /is-bigint@1.0.2: resolution: {integrity: sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==} + dev: true /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} @@ -4187,10 +4309,12 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 + dev: true /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} + dev: true /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} @@ -4202,12 +4326,14 @@ packages: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.0 + dev: true /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 + dev: true /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -4216,11 +4342,12 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + dev: true /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - dev: false + dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4235,6 +4362,7 @@ packages: /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} @@ -4244,6 +4372,7 @@ packages: /is-number-object@1.0.5: resolution: {integrity: sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==} engines: {node: '>= 0.4'} + dev: true /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} @@ -4264,41 +4393,47 @@ packages: dependencies: call-bind: 1.0.2 has-tostringtag: 1.0.0 + dev: true /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: call-bind: 1.0.2 + dev: true /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: false + dev: true /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 + dev: true /is-subset@0.1.1: resolution: {integrity: sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==} - dev: false + dev: true /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 + dev: true /is-typed-array@1.1.12: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} dependencies: which-typed-array: 1.1.11 + dev: true /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} @@ -4307,6 +4442,7 @@ packages: /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} @@ -4319,17 +4455,25 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.1 + dev: true + + /is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + dev: false /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} - dev: false + dev: true /istanbul-lib-instrument@5.2.0: resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} @@ -4342,7 +4486,7 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: false + dev: true /istanbul-lib-instrument@6.0.0: resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==} @@ -4355,7 +4499,7 @@ packages: semver: 7.5.4 transitivePeerDependencies: - supports-color - dev: false + dev: true /istanbul-lib-report@3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} @@ -4364,7 +4508,7 @@ packages: istanbul-lib-coverage: 3.2.0 make-dir: 3.1.0 supports-color: 7.2.0 - dev: false + dev: true /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} @@ -4375,7 +4519,7 @@ packages: source-map: 0.6.1 transitivePeerDependencies: - supports-color - dev: false + dev: true /istanbul-reports@3.1.4: resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==} @@ -4383,7 +4527,7 @@ packages: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 - dev: false + dev: true /iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} @@ -4408,7 +4552,7 @@ packages: execa: 5.1.1 jest-util: 29.6.3 p-limit: 3.1.0 - dev: false + dev: true /jest-circus@29.6.4: resolution: {integrity: sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==} @@ -4437,7 +4581,7 @@ packages: transitivePeerDependencies: - babel-plugin-macros - supports-color - dev: false + dev: true /jest-cli@29.6.4(@types/node@20.11.17)(ts-node@10.9.1): resolution: {integrity: sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==} @@ -4466,7 +4610,7 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: false + dev: true /jest-config@29.6.4(@types/node@16.11.7)(ts-node@10.9.1): resolution: {integrity: sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==} @@ -4507,7 +4651,7 @@ packages: transitivePeerDependencies: - babel-plugin-macros - supports-color - dev: false + dev: true /jest-config@29.6.4(@types/node@20.11.17)(ts-node@10.9.1): resolution: {integrity: sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==} @@ -4548,7 +4692,7 @@ packages: transitivePeerDependencies: - babel-plugin-macros - supports-color - dev: false + dev: true /jest-diff@29.6.4: resolution: {integrity: sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==} @@ -4558,13 +4702,14 @@ packages: diff-sequences: 29.6.3 jest-get-type: 29.6.3 pretty-format: 29.6.3 + dev: true /jest-docblock@29.6.3: resolution: {integrity: sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: detect-newline: 3.1.0 - dev: false + dev: true /jest-each@29.6.3: resolution: {integrity: sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==} @@ -4575,7 +4720,7 @@ packages: jest-get-type: 29.6.3 jest-util: 29.6.3 pretty-format: 29.6.3 - dev: false + dev: true /jest-environment-jsdom@29.6.4: resolution: {integrity: sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA==} @@ -4610,11 +4755,12 @@ packages: '@types/node': 16.11.7 jest-mock: 29.6.3 jest-util: 29.6.3 - dev: false + dev: true /jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true /jest-haste-map@29.6.4: resolution: {integrity: sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==} @@ -4633,7 +4779,7 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 - dev: false + dev: true /jest-leak-detector@29.6.3: resolution: {integrity: sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==} @@ -4641,7 +4787,7 @@ packages: dependencies: jest-get-type: 29.6.3 pretty-format: 29.6.3 - dev: false + dev: true /jest-matcher-utils@29.6.4: resolution: {integrity: sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==} @@ -4651,6 +4797,7 @@ packages: jest-diff: 29.6.4 jest-get-type: 29.6.3 pretty-format: 29.6.3 + dev: true /jest-message-util@29.6.3: resolution: {integrity: sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==} @@ -4665,6 +4812,7 @@ packages: pretty-format: 29.6.3 slash: 3.0.0 stack-utils: 2.0.5 + dev: true /jest-mock@29.6.3: resolution: {integrity: sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==} @@ -4673,6 +4821,7 @@ packages: '@jest/types': 29.6.3 '@types/node': 16.11.7 jest-util: 29.6.3 + dev: true /jest-pnp-resolver@1.2.2(jest-resolve@29.6.4): resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} @@ -4684,17 +4833,17 @@ packages: optional: true dependencies: jest-resolve: 29.6.4 - dev: false + dev: true /jest-regex-util@29.0.0: resolution: {integrity: sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: false + dev: true /jest-regex-util@29.6.3: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: false + dev: true /jest-resolve-dependencies@29.6.4: resolution: {integrity: sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==} @@ -4704,7 +4853,7 @@ packages: jest-snapshot: 29.6.4 transitivePeerDependencies: - supports-color - dev: false + dev: true /jest-resolve@29.6.4: resolution: {integrity: sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==} @@ -4719,7 +4868,7 @@ packages: resolve: 1.22.4 resolve.exports: 2.0.1 slash: 3.0.0 - dev: false + dev: true /jest-runner@29.6.4: resolution: {integrity: sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==} @@ -4748,7 +4897,7 @@ packages: source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - dev: false + dev: true /jest-runtime@29.6.4: resolution: {integrity: sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==} @@ -4778,7 +4927,7 @@ packages: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - dev: false + dev: true /jest-snapshot@29.6.4: resolution: {integrity: sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==} @@ -4806,7 +4955,7 @@ packages: semver: 7.5.4 transitivePeerDependencies: - supports-color - dev: false + dev: true /jest-sonar-reporter@2.0.0: resolution: {integrity: sha512-ZervDCgEX5gdUbdtWsjdipLN3bKJwpxbvhkYNXTAYvAckCihobSLr9OT/IuyNIRT1EZMDDwR6DroWtrq+IL64w==} @@ -4835,6 +4984,7 @@ packages: ci-info: 3.3.1 graceful-fs: 4.2.10 picomatch: 2.3.1 + dev: true /jest-validate@29.6.3: resolution: {integrity: sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==} @@ -4846,7 +4996,7 @@ packages: jest-get-type: 29.6.3 leven: 3.1.0 pretty-format: 29.6.3 - dev: false + dev: true /jest-watch-typeahead@2.2.2(jest@29.6.4): resolution: {integrity: sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ==} @@ -4862,7 +5012,7 @@ packages: slash: 5.0.0 string-length: 5.0.1 strip-ansi: 7.0.1 - dev: false + dev: true /jest-watcher@29.0.3: resolution: {integrity: sha512-tQX9lU91A+9tyUQKUMp0Ns8xAcdhC9fo73eqA3LFxP2bSgiF49TNcc+vf3qgGYYK9qRjFpXW9+4RgF/mbxyOOw==} @@ -4876,7 +5026,7 @@ packages: emittery: 0.10.2 jest-util: 29.6.3 string-length: 4.0.2 - dev: false + dev: true /jest-watcher@29.6.4: resolution: {integrity: sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==} @@ -4890,7 +5040,7 @@ packages: emittery: 0.13.1 jest-util: 29.6.3 string-length: 4.0.2 - dev: false + dev: true /jest-worker@29.6.4: resolution: {integrity: sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==} @@ -4900,7 +5050,7 @@ packages: jest-util: 29.6.3 merge-stream: 2.0.0 supports-color: 8.1.1 - dev: false + dev: true /jest@29.6.4(@types/node@20.11.17)(ts-node@10.9.1): resolution: {integrity: sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==} @@ -4921,7 +5071,7 @@ packages: - babel-plugin-macros - supports-color - ts-node - dev: false + dev: true /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4932,7 +5082,6 @@ packages: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: false /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} @@ -4990,6 +5139,7 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true /json-schema-faker@0.5.6: resolution: {integrity: sha512-u/cFC26/GDxh2vPiAC8B8xVvpXAW+QYtG2mijEbKrimCk8IHtiwQBjCE8TwvowdhALWq9IcdIWZ+/8ocXvdL3Q==} @@ -5031,6 +5181,7 @@ packages: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true + dev: true /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} @@ -5070,7 +5221,7 @@ packages: /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - dev: false + dev: true /language-subtag-registry@0.3.21: resolution: {integrity: sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==} @@ -5085,7 +5236,7 @@ packages: /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - dev: false + dev: true /levn@0.3.0: resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} @@ -5105,13 +5256,14 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} dependencies: p-locate: 4.1.0 - dev: false + dev: true /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -5126,7 +5278,6 @@ packages: /lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: false /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -5134,7 +5285,7 @@ packages: /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: false + dev: true /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -5162,6 +5313,7 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 + dev: true /lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} @@ -5171,26 +5323,28 @@ packages: /lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + dev: true /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: semver: 6.3.1 - dev: false + dev: true /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 - dev: false + dev: true /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: false + dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -5203,6 +5357,7 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 + dev: true /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -5219,6 +5374,7 @@ packages: /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + dev: true /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -5229,6 +5385,7 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 + dev: true /minimatch@5.1.2: resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==} @@ -5274,9 +5431,11 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + dev: true /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} @@ -5292,11 +5451,11 @@ packages: /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: false + dev: true /node-releases@2.0.5: resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==} - dev: false + dev: true /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -5307,6 +5466,7 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 + dev: true /nwsapi@2.2.0: resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} @@ -5318,6 +5478,7 @@ packages: /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true /object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} @@ -5325,10 +5486,12 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 + dev: true /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + dev: true /object.assign@4.1.2: resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} @@ -5348,6 +5511,7 @@ packages: define-properties: 1.2.0 has-symbols: 1.0.3 object-keys: 1.1.1 + dev: true /object.entries@1.1.5: resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} @@ -5423,12 +5587,14 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 + dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 + dev: true /ono@4.0.11: resolution: {integrity: sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==} @@ -5485,20 +5651,21 @@ packages: engines: {node: '>=6'} dependencies: p-try: 2.2.0 - dev: false + dev: true /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 + dev: true /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} dependencies: p-limit: 2.3.0 - dev: false + dev: true /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} @@ -5510,7 +5677,7 @@ packages: /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - dev: false + dev: true /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -5527,6 +5694,7 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + dev: true /parse-ms@2.1.0: resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} @@ -5546,17 +5714,21 @@ packages: /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + dev: true /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + dev: true /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true /path-scurry@1.6.1: resolution: {integrity: sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==} @@ -5568,7 +5740,7 @@ packages: /path-to-regexp@2.4.0: resolution: {integrity: sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==} - dev: false + dev: true /path-to-regexp@3.2.0: resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} @@ -5581,6 +5753,7 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -5589,14 +5762,14 @@ packages: /pirates@4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} - dev: false + dev: true /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} dependencies: find-up: 4.1.0 - dev: false + dev: true /postcss-value-parser@4.1.0: resolution: {integrity: sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==} @@ -5608,6 +5781,7 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.0.2 + dev: true /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} @@ -5639,6 +5813,7 @@ packages: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 + dev: true /pretty-format@29.6.3: resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==} @@ -5647,6 +5822,7 @@ packages: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.2.0 + dev: true /pretty-ms@7.0.1: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} @@ -5661,7 +5837,7 @@ packages: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 - dev: false + dev: true /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -5688,13 +5864,13 @@ packages: /pure-rand@6.0.0: resolution: {integrity: sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==} - dev: false + dev: true /querystring@0.2.1: resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} engines: {node: '>=0.4.x'} deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - dev: false + dev: true /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -5785,6 +5961,7 @@ packages: /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} @@ -5835,40 +6012,6 @@ packages: warning: 4.0.3 dev: false - /react-redux@8.0.2(@types/react-dom@18.0.5)(@types/react@18.2.21)(react-dom@18.1.0)(react@18.2.0)(redux@4.2.0): - resolution: {integrity: sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==} - peerDependencies: - '@types/react': ^16.8 || ^17.0 || ^18.0 - '@types/react-dom': ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: '>=0.59' - redux: ^4 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - react-dom: - optional: true - react-native: - optional: true - redux: - optional: true - dependencies: - '@babel/runtime': 7.17.9 - '@types/hoist-non-react-statics': 3.3.1 - '@types/react': 18.2.21 - '@types/react-dom': 18.0.5 - '@types/use-sync-external-store': 0.0.3 - hoist-non-react-statics: 3.3.2 - react: 18.2.0 - react-dom: 18.1.0(react@18.2.0) - react-is: 18.2.0 - redux: 4.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - /react-router-dom@6.15.0(react-dom@18.1.0)(react@18.2.0): resolution: {integrity: sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==} engines: {node: '>=14.0.0'} @@ -5931,20 +6074,6 @@ packages: strip-indent: 3.0.0 dev: true - /redux-thunk@2.4.1(redux@4.2.0): - resolution: {integrity: sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==} - peerDependencies: - redux: ^4 - dependencies: - redux: 4.2.0 - dev: false - - /redux@4.2.0: - resolution: {integrity: sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==} - dependencies: - '@babel/runtime': 7.17.9 - dev: false - /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} dev: true @@ -5954,6 +6083,7 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: true /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} @@ -5962,15 +6092,21 @@ packages: call-bind: 1.0.2 define-properties: 1.2.0 functions-have-names: 1.2.3 + dev: true /regexpp@3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} dev: true + /remove-accents@0.5.0: + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} + dev: false + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -5986,16 +6122,12 @@ packages: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true - /reselect@4.1.5: - resolution: {integrity: sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==} - dev: false - /resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 - dev: false + dev: true /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -6005,7 +6137,7 @@ packages: /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - dev: false + dev: true /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -6014,7 +6146,7 @@ packages: /resolve.exports@2.0.1: resolution: {integrity: sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==} engines: {node: '>=10'} - dev: false + dev: true /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} @@ -6023,6 +6155,7 @@ packages: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + dev: true /resolve@2.0.0-next.3: resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==} @@ -6065,6 +6198,7 @@ packages: hasBin: true optionalDependencies: fsevents: 2.3.2 + dev: true /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} @@ -6102,7 +6236,7 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false + dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -6144,6 +6278,7 @@ packages: /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + dev: true /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} @@ -6151,6 +6286,7 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 + dev: true /shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} @@ -6160,10 +6296,12 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + dev: true /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -6171,22 +6309,25 @@ packages: call-bind: 1.0.2 get-intrinsic: 1.2.1 object-inspect: 1.12.3 + dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: false + dev: true /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + dev: true /slash@5.0.0: resolution: {integrity: sha512-n6KkmvKS0623igEVj3FF0OZs1gYYJ0o0Hj939yc1fyxl2xt+xYpLnzJB6xBSqOfV9ZFLEWodBBN/heZJahuIJQ==} engines: {node: '>=14.16'} - dev: false + dev: true /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} @@ -6197,11 +6338,12 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: false + dev: true /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + dev: true /spawn-command@0.0.2-1: resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==} @@ -6209,19 +6351,20 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: false /stack-utils@2.0.5: resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 + dev: true /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} dependencies: internal-slot: 1.0.5 + dev: true /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -6229,7 +6372,7 @@ packages: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - dev: false + dev: true /string-length@5.0.1: resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} @@ -6237,7 +6380,7 @@ packages: dependencies: char-regex: 2.0.1 strip-ansi: 7.0.1 - dev: false + dev: true /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -6246,6 +6389,7 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: true /string.prototype.matchall@4.0.7: resolution: {integrity: sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==} @@ -6296,13 +6440,14 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + dev: true /strip-ansi@7.0.1: resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: false + dev: true /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -6312,12 +6457,12 @@ packages: /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - dev: false + dev: true /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: false + dev: true /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} @@ -6329,6 +6474,7 @@ packages: /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + dev: true /styled-components@5.3.1(react-dom@18.1.0)(react-is@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JThv2JRzyH0NOIURrk9iskdxMSAAtCfj/b2Sf1WJaCUsloQkblepy1jaCLX/bYE+mhYo3unmwVSI9I5d9ncSiQ==} @@ -6352,6 +6498,13 @@ packages: shallowequal: 1.1.0 supports-color: 5.5.0 + /superjson@1.13.3: + resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==} + engines: {node: '>=10'} + dependencies: + copy-anything: 3.0.5 + dev: false + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -6363,16 +6516,19 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + dev: true /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + dev: true /symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -6394,7 +6550,7 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.0 minimatch: 3.1.2 - dev: false + dev: true /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -6421,7 +6577,7 @@ packages: /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: false + dev: true /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} @@ -6455,7 +6611,7 @@ packages: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} dependencies: punycode: 2.1.1 - dev: false + dev: true /tr46@3.0.0: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} @@ -6511,6 +6667,7 @@ packages: typescript: 4.7.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true /ts-prune@0.10.3: resolution: {integrity: sha512-iS47YTbdIcvN8Nh/1BFyziyUqmjXz7GVzWu02RaZXqb+e/3Qe1B7IQ4860krOeCGUeJmterAlaM2FRH0Ue0hjw==} @@ -6535,7 +6692,7 @@ packages: optional: true dependencies: typescript: 4.7.4 - dev: false + dev: true /tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -6584,6 +6741,7 @@ packages: /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + dev: true /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} @@ -6593,6 +6751,7 @@ packages: /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + dev: true /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} @@ -6602,7 +6761,7 @@ packages: /type-fest@3.6.1: resolution: {integrity: sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA==} engines: {node: '>=14.16'} - dev: false + dev: true /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} @@ -6646,6 +6805,7 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /uid@2.0.1: resolution: {integrity: sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==} @@ -6665,6 +6825,7 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} @@ -6711,6 +6872,7 @@ packages: /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true /v8-to-istanbul@9.0.1: resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} @@ -6719,9 +6881,9 @@ packages: '@jridgewell/trace-mapping': 0.3.19 '@types/istanbul-lib-coverage': 2.0.3 convert-source-map: 1.7.0 - dev: false + dev: true - /vite-plugin-checker@0.6.4(eslint@8.48.0)(typescript@4.7.4)(vite@4.5.2): + /vite-plugin-checker@0.6.4(eslint@8.48.0)(typescript@4.7.4)(vite@4.5.3): resolution: {integrity: sha512-2zKHH5oxr+ye43nReRbC2fny1nyARwhxdm0uNYp/ERy4YvU9iZpNOsueoi/luXw5gnpqRSvjcEPxXbS153O2wA==} engines: {node: '>=14.16'} peerDependencies: @@ -6765,7 +6927,7 @@ packages: strip-ansi: 6.0.1 tiny-invariant: 1.3.3 typescript: 4.7.4 - vite: 4.5.2(@types/node@20.11.17)(sass@1.66.1) + vite: 4.5.3(@types/node@20.11.17)(sass@1.66.1) vscode-languageclient: 7.0.0 vscode-languageserver: 7.0.0 vscode-languageserver-textdocument: 1.0.11 @@ -6778,7 +6940,7 @@ packages: ejs: 3.1.8 dev: true - /vite-tsconfig-paths@4.3.1(typescript@4.7.4)(vite@4.5.2): + /vite-tsconfig-paths@4.3.1(typescript@4.7.4)(vite@4.5.3): resolution: {integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw==} peerDependencies: vite: '>=4.5.2' @@ -6789,14 +6951,14 @@ packages: debug: 4.3.4(supports-color@5.5.0) globrex: 0.1.2 tsconfck: 3.0.2(typescript@4.7.4) - vite: 4.5.2(@types/node@20.11.17)(sass@1.66.1) + vite: 4.5.3(@types/node@20.11.17)(sass@1.66.1) transitivePeerDependencies: - supports-color - typescript - dev: false + dev: true - /vite@4.5.2(@types/node@20.11.17)(sass@1.66.1): - resolution: {integrity: sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==} + /vite@4.5.3(@types/node@20.11.17)(sass@1.66.1): + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -6830,6 +6992,7 @@ packages: sass: 1.66.1 optionalDependencies: fsevents: 2.3.2 + dev: true /vscode-jsonrpc@6.0.0: resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} @@ -6889,7 +7052,7 @@ packages: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 - dev: false + dev: true /warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -6908,7 +7071,7 @@ packages: /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - dev: false + dev: true /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} @@ -6924,7 +7087,7 @@ packages: /whatwg-fetch@3.6.2: resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} - dev: false + dev: true /whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} @@ -6952,7 +7115,7 @@ packages: lodash.sortby: 4.7.0 tr46: 1.0.1 webidl-conversions: 4.0.2 - dev: false + dev: true /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -6962,6 +7125,7 @@ packages: is-number-object: 1.0.5 is-string: 1.0.7 is-symbol: 1.0.4 + dev: true /which-collection@1.0.1: resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} @@ -6970,6 +7134,7 @@ packages: is-set: 2.0.2 is-weakmap: 2.0.1 is-weakset: 2.0.2 + dev: true /which-typed-array@1.1.11: resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} @@ -6980,6 +7145,7 @@ packages: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 + dev: true /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -6987,6 +7153,7 @@ packages: hasBin: true dependencies: isexe: 2.0.0 + dev: true /word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} @@ -7000,9 +7167,11 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true /write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} @@ -7010,7 +7179,7 @@ packages: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - dev: false + dev: true /ws@8.8.0: resolution: {integrity: sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==} @@ -7041,9 +7210,11 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + dev: true /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} @@ -7058,7 +7229,7 @@ packages: /yargs-parser@21.0.1: resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} engines: {node: '>=12'} - dev: false + dev: true /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} @@ -7084,15 +7255,17 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.0.1 - dev: false + dev: true /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} + dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + dev: true /yup@1.3.3: resolution: {integrity: sha512-v8QwZSsHH2K3/G9WSkp6mZKO+hugKT1EmnMqLNUcfu51HU9MDyhlETT/JgtzprnrnQHPWsjc6MUDMBp/l9fNnw==} diff --git a/frontend/src/components/ACLPage/Form/CustomACL/Form.tsx b/frontend/src/components/ACLPage/Form/CustomACL/Form.tsx index a0cf8d0ba..720666972 100644 --- a/frontend/src/components/ACLPage/Form/CustomACL/Form.tsx +++ b/frontend/src/components/ACLPage/Form/CustomACL/Form.tsx @@ -7,10 +7,10 @@ import Input from 'components/common/Input/Input'; import ControlledSelect from 'components/common/Select/ControlledSelect'; import { matchTypeOptions } from 'components/ACLPage/Form/constants'; import useAppParams from 'lib/hooks/useAppParams'; -import { ClusterName } from 'redux/interfaces'; import * as S from 'components/ACLPage/Form/Form.styled'; import ACLFormContext from 'components/ACLPage/Form/AclFormContext'; import { AclDetailedFormProps } from 'components/ACLPage/Form/types'; +import { ClusterName } from 'lib/interfaces/cluster'; import formSchema from './schema'; import { FormValues } from './types'; diff --git a/frontend/src/components/ACLPage/Form/ForConsumers/Form.tsx b/frontend/src/components/ACLPage/Form/ForConsumers/Form.tsx index febd75e08..9db8f7498 100644 --- a/frontend/src/components/ACLPage/Form/ForConsumers/Form.tsx +++ b/frontend/src/components/ACLPage/Form/ForConsumers/Form.tsx @@ -1,7 +1,7 @@ import React, { FC, useContext } from 'react'; import { yupResolver } from '@hookform/resolvers/yup'; import { FormProvider, useForm } from 'react-hook-form'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { useCreateConsumersAcl } from 'lib/hooks/api/acl'; import useAppParams from 'lib/hooks/useAppParams'; import ControlledMultiSelect from 'components/common/MultiSelect/ControlledMultiSelect'; diff --git a/frontend/src/components/ACLPage/Form/ForKafkaStreamApps/Form.tsx b/frontend/src/components/ACLPage/Form/ForKafkaStreamApps/Form.tsx index 7a747de4e..c03e41c89 100644 --- a/frontend/src/components/ACLPage/Form/ForKafkaStreamApps/Form.tsx +++ b/frontend/src/components/ACLPage/Form/ForKafkaStreamApps/Form.tsx @@ -1,7 +1,7 @@ import React, { FC, useContext } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { useCreateStreamAppAcl } from 'lib/hooks/api/acl'; import useAppParams from 'lib/hooks/useAppParams'; import Input from 'components/common/Input/Input'; diff --git a/frontend/src/components/ACLPage/Form/ForProducers/Form.tsx b/frontend/src/components/ACLPage/Form/ForProducers/Form.tsx index cd84aa674..c23816de8 100644 --- a/frontend/src/components/ACLPage/Form/ForProducers/Form.tsx +++ b/frontend/src/components/ACLPage/Form/ForProducers/Form.tsx @@ -3,7 +3,7 @@ import { yupResolver } from '@hookform/resolvers/yup'; import { useCreateProducerAcl } from 'lib/hooks/api/acl'; import { FormProvider, useForm } from 'react-hook-form'; import useAppParams from 'lib/hooks/useAppParams'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import Input from 'components/common/Input/Input'; import ControlledMultiSelect from 'components/common/MultiSelect/ControlledMultiSelect'; import Checkbox from 'components/common/Checkbox/Checkbox'; diff --git a/frontend/src/components/ACLPage/List/List.tsx b/frontend/src/components/ACLPage/List/List.tsx index ddf60d419..c2befd89c 100644 --- a/frontend/src/components/ACLPage/List/List.tsx +++ b/frontend/src/components/ACLPage/List/List.tsx @@ -5,7 +5,7 @@ import Table from 'components/common/NewTable'; import { useConfirm } from 'lib/hooks/useConfirm'; import useAppParams from 'lib/hooks/useAppParams'; import { useAcls, useDeleteAcl } from 'lib/hooks/api/acl'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { KafkaAcl, KafkaAclNamePatternType, diff --git a/frontend/src/components/App.tsx b/frontend/src/components/App.tsx index f3d6d88cd..a2e1b1c72 100644 --- a/frontend/src/components/App.tsx +++ b/frontend/src/components/App.tsx @@ -8,8 +8,6 @@ import { clusterNewConfigPath, } from 'lib/paths'; import PageLoader from 'components/common/PageLoader/PageLoader'; -import Dashboard from 'components/Dashboard/Dashboard'; -import ClusterPage from 'components/ClusterPage/ClusterPage'; import { ThemeProvider } from 'styled-components'; import { theme, darkTheme } from 'theme/theme'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; @@ -17,16 +15,22 @@ import { showServerError } from 'lib/errorHandling'; import { Toaster } from 'react-hot-toast'; import GlobalCSS from 'components/globalCss'; import * as S from 'components/App.styled'; -import ClusterConfigForm from 'widgets/ClusterConfigForm'; import { ThemeModeContext } from 'components/contexts/ThemeModeContext'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import ConfirmationModal from './common/ConfirmationModal/ConfirmationModal'; import { ConfirmContextProvider } from './contexts/ConfirmContext'; import { GlobalSettingsProvider } from './contexts/GlobalSettingsContext'; -import ErrorPage from './ErrorPage/ErrorPage'; import { UserInfoRolesAccessProvider } from './contexts/UserInfoRolesAccessContext'; import PageContainer from './PageContainer/PageContainer'; +const Dashboard = React.lazy(() => import('components/Dashboard/Dashboard')); +const ClusterPage = React.lazy( + () => import('components/ClusterPage/ClusterPage') +); +const ClusterConfigForm = React.lazy(() => import('widgets/ClusterConfigForm')); +const ErrorPage = React.lazy(() => import('components/ErrorPage/ErrorPage')); + const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -93,6 +97,7 @@ const App: React.FC = () => { + ); }; diff --git a/frontend/src/components/Brokers/BrokersList/BrokersList.tsx b/frontend/src/components/Brokers/BrokersList/BrokersList.tsx index bdc1f9c00..3ef59d0b5 100644 --- a/frontend/src/components/Brokers/BrokersList/BrokersList.tsx +++ b/frontend/src/components/Brokers/BrokersList/BrokersList.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { useNavigate } from 'react-router-dom'; import PageHeading from 'components/common/PageHeading/PageHeading'; import useAppParams from 'lib/hooks/useAppParams'; diff --git a/frontend/src/components/ConsumerGroups/Details/ListItem.tsx b/frontend/src/components/ConsumerGroups/Details/ListItem.tsx index 99a3c0bd8..57b7cb133 100644 --- a/frontend/src/components/ConsumerGroups/Details/ListItem.tsx +++ b/frontend/src/components/ConsumerGroups/Details/ListItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { ConsumerGroupTopicPartition } from 'generated-sources'; import { Link } from 'react-router-dom'; -import { ClusterName } from 'redux/interfaces/cluster'; +import { ClusterName } from 'lib/interfaces/cluster'; import { clusterTopicPath } from 'lib/paths'; import MessageToggleIcon from 'components/common/Icons/MessageToggleIcon'; import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper'; diff --git a/frontend/src/components/KsqlDb/Query/renderer/TableRenderer/TableRenderer.tsx b/frontend/src/components/KsqlDb/Query/renderer/TableRenderer/TableRenderer.tsx index 4e1acb38d..c6ae3efd1 100644 --- a/frontend/src/components/KsqlDb/Query/renderer/TableRenderer/TableRenderer.tsx +++ b/frontend/src/components/KsqlDb/Query/renderer/TableRenderer/TableRenderer.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { KsqlTableResponse } from 'generated-sources'; import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell'; -import { nanoid } from '@reduxjs/toolkit'; import { TableTitle } from 'components/common/table/TableTitle/TableTitle.styled'; +import { nanoid } from 'lib/functions/nanoid'; import * as S from './TableRenderer.styled'; @@ -15,17 +15,13 @@ function hasJsonStructure(str: string | Record): boolean { return true; } - if (typeof str === 'string') { - try { - const result = JSON.parse(str); - const type = Object.prototype.toString.call(result); - return type === '[object Object]' || type === '[object Array]'; - } catch (err) { - return false; - } + try { + const result = JSON.parse(str); + const type = Object.prototype.toString.call(result); + return type === '[object Object]' || type === '[object Array]'; + } catch (err) { + return false; } - - return false; } const TableRenderer: React.FC = ({ table }) => { diff --git a/frontend/src/components/Schemas/Details/Details.tsx b/frontend/src/components/Schemas/Details/Details.tsx index 4204e647b..2d0c345d7 100644 --- a/frontend/src/components/Schemas/Details/Details.tsx +++ b/frontend/src/components/Schemas/Details/Details.tsx @@ -10,23 +10,8 @@ import ClusterContext from 'components/contexts/ClusterContext'; import PageLoader from 'components/common/PageLoader/PageLoader'; import PageHeading from 'components/common/PageHeading/PageHeading'; import { Button } from 'components/common/Button/Button'; -import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; -import { - fetchLatestSchema, - fetchSchemaVersions, - getAreSchemaLatestFulfilled, - getAreSchemaVersionsFulfilled, - SCHEMAS_VERSIONS_FETCH_ACTION, - SCHEMA_LATEST_FETCH_ACTION, - selectAllSchemaVersions, - getSchemaLatest, - getAreSchemaLatestRejected, -} from 'redux/reducers/schemas/schemasSlice'; -import { showServerError } from 'lib/errorHandling'; -import { resetLoaderById } from 'redux/reducers/loader/loaderSlice'; import { TableTitle } from 'components/common/table/TableTitle/TableTitle.styled'; import useAppParams from 'lib/hooks/useAppParams'; -import { schemasApiClient } from 'lib/api'; import { Dropdown } from 'components/common/Dropdown'; import Table from 'components/common/NewTable'; import { Action, ResourceType } from 'generated-sources'; @@ -34,35 +19,37 @@ import { ActionButton, ActionDropdownItem, } from 'components/common/ActionComponent'; +import { + useDeleteSchema, + useGetLatestSchema, + useGetSchemasVersions, +} from 'lib/hooks/api/schemas'; import LatestVersionItem from './LatestVersion/LatestVersionItem'; import SchemaVersion from './SchemaVersion/SchemaVersion'; const Details: React.FC = () => { const navigate = useNavigate(); - const dispatch = useAppDispatch(); const { isReadOnly } = React.useContext(ClusterContext); const { clusterName, subject } = useAppParams(); + const { data: versions = [], isFetching: areVersionsFetching } = + useGetSchemasVersions({ + clusterName, + subject, + }); + const { + data: schema, + isFetching: isSchemaFetching, + isError: isErrorLatestSchema, + } = useGetLatestSchema({ + clusterName, + subject, + }); - React.useEffect(() => { - dispatch(fetchLatestSchema({ clusterName, subject })); - return () => { - dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION)); - }; - }, [clusterName, dispatch, subject]); - - React.useEffect(() => { - dispatch(fetchSchemaVersions({ clusterName, subject })); - return () => { - dispatch(resetLoaderById(SCHEMAS_VERSIONS_FETCH_ACTION)); - }; - }, [clusterName, dispatch, subject]); - - const versions = useAppSelector((state) => selectAllSchemaVersions(state)); - const schema = useAppSelector(getSchemaLatest); - const isFetched = useAppSelector(getAreSchemaLatestFulfilled); - const isRejected = useAppSelector(getAreSchemaLatestRejected); - const areVersionsFetched = useAppSelector(getAreSchemaVersionsFulfilled); + const { mutateAsync: deleteSchema } = useDeleteSchema({ + clusterName, + subject, + }); const columns = React.useMemo( () => [ @@ -74,28 +61,18 @@ const Details: React.FC = () => { ); const deleteHandler = async () => { - try { - await schemasApiClient.deleteSchema({ - clusterName, - subject, - }); - navigate('../'); - } catch (e) { - showServerError(e as Response); - } + await deleteSchema(); + navigate('../'); }; - if (isRejected) { - navigate('/404'); - } - - if (!isFetched || !schema) { + if (isSchemaFetching || areVersionsFetching || isErrorLatestSchema) { return ; } + return ( <> @@ -144,9 +121,11 @@ const Details: React.FC = () => { )} - + {schema && } Old versions - {areVersionsFetched ? ( + {areVersionsFetching ? ( + + ) : ( { renderSubComponent={SchemaVersion} enableSorting /> - ) : ( - )} ); diff --git a/frontend/src/components/Schemas/Details/__test__/Details.spec.tsx b/frontend/src/components/Schemas/Details/__test__/Details.spec.tsx index 3b58c376e..ef40efa2d 100644 --- a/frontend/src/components/Schemas/Details/__test__/Details.spec.tsx +++ b/frontend/src/components/Schemas/Details/__test__/Details.spec.tsx @@ -4,23 +4,22 @@ import { render, WithRoute } from 'lib/testHelpers'; import { clusterSchemaPath } from 'lib/paths'; import { screen } from '@testing-library/dom'; import { - schemasInitialState, schemaVersion, schemaVersionWithNonAsciiChars, -} from 'redux/reducers/schemas/__test__/fixtures'; -import fetchMock from 'fetch-mock'; +} from 'components/Schemas/Edit/__tests__/fixtures'; import ClusterContext, { ContextProps, initialValue as contextInitialValue, } from 'components/contexts/ClusterContext'; -import { RootState } from 'redux/interfaces'; -import { act } from '@testing-library/react'; +import { + useDeleteSchema, + useGetLatestSchema, + useGetSchemasVersions, +} from 'lib/hooks/api/schemas'; import { versionPayload, versionEmptyPayload } from './fixtures'; const clusterName = 'testClusterName'; -const schemasAPILatestUrl = `/api/clusters/${clusterName}/schemas/${schemaVersion.subject}/latest`; -const schemasAPIVersionsUrl = `/api/clusters/${clusterName}/schemas/${schemaVersion.subject}/versions`; const mockHistoryPush = jest.fn(); jest.mock('react-router-dom', () => ({ @@ -28,10 +27,13 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockHistoryPush, })); -const renderComponent = ( - initialState: RootState['schemas'] = schemasInitialState, - context: ContextProps = contextInitialValue -) => +jest.mock('lib/hooks/api/schemas', () => ({ + useGetSchemasVersions: jest.fn(), + useGetLatestSchema: jest.fn(), + useDeleteSchema: jest.fn(), +})); + +const renderComponent = (context: ContextProps = contextInitialValue) => render( @@ -40,27 +42,33 @@ const renderComponent = ( , { initialEntries: [clusterSchemaPath(clusterName, schemaVersion.subject)], - preloadedState: { - schemas: initialState, - }, } ); describe('Details', () => { - afterEach(() => fetchMock.reset()); + const deleteMockfn = jest.fn(); + beforeEach(() => { + deleteMockfn.mockClear(); + + // TODO test case should be added for this + (useDeleteSchema as jest.Mock).mockImplementation(() => ({ + mutateAsync: deleteMockfn, + })); + }); describe('fetch failed', () => { - it('renders pageloader', async () => { - const schemasAPILatestMock = fetchMock.getOnce(schemasAPILatestUrl, 404); - const schemasAPIVersionsMock = fetchMock.getOnce( - schemasAPIVersionsUrl, - 404 - ); - await act(() => { - renderComponent(); - }); - expect(schemasAPILatestMock.called(schemasAPILatestUrl)).toBeTruthy(); - expect(schemasAPIVersionsMock.called(schemasAPIVersionsUrl)).toBeTruthy(); + it('renders page loader', async () => { + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: undefined, + isFetching: false, + isError: false, + })); + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: undefined, + isFetching: false, + isError: true, + })); + renderComponent(); expect(screen.getByRole('progressbar')).toBeInTheDocument(); expect(screen.queryByText(schemaVersion.subject)).not.toBeInTheDocument(); expect(screen.queryByText('Edit Schema')).not.toBeInTheDocument(); @@ -71,19 +79,17 @@ describe('Details', () => { describe('fetch success', () => { describe('has schema versions', () => { it('renders component with schema info', async () => { - const schemasAPILatestMock = fetchMock.getOnce( - schemasAPILatestUrl, - schemaVersion - ); - const schemasAPIVersionsMock = fetchMock.getOnce( - schemasAPIVersionsUrl, - versionPayload - ); - await act(() => { - renderComponent(); - }); - expect(schemasAPILatestMock.called()).toBeTruthy(); - expect(schemasAPIVersionsMock.called()).toBeTruthy(); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versionPayload, + isFetching: false, + isError: false, + })); + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: useGetSchemasVersions, + isFetching: false, + isError: false, + })); + renderComponent(); expect(screen.getByText('Edit Schema')).toBeInTheDocument(); expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); expect(screen.getByRole('table')).toBeInTheDocument(); @@ -93,19 +99,17 @@ describe('Details', () => { describe('fetch success schema with non ascii characters', () => { describe('has schema versions', () => { it('renders component with schema info', async () => { - const schemasAPILatestMock = fetchMock.getOnce( - schemasAPILatestUrl, - schemaVersionWithNonAsciiChars - ); - const schemasAPIVersionsMock = fetchMock.getOnce( - schemasAPIVersionsUrl, - versionPayload - ); - await act(() => { - renderComponent(); - }); - expect(schemasAPILatestMock.called()).toBeTruthy(); - expect(schemasAPIVersionsMock.called()).toBeTruthy(); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versionPayload, + isFetching: false, + isError: false, + })); + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: schemaVersionWithNonAsciiChars, + isFetching: false, + isError: false, + })); + renderComponent(); expect(screen.getByText('Edit Schema')).toBeInTheDocument(); expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); expect(screen.getByRole('table')).toBeInTheDocument(); @@ -115,19 +119,17 @@ describe('Details', () => { describe('empty schema versions', () => { beforeEach(async () => { - const schemasAPILatestMock = fetchMock.getOnce( - schemasAPILatestUrl, - schemaVersion - ); - const schemasAPIVersionsMock = fetchMock.getOnce( - schemasAPIVersionsUrl, - versionEmptyPayload - ); - await act(() => { - renderComponent(); - }); - expect(schemasAPILatestMock.called()).toBeTruthy(); - expect(schemasAPIVersionsMock.called()).toBeTruthy(); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versionEmptyPayload, + isFetching: false, + isError: false, + })); + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: schemaVersionWithNonAsciiChars, + isFetching: false, + isError: false, + })); + renderComponent(); }); // seems like incorrect behaviour diff --git a/frontend/src/components/Schemas/Details/__test__/fixtures.ts b/frontend/src/components/Schemas/Details/__test__/fixtures.ts index 18cca3b10..404d52251 100644 --- a/frontend/src/components/Schemas/Details/__test__/fixtures.ts +++ b/frontend/src/components/Schemas/Details/__test__/fixtures.ts @@ -3,7 +3,7 @@ import { schemaVersion1, schemaVersion2, schemaVersionWithNonAsciiChars, -} from 'redux/reducers/schemas/__test__/fixtures'; +} from 'components/Schemas/Edit/__tests__/fixtures'; export const versionPayload = [ schemaVersion1, diff --git a/frontend/src/components/Schemas/Diff/Diff.tsx b/frontend/src/components/Schemas/Diff/Diff.tsx index 05b1373ab..43e31eddf 100644 --- a/frontend/src/components/Schemas/Diff/Diff.tsx +++ b/frontend/src/components/Schemas/Diff/Diff.tsx @@ -8,27 +8,20 @@ import { import PageLoader from 'components/common/PageLoader/PageLoader'; import DiffViewer from 'components/common/DiffViewer/DiffViewer'; import { useNavigate, useLocation } from 'react-router-dom'; -import { - fetchSchemaVersions, - SCHEMAS_VERSIONS_FETCH_ACTION, -} from 'redux/reducers/schemas/schemasSlice'; import { useForm, Controller } from 'react-hook-form'; import Select from 'components/common/Select/Select'; -import { useAppDispatch } from 'lib/hooks/redux'; -import { resetLoaderById } from 'redux/reducers/loader/loaderSlice'; import useAppParams from 'lib/hooks/useAppParams'; import PageHeading from 'components/common/PageHeading/PageHeading'; +import { useGetSchemasVersions } from 'lib/hooks/api/schemas'; import * as S from './Diff.styled'; import { BackButton } from './Diff.styled'; -export interface DiffProps { - versions: SchemaSubject[]; - areVersionsFetched: boolean; -} - -const Diff: React.FC = ({ versions, areVersionsFetched }) => { +const Diff: React.FC = () => { const { clusterName, subject } = useAppParams(); + const { data: versions, isFetching: areVersionsFetching } = + useGetSchemasVersions({ clusterName, subject }); + const navigate = useNavigate(); const location = useLocation(); @@ -44,15 +37,6 @@ const Diff: React.FC = ({ versions, areVersionsFetched }) => { searchParams.get('rightVersion') || '' ); - const dispatch = useAppDispatch(); - - React.useEffect(() => { - dispatch(fetchSchemaVersions({ clusterName, subject })); - return () => { - dispatch(resetLoaderById(SCHEMAS_VERSIONS_FETCH_ACTION)); - }; - }, [clusterName, subject, dispatch]); - const getSchemaContent = (allVersions: SchemaSubject[], version: string) => { const selectedSchema = allVersions.find((s) => s.version === version)?.schema || @@ -86,7 +70,7 @@ const Diff: React.FC = ({ versions, areVersionsFetched }) => { Back - {areVersionsFetched ? ( + {!areVersionsFetching && versions ? ( diff --git a/frontend/src/components/Schemas/Diff/DiffContainer.ts b/frontend/src/components/Schemas/Diff/DiffContainer.ts deleted file mode 100644 index 4b89529a1..000000000 --- a/frontend/src/components/Schemas/Diff/DiffContainer.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { connect } from 'react-redux'; -import { RootState } from 'redux/interfaces'; -import { - getAreSchemaVersionsFulfilled, - selectAllSchemaVersions, -} from 'redux/reducers/schemas/schemasSlice'; - -import Diff from './Diff'; - -const mapStateToProps = (state: RootState) => ({ - versions: selectAllSchemaVersions(state), - areVersionsFetched: getAreSchemaVersionsFulfilled(state), -}); - -export default connect(mapStateToProps)(Diff); diff --git a/frontend/src/components/Schemas/Diff/__test__/Diff.spec.tsx b/frontend/src/components/Schemas/Diff/__test__/Diff.spec.tsx index 2a9429eef..75734ff1c 100644 --- a/frontend/src/components/Schemas/Diff/__test__/Diff.spec.tsx +++ b/frontend/src/components/Schemas/Diff/__test__/Diff.spec.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import Diff, { DiffProps } from 'components/Schemas/Diff/Diff'; +import Diff from 'components/Schemas/Diff/Diff'; import { render, WithRoute } from 'lib/testHelpers'; import { screen } from '@testing-library/react'; import { clusterSchemaComparePath } from 'lib/paths'; import userEvent from '@testing-library/user-event'; +import { useGetSchemasVersions } from 'lib/hooks/api/schemas'; import { versions } from './fixtures'; @@ -14,9 +15,12 @@ const defaultPathName = clusterSchemaComparePath( defaultSubject ); +jest.mock('lib/hooks/api/schemas', () => ({ + useGetSchemasVersions: jest.fn(), +})); + describe('Diff', () => { const setupComponent = ( - props: DiffProps, searchQuery: { rightVersion?: string; leftVersion?: string } = {} ) => { let pathname = defaultPathName; @@ -32,10 +36,7 @@ describe('Diff', () => { return render( - + , { initialEntries: [pathname], @@ -45,26 +46,25 @@ describe('Diff', () => { describe('Container', () => { it('renders view', () => { - setupComponent({ - areVersionsFetched: true, - versions, - }); - expect(screen.getAllByText('Version 3').length).toEqual(4); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versions, + isFetching: false, + isError: false, + })); + setupComponent(); + // TODO make sure this case it correct + expect(screen.getAllByText('Version 3').length).toEqual(2); }); }); - describe('View', () => { - setupComponent({ - areVersionsFetched: true, - versions, - }); - }); describe('when page with schema versions is loading', () => { beforeAll(() => { - setupComponent({ - areVersionsFetched: false, - versions: [], - }); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: undefined, + isFetching: true, + isError: false, + })); + setupComponent(); }); it('renders PageLoader', () => { expect(screen.getByRole('progressbar')).toBeInTheDocument(); @@ -73,10 +73,12 @@ describe('Diff', () => { describe('when schema versions are loaded and no specified versions in path', () => { beforeEach(() => { - setupComponent({ - areVersionsFetched: true, - versions, - }); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versions, + isFetching: false, + isError: false, + })); + setupComponent(); }); it('renders all options', () => { @@ -94,15 +96,15 @@ describe('Diff', () => { expect(select).toHaveTextContent(versions[0].version); }); }); + describe('when schema versions are loaded and two versions in path', () => { beforeEach(() => { - setupComponent( - { - areVersionsFetched: true, - versions, - }, - { leftVersion: '1', rightVersion: '2' } - ); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versions, + isFetching: false, + isError: false, + })); + setupComponent({ leftVersion: '1', rightVersion: '2' }); }); it('renders left select with version 1', () => { @@ -120,15 +122,14 @@ describe('Diff', () => { describe('when schema versions are loaded and only one versions in path', () => { beforeEach(() => { - setupComponent( - { - areVersionsFetched: true, - versions, - }, - { - leftVersion: '1', - } - ); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versions, + isFetching: false, + isError: false, + })); + setupComponent({ + leftVersion: '1', + }); }); it('renders left select with version 1', () => { @@ -146,10 +147,12 @@ describe('Diff', () => { describe('Back button', () => { beforeEach(() => { - setupComponent({ - areVersionsFetched: true, - versions, - }); + (useGetSchemasVersions as jest.Mock).mockImplementation(() => ({ + data: versions, + isFetching: false, + isError: false, + })); + setupComponent(); }); it('back button is appear', () => { diff --git a/frontend/src/components/Schemas/Edit/Edit.tsx b/frontend/src/components/Schemas/Edit/Edit.tsx index bddb2f173..2e6892197 100644 --- a/frontend/src/components/Schemas/Edit/Edit.tsx +++ b/frontend/src/components/Schemas/Edit/Edit.tsx @@ -1,37 +1,37 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { ClusterSubjectParam } from 'lib/paths'; -import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import useAppParams from 'lib/hooks/useAppParams'; -import { - fetchLatestSchema, - getSchemaLatest, - SCHEMA_LATEST_FETCH_ACTION, - getAreSchemaLatestFulfilled, -} from 'redux/reducers/schemas/schemasSlice'; import PageLoader from 'components/common/PageLoader/PageLoader'; -import { resetLoaderById } from 'redux/reducers/loader/loaderSlice'; +import { useNavigate } from 'react-router-dom'; +import { useGetLatestSchema } from 'lib/hooks/api/schemas'; import Form from './Form'; const Edit: React.FC = () => { - const dispatch = useAppDispatch(); - + const navigate = useNavigate(); const { clusterName, subject } = useAppParams(); + const { + isFetching, + isError, + data: schema, + } = useGetLatestSchema({ + clusterName, + subject, + }); - React.useEffect(() => { - dispatch(fetchLatestSchema({ clusterName, subject })); - return () => { - dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION)); - }; - }, [clusterName, dispatch, subject]); - - const schema = useAppSelector((state) => getSchemaLatest(state)); - const isFetched = useAppSelector(getAreSchemaLatestFulfilled); + useEffect(() => { + if (isError) { + navigate('/404'); + } + }, [isError]); - if (!isFetched || !schema) { + if (isFetching) { return ; } - return
; + + if (!schema) return null; + + return ; }; export default Edit; diff --git a/frontend/src/components/Schemas/Edit/Form.tsx b/frontend/src/components/Schemas/Edit/Form.tsx index 56d7bdc81..8542732ac 100644 --- a/frontend/src/components/Schemas/Edit/Form.tsx +++ b/frontend/src/components/Schemas/Edit/Form.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { useForm, Controller, FormProvider } from 'react-hook-form'; import { CompatibilityLevelCompatibilityEnum, + SchemaSubject, SchemaType, } from 'generated-sources'; import { @@ -11,39 +12,33 @@ import { ClusterSubjectParam, } from 'lib/paths'; import yup from 'lib/yupExtended'; -import { NewSchemaSubjectRaw } from 'redux/interfaces'; +import { NewSchemaSubjectRaw } from 'lib/interfaces/schema'; import Editor from 'components/common/Editor/Editor'; import Select from 'components/common/Select/Select'; import { Button } from 'components/common/Button/Button'; import { InputLabel } from 'components/common/Input/InputLabel.styled'; import PageHeading from 'components/common/PageHeading/PageHeading'; -import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import useAppParams from 'lib/hooks/useAppParams'; -import { - schemaAdded, - getSchemaLatest, - getAreSchemaLatestFulfilled, - schemaUpdated, - getAreSchemaLatestRejected, -} from 'redux/reducers/schemas/schemasSlice'; -import PageLoader from 'components/common/PageLoader/PageLoader'; -import { schemasApiClient } from 'lib/api'; -import { showServerError } from 'lib/errorHandling'; import { yupResolver } from '@hookform/resolvers/yup'; import { FormError } from 'components/common/Input/Input.styled'; import { ErrorMessage } from '@hookform/error-message'; +import { + useCreateSchema, + useUpdateSchemaCompatibilityLayer, +} from 'lib/hooks/api/schemas'; import * as S from './Edit.styled'; -const Form: React.FC = () => { - const navigate = useNavigate(); - const dispatch = useAppDispatch(); +interface FormProps { + schema: SchemaSubject; +} +const Form: React.FC = ({ schema }) => { + const navigate = useNavigate(); const { clusterName, subject } = useAppParams(); - - const schema = useAppSelector((state) => getSchemaLatest(state)); - const isFetched = useAppSelector(getAreSchemaLatestFulfilled); - const isRejected = useAppSelector(getAreSchemaLatestRejected); + const { mutateAsync: createSchema } = useCreateSchema(clusterName); + const { mutateAsync: updateCompatibilityLayer } = + useUpdateSchemaCompatibilityLayer({ clusterName, subject }); const formatedSchema = React.useMemo(() => { return schema?.schemaType === SchemaType.PROTOBUF @@ -77,47 +72,26 @@ const Form: React.FC = () => { const onSubmit = async (props: NewSchemaSubjectRaw) => { if (!schema) return; - try { - if (dirtyFields.compatibilityLevel) { - await schemasApiClient.updateSchemaCompatibilityLevel({ - clusterName, - subject, - compatibilityLevel: { - compatibility: props.compatibilityLevel, - }, - }); - dispatch( - schemaUpdated({ - ...schema, - compatibilityLevel: props.compatibilityLevel, - }) - ); - } - if (dirtyFields.newSchema || dirtyFields.schemaType) { - const resp = await schemasApiClient.createNewSchema({ - clusterName, - newSchemaSubject: { - ...schema, - schema: props.newSchema || schema.schema, - schemaType: props.schemaType || schema.schemaType, - }, - }); - dispatch(schemaAdded(resp)); - } + if (dirtyFields.compatibilityLevel) { + await updateCompatibilityLayer({ + ...schema, + compatibilityLevel: { + compatibility: props.compatibilityLevel, + }, + }); + } - navigate(clusterSchemaPath(clusterName, subject)); - } catch (e) { - showServerError(e as Response); + if (dirtyFields.newSchema || dirtyFields.schemaType) { + await createSchema({ + ...schema, + schema: props.newSchema || schema.schema, + schemaType: props.schemaType || schema.schemaType, + }); } - }; - if (isRejected) { - navigate('/404'); - } + navigate(clusterSchemaPath(clusterName, subject)); + }; - if (!isFetched || !schema) { - return ; - } return ( +const renderComponent = (context: ContextProps = contextInitialValue) => render( @@ -33,40 +23,65 @@ const renderComponent = ( initialEntries: [ clusterSchemaEditPath(clusterName, schemaVersion.subject), ], - preloadedState: { - schemas: initialState, - }, } ); +jest.mock('lib/hooks/api/schemas', () => ({ + useGetLatestSchema: jest.fn(), +})); + +const mockedUsedNavigate = jest.fn(); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => mockedUsedNavigate, +})); + +const FormCompText = 'FormCompText'; + +jest.mock('components/Schemas/Edit/Form', () => () => ( +
{FormCompText}
+)); + describe('Edit', () => { - afterEach(() => fetchMock.reset()); + beforeEach(() => { + mockedUsedNavigate.mockClear(); + }); + + describe('Fetch is Errored', () => { + it('should navigate to the 404 page when fetch is not successful', () => { + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: undefined, + isFetching: false, + isError: true, + })); + renderComponent(); + expect(mockedUsedNavigate).toHaveBeenCalledTimes(1); + }); + }); describe('fetch success', () => { - describe('has schema versions', () => { - it('renders component with schema info', async () => { - fetchMock.getOnce(schemasAPILatestUrl, schemaVersion); - await act(() => { - renderComponent(); - }); - expect(fetchMock.called(schemasAPILatestUrl)).toBeTruthy(); - expect(screen.getByText('Submit')).toBeInTheDocument(); - expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); - }); + it('renders component with schema info', async () => { + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: schemaVersion, + isFetching: false, + isError: false, + })); + renderComponent(); + expect(screen.getByText(FormCompText)).toBeInTheDocument(); + expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); }); }); - describe('fetch success schema with non ascii characters', () => { - describe('has schema versions', () => { - it('renders component with schema info', async () => { - fetchMock.getOnce(schemasAPILatestUrl, schemaVersionWithNonAsciiChars); - await act(() => { - renderComponent(); - }); - expect(fetchMock.called(schemasAPILatestUrl)).toBeTruthy(); - expect(screen.getByText('Submit')).toBeInTheDocument(); - expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); - }); + describe('fetch is loading', () => { + it('renders loader during', async () => { + (useGetLatestSchema as jest.Mock).mockImplementation(() => ({ + data: undefined, + isFetching: true, + isError: false, + })); + renderComponent(); + expect(screen.queryByRole('progressbar')).toBeInTheDocument(); }); }); }); diff --git a/frontend/src/redux/reducers/schemas/__test__/fixtures.ts b/frontend/src/components/Schemas/Edit/__tests__/fixtures.ts similarity index 64% rename from frontend/src/redux/reducers/schemas/__test__/fixtures.ts rename to frontend/src/components/Schemas/Edit/__tests__/fixtures.ts index 5cab12584..6d2cf49ba 100644 --- a/frontend/src/redux/reducers/schemas/__test__/fixtures.ts +++ b/frontend/src/components/Schemas/Edit/__tests__/fixtures.ts @@ -1,16 +1,4 @@ import { SchemaType, SchemaSubject } from 'generated-sources'; -import { RootState } from 'redux/interfaces'; - -export const schemasInitialState: RootState['schemas'] = { - totalPages: 0, - ids: [], - entities: {}, - versions: { - latest: null, - ids: [], - entities: {}, - }, -}; export const schemaVersion1: SchemaSubject = { subject: 'schema7_1', @@ -39,17 +27,3 @@ export const schemaVersionWithNonAsciiChars: SchemaSubject = { }; export { schemaVersion1 as schemaVersion }; - -export const schemasFulfilledState = { - totalPages: 1, - ids: [schemaVersion2.subject, schemaVersion1.subject], - entities: { - [schemaVersion2.subject]: schemaVersion2, - [schemaVersion1.subject]: schemaVersion1, - }, - versions: { - latest: null, - ids: [], - entities: {}, - }, -}; diff --git a/frontend/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx b/frontend/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx index 002d71a37..ad5563229 100644 --- a/frontend/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx +++ b/frontend/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx @@ -4,50 +4,34 @@ import { CompatibilityLevelCompatibilityEnum, ResourceType, } from 'generated-sources'; -import { useAppDispatch } from 'lib/hooks/redux'; import useAppParams from 'lib/hooks/useAppParams'; -import { fetchSchemas } from 'redux/reducers/schemas/schemasSlice'; import { ClusterNameRoute } from 'lib/paths'; -import { schemasApiClient } from 'lib/api'; -import { showServerError } from 'lib/errorHandling'; import { useConfirm } from 'lib/hooks/useConfirm'; -import { useSearchParams } from 'react-router-dom'; -import { PER_PAGE } from 'lib/constants'; import { ActionSelect } from 'components/common/ActionComponent'; +import { + useGetGlobalCompatibilityLayer, + useUpdateGlobalSchemaCompatibilityLevel, +} from 'lib/hooks/api/schemas'; import * as S from './GlobalSchemaSelector.styled'; +function isCompatibilityLevelCompatibilityEnum( + value: string | number +): value is CompatibilityLevelCompatibilityEnum { + return value in CompatibilityLevelCompatibilityEnum; +} + const GlobalSchemaSelector: React.FC = () => { const { clusterName } = useAppParams(); - const dispatch = useAppDispatch(); - const [searchParams] = useSearchParams(); + const { data: currentCompatibilityLevel, isFetching } = + useGetGlobalCompatibilityLayer(clusterName); + const { mutateAsync } = useUpdateGlobalSchemaCompatibilityLevel(clusterName); const confirm = useConfirm(); - const [currentCompatibilityLevel, setCurrentCompatibilityLevel] = - React.useState(); - - const [isFetching, setIsFetching] = React.useState(false); - - React.useEffect(() => { - const fetchData = async () => { - setIsFetching(true); - try { - const { compatibility } = - await schemasApiClient.getGlobalSchemaCompatibilityLevel({ - clusterName, - }); - setCurrentCompatibilityLevel(compatibility); - } catch (error) { - // do nothing - } - setIsFetching(false); - }; - - fetchData(); - }, [clusterName]); - const handleChangeCompatibilityLevel = (level: string | number) => { - const nextLevel = level as CompatibilityLevelCompatibilityEnum; + if (!isCompatibilityLevelCompatibilityEnum(level)) return; + + const nextLevel = level; confirm( <> Are you sure you want to update the global compatibility level and set @@ -55,25 +39,9 @@ const GlobalSchemaSelector: React.FC = () => { the schemas. , async () => { - try { - await schemasApiClient.updateGlobalSchemaCompatibilityLevel({ - clusterName, - compatibilityLevel: { - compatibility: nextLevel, - }, - }); - setCurrentCompatibilityLevel(nextLevel); - dispatch( - fetchSchemas({ - clusterName, - page: Number(searchParams.get('page') || 1), - perPage: Number(searchParams.get('perPage') || PER_PAGE), - search: searchParams.get('q') || '', - }) - ); - } catch (e) { - showServerError(e as Response); - } + await mutateAsync({ + compatibilityLevel: { compatibility: nextLevel }, + }); } ); }; @@ -85,7 +53,7 @@ const GlobalSchemaSelector: React.FC = () => {
Global Compatibility Level:
{ // clicks to open dropdown await userEvent.click(within(dropdownElement).getByRole('option')); await userEvent.click( - screen.getByText(CompatibilityLevelCompatibilityEnum.FORWARD) + within(dropdownElement).getByText( + CompatibilityLevelCompatibilityEnum.FORWARD + ) ); }; @@ -25,6 +30,11 @@ const expectOptionIsSelected = (option: string) => { expect(selectedOption[0]).toHaveTextContent(option); }; +jest.mock('lib/hooks/api/schemas', () => ({ + useGetGlobalCompatibilityLayer: jest.fn(), + useUpdateGlobalSchemaCompatibilityLevel: jest.fn(), +})); + describe('GlobalSchemaSelector', () => { const renderComponent = () => render( @@ -36,21 +46,21 @@ describe('GlobalSchemaSelector', () => { } ); + const updateMockFn = jest.fn(); + beforeEach(async () => { - const fetchGlobalCompatibilityLevelMock = fetchMock.getOnce( - `api/clusters/${clusterName}/schemas/compatibility`, - { compatibility: CompatibilityLevelCompatibilityEnum.FULL } + updateMockFn.mockClear(); + (useUpdateGlobalSchemaCompatibilityLevel as jest.Mock).mockImplementation( + () => ({ + mutateAsync: updateMockFn, + }) ); - await act(() => { - renderComponent(); - }); - await waitFor(() => - expect(fetchGlobalCompatibilityLevelMock.called()).toBeTruthy() - ); - }); + (useGetGlobalCompatibilityLayer as jest.Mock).mockImplementation(() => ({ + data: { compatibility: CompatibilityLevelCompatibilityEnum.FULL }, + isFetching: false, + })); - afterEach(() => { - fetchMock.reset(); + renderComponent(); }); it('renders with initial prop', () => { @@ -72,31 +82,20 @@ describe('GlobalSchemaSelector', () => { it('sets new schema when confirm is clicked', async () => { await selectForwardOption(); - const putNewCompatibilityMock = fetchMock.putOnce( - `api/clusters/${clusterName}/schemas/compatibility`, - 200, - { - body: { - compatibility: CompatibilityLevelCompatibilityEnum.FORWARD, - }, - } - ); - const getSchemasMock = fetchMock.getOnce( - `api/clusters/${clusterName}/schemas?page=1&perPage=25`, - 200 - ); await waitFor(() => { userEvent.click(screen.getByRole('button', { name: 'Confirm' })); }); - await waitFor(() => expect(putNewCompatibilityMock.called()).toBeTruthy()); - await waitFor(() => expect(getSchemasMock.called()).toBeTruthy()); + await waitFor(() => { + expect(updateMockFn).toHaveBeenCalledTimes(1); + }); await waitFor(() => expect(screen.queryByText('Confirm the action')).not.toBeInTheDocument() ); - await waitFor(() => - expectOptionIsSelected(CompatibilityLevelCompatibilityEnum.FORWARD) - ); + // TODO this should be checked later not that important working as expected + // await waitFor(() => + // expectOptionIsSelected(CompatibilityLevelCompatibilityEnum.FORWARD) + // ); }); }); diff --git a/frontend/src/components/Schemas/List/List.tsx b/frontend/src/components/Schemas/List/List.tsx index 50ef556c6..5e4daf5f4 100644 --- a/frontend/src/components/Schemas/List/List.tsx +++ b/frontend/src/components/Schemas/List/List.tsx @@ -7,16 +7,8 @@ import { import ClusterContext from 'components/contexts/ClusterContext'; import { ActionButton } from 'components/common/ActionComponent'; import PageHeading from 'components/common/PageHeading/PageHeading'; -import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import useAppParams from 'lib/hooks/useAppParams'; -import { - selectAllSchemas, - fetchSchemas, - getAreSchemasFulfilled, - SCHEMAS_FETCH_ACTION, -} from 'redux/reducers/schemas/schemasSlice'; import PageLoader from 'components/common/PageLoader/PageLoader'; -import { resetLoaderById } from 'redux/reducers/loader/loaderSlice'; import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel.styled'; import Search from 'components/common/Search/Search'; import PlusIcon from 'components/common/Icons/PlusIcon'; @@ -25,32 +17,25 @@ import { ColumnDef } from '@tanstack/react-table'; import { Action, SchemaSubject, ResourceType } from 'generated-sources'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { PER_PAGE } from 'lib/constants'; +import { useGetSchemas } from 'lib/hooks/api/schemas'; import GlobalSchemaSelector from './GlobalSchemaSelector/GlobalSchemaSelector'; const List: React.FC = () => { - const dispatch = useAppDispatch(); const { isReadOnly } = React.useContext(ClusterContext); const { clusterName } = useAppParams(); const navigate = useNavigate(); - const schemas = useAppSelector(selectAllSchemas); - const isFetched = useAppSelector(getAreSchemasFulfilled); - const totalPages = useAppSelector((state) => state.schemas.totalPages); const [searchParams] = useSearchParams(); - - React.useEffect(() => { - dispatch( - fetchSchemas({ - clusterName, - page: Number(searchParams.get('page') || 1), - perPage: Number(searchParams.get('perPage') || PER_PAGE), - search: searchParams.get('q') || '', - }) - ); - return () => { - dispatch(resetLoaderById(SCHEMAS_FETCH_ACTION)); - }; - }, [clusterName, dispatch, searchParams]); + const { + isFetching, + isError, + data = { pageCount: 1, schemas: [] as SchemaSubject[] }, + } = useGetSchemas({ + clusterName, + page: Number(searchParams.get('page') || 1), + perPage: Number(searchParams.get('perPage') || PER_PAGE), + search: searchParams.get('q') || '', + }); const columns = React.useMemo[]>( () => [ @@ -96,19 +81,19 @@ const List: React.FC = () => { - {isFetched ? ( + {isFetching || isError ? ( + + ) : (
navigate(clusterSchemaPath(clusterName, row.original.subject)) } serverSideProcessing /> - ) : ( - )} ); diff --git a/frontend/src/components/Schemas/List/__test__/List.spec.tsx b/frontend/src/components/Schemas/List/__test__/List.spec.tsx index f27f659b2..e3dcb6e44 100644 --- a/frontend/src/components/Schemas/List/__test__/List.spec.tsx +++ b/frontend/src/components/Schemas/List/__test__/List.spec.tsx @@ -2,37 +2,40 @@ import React from 'react'; import List from 'components/Schemas/List/List'; import { render, WithRoute } from 'lib/testHelpers'; import { clusterSchemaPath, clusterSchemasPath } from 'lib/paths'; -import { act, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { - schemasFulfilledState, - schemasInitialState, schemaVersion1, schemaVersion2, -} from 'redux/reducers/schemas/__test__/fixtures'; +} from 'components/Schemas/Edit/__tests__/fixtures'; import ClusterContext, { ContextProps, initialValue as contextInitialValue, } from 'components/contexts/ClusterContext'; -import { RootState } from 'redux/interfaces'; -import fetchMock from 'fetch-mock'; import userEvent from '@testing-library/user-event'; +import { useGetSchemas } from 'lib/hooks/api/schemas'; import { schemasPayload, schemasEmptyPayload } from './fixtures'; const mockedUsedNavigate = jest.fn(); +const GlobalSchemaSelectorText = 'GlobalSchemaSelectorText'; + jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockedUsedNavigate, })); +jest.mock('lib/hooks/api/schemas', () => ({ + useGetSchemas: jest.fn(), +})); + +jest.mock( + 'components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector', + () => () =>
{GlobalSchemaSelectorText}
+); + const clusterName = 'testClusterName'; -const schemasAPIUrl = `/api/clusters/${clusterName}/schemas?page=1&perPage=25`; -const schemasAPICompabilityUrl = `/api/clusters/${clusterName}/schemas/compatibility`; -const renderComponent = ( - initialState: RootState['schemas'] = schemasInitialState, - context: ContextProps = contextInitialValue -) => +const renderComponent = (context: ContextProps = contextInitialValue) => render( @@ -41,29 +44,17 @@ const renderComponent = ( , { initialEntries: [clusterSchemasPath(clusterName)], - preloadedState: { - schemas: initialState, - }, } ); describe('List', () => { - afterEach(() => { - fetchMock.reset(); - }); - describe('fetch error', () => { it('shows progressbar', async () => { - const fetchSchemasMock = fetchMock.getOnce(schemasAPIUrl, 404); - const fetchCompabilityMock = fetchMock.getOnce( - schemasAPICompabilityUrl, - 404 - ); - await act(() => { - renderComponent(); - }); - expect(fetchSchemasMock.called()).toBeTruthy(); - expect(fetchCompabilityMock.called()).toBeTruthy(); + (useGetSchemas as jest.Mock).mockImplementation(() => ({ + data: {}, + isError: true, + })); + renderComponent(); expect(screen.getByRole('progressbar')).toBeInTheDocument(); }); }); @@ -71,19 +62,12 @@ describe('List', () => { describe('fetch success', () => { describe('responded without schemas', () => { beforeEach(async () => { - const fetchSchemasMock = fetchMock.getOnce( - schemasAPIUrl, - schemasEmptyPayload - ); - const fetchCompabilityMock = fetchMock.getOnce( - schemasAPICompabilityUrl, - 200 - ); - await act(() => { - renderComponent(); - }); - expect(fetchSchemasMock.called()).toBeTruthy(); - expect(fetchCompabilityMock.called()).toBeTruthy(); + (useGetSchemas as jest.Mock).mockImplementation(() => ({ + data: schemasEmptyPayload, + isFetching: false, + isError: false, + })); + renderComponent(); }); it('renders empty table', () => { expect(screen.getByText('No schemas found')).toBeInTheDocument(); @@ -91,19 +75,12 @@ describe('List', () => { }); describe('responded with schemas', () => { beforeEach(async () => { - const fetchSchemasMock = fetchMock.getOnce( - schemasAPIUrl, - schemasPayload - ); - const fetchCompabilityMock = fetchMock.getOnce( - schemasAPICompabilityUrl, - 200 - ); - await act(() => { - renderComponent(schemasFulfilledState); - }); - expect(fetchSchemasMock.called()).toBeTruthy(); - expect(fetchCompabilityMock.called()).toBeTruthy(); + (useGetSchemas as jest.Mock).mockImplementation(() => ({ + data: schemasPayload, + isFetching: false, + isError: false, + })); + renderComponent(); }); it('renders list', () => { expect(screen.getByText(schemaVersion1.subject)).toBeInTheDocument(); @@ -125,22 +102,31 @@ describe('List', () => { describe('responded with readonly cluster schemas', () => { beforeEach(async () => { - const fetchSchemasMock = fetchMock.getOnce( - schemasAPIUrl, - schemasPayload - ); - fetchMock.getOnce(schemasAPICompabilityUrl, 200); - await act(() => { - renderComponent(schemasFulfilledState, { - ...contextInitialValue, - isReadOnly: true, - }); + (useGetSchemas as jest.Mock).mockImplementation(() => ({ + data: schemasPayload, + isFetching: false, + isError: false, + })); + renderComponent({ + ...contextInitialValue, + isReadOnly: true, }); - expect(fetchSchemasMock.called()).toBeTruthy(); }); it('does not render Create Schema button', () => { expect(screen.queryByText('Create Schema')).not.toBeInTheDocument(); }); }); }); + + describe('check the compatibility layer', () => { + it('should check if the compatibility layer component is being shown', () => { + (useGetSchemas as jest.Mock).mockImplementation(() => ({ + data: {}, + isError: false, + isFetching: false, + })); + renderComponent(); + expect(screen.getByText(GlobalSchemaSelectorText)).toBeInTheDocument(); + }); + }); }); diff --git a/frontend/src/components/Schemas/List/__test__/fixtures.ts b/frontend/src/components/Schemas/List/__test__/fixtures.ts index 992882d33..e9a357702 100644 --- a/frontend/src/components/Schemas/List/__test__/fixtures.ts +++ b/frontend/src/components/Schemas/List/__test__/fixtures.ts @@ -2,7 +2,7 @@ import { schemaVersion1, schemaVersion2, schemaVersionWithNonAsciiChars, -} from 'redux/reducers/schemas/__test__/fixtures'; +} from 'components/Schemas/Edit/__tests__/fixtures'; const schemas = [ schemaVersion1, diff --git a/frontend/src/components/Schemas/New/New.tsx b/frontend/src/components/Schemas/New/New.tsx index 81c987ccd..4bfcc4605 100644 --- a/frontend/src/components/Schemas/New/New.tsx +++ b/frontend/src/components/Schemas/New/New.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NewSchemaSubjectRaw } from 'redux/interfaces'; +import { NewSchemaSubjectRaw } from 'lib/interfaces/schema'; import { Controller, FormProvider, useForm } from 'react-hook-form'; import { ErrorMessage } from '@hookform/error-message'; import { @@ -17,13 +17,10 @@ import Select from 'components/common/Select/Select'; import { Button } from 'components/common/Button/Button'; import { Textarea } from 'components/common/Textbox/Textarea.styled'; import PageHeading from 'components/common/PageHeading/PageHeading'; -import { schemaAdded } from 'redux/reducers/schemas/schemasSlice'; -import { useAppDispatch } from 'lib/hooks/redux'; import useAppParams from 'lib/hooks/useAppParams'; -import { showServerError } from 'lib/errorHandling'; -import { schemasApiClient } from 'lib/api'; import yup from 'lib/yupExtended'; import { yupResolver } from '@hookform/resolvers/yup'; +import { useCreateSchema } from 'lib/hooks/api/schemas'; import * as S from './New.styled'; @@ -33,16 +30,6 @@ const SchemaTypeOptions = [ { value: SchemaType.PROTOBUF, label: 'PROTOBUF' }, ]; -const schemaCreate = async ( - { subject, schema, schemaType }: NewSchemaSubjectRaw, - clusterName: string -) => { - return schemasApiClient.createNewSchema({ - clusterName, - newSchemaSubject: { subject, schema, schemaType }, - }); -}; - const validationSchema = yup.object().shape({ subject: yup .string() @@ -58,7 +45,7 @@ const validationSchema = yup.object().shape({ const New: React.FC = () => { const { clusterName } = useAppParams(); const navigate = useNavigate(); - const dispatch = useAppDispatch(); + const { mutateAsync } = useCreateSchema(clusterName); const methods = useForm({ mode: 'onChange', defaultValues: { @@ -78,16 +65,8 @@ const New: React.FC = () => { schema, schemaType, }: NewSchemaSubjectRaw) => { - try { - const resp = await schemaCreate( - { subject, schema, schemaType } as NewSchemaSubjectRaw, - clusterName - ); - dispatch(schemaAdded(resp)); - navigate(clusterSchemaPath(clusterName, subject)); - } catch (e) { - showServerError(e as Response); - } + await mutateAsync({ subject, schema, schemaType }); + navigate(clusterSchemaPath(clusterName, subject)); }; return ( diff --git a/frontend/src/components/Schemas/Schemas.tsx b/frontend/src/components/Schemas/Schemas.tsx index 36b18773f..39f058e62 100644 --- a/frontend/src/components/Schemas/Schemas.tsx +++ b/frontend/src/components/Schemas/Schemas.tsx @@ -10,7 +10,7 @@ import List from 'components/Schemas/List/List'; import Details from 'components/Schemas/Details/Details'; import New from 'components/Schemas/New/New'; import Edit from 'components/Schemas/Edit/Edit'; -import DiffContainer from 'components/Schemas/Diff/DiffContainer'; +import Diff from 'components/Schemas/Diff/Diff'; const Schemas: React.FC = () => { return ( @@ -19,10 +19,7 @@ const Schemas: React.FC = () => { } /> } /> } /> - } - /> + } /> ); }; diff --git a/frontend/src/components/Schemas/__test__/Schemas.spec.tsx b/frontend/src/components/Schemas/__test__/Schemas.spec.tsx index 604a21d0b..d9d97fcc8 100644 --- a/frontend/src/components/Schemas/__test__/Schemas.spec.tsx +++ b/frontend/src/components/Schemas/__test__/Schemas.spec.tsx @@ -10,7 +10,7 @@ import { } from 'lib/paths'; import { screen, waitFor } from '@testing-library/dom'; import fetchMock from 'fetch-mock'; -import { schemaVersion } from 'redux/reducers/schemas/__test__/fixtures'; +import { schemaVersion } from 'components/Schemas/Edit/__tests__/fixtures'; const renderComponent = (pathname: string) => render( diff --git a/frontend/src/components/Topics/List/BatchActionsBar.tsx b/frontend/src/components/Topics/List/BatchActionsBar.tsx index 9e7d6417b..a3c0eb0f1 100644 --- a/frontend/src/components/Topics/List/BatchActionsBar.tsx +++ b/frontend/src/components/Topics/List/BatchActionsBar.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { Row } from '@tanstack/react-table'; import { Action, Topic, ResourceType } from 'generated-sources'; import useAppParams from 'lib/hooks/useAppParams'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { topicKeys, useClearTopicMessages, diff --git a/frontend/src/components/Topics/List/TopicTable.tsx b/frontend/src/components/Topics/List/TopicTable.tsx index dcafa61b9..6ebf9b0b1 100644 --- a/frontend/src/components/Topics/List/TopicTable.tsx +++ b/frontend/src/components/Topics/List/TopicTable.tsx @@ -3,7 +3,7 @@ import { SortOrder, Topic, TopicColumnsToSort } from 'generated-sources'; import { ColumnDef } from '@tanstack/react-table'; import Table, { SizeCell } from 'components/common/NewTable'; import useAppParams from 'lib/hooks/useAppParams'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { useSearchParams } from 'react-router-dom'; import ClusterContext from 'components/contexts/ClusterContext'; import { useTopics } from 'lib/hooks/api/topics'; diff --git a/frontend/src/components/Topics/List/__tests__/TopicTable.spec.tsx b/frontend/src/components/Topics/List/__tests__/TopicTable.spec.tsx index 61be3f136..cca768652 100644 --- a/frontend/src/components/Topics/List/__tests__/TopicTable.spec.tsx +++ b/frontend/src/components/Topics/List/__tests__/TopicTable.spec.tsx @@ -16,11 +16,6 @@ import { clusterTopicsPath } from 'lib/paths'; const clusterName = 'test-cluster'; -jest.mock('lib/hooks/redux', () => ({ - ...jest.requireActual('lib/hooks/redux'), - useAppDispatch: jest.fn(), -})); - const getButtonByName = (name: string) => screen.getByRole('button', { name }); jest.mock('lib/hooks/api/topics', () => ({ diff --git a/frontend/src/components/Topics/New/New.tsx b/frontend/src/components/Topics/New/New.tsx index c7528fb99..acc63185f 100644 --- a/frontend/src/components/Topics/New/New.tsx +++ b/frontend/src/components/Topics/New/New.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { TopicFormData } from 'redux/interfaces'; import { FormProvider, useForm } from 'react-hook-form'; import { ClusterNameRoute, clusterTopicsPath } from 'lib/paths'; import TopicForm from 'components/Topics/shared/Form/TopicForm'; @@ -9,6 +8,7 @@ import { topicFormValidationSchema } from 'lib/yupExtended'; import PageHeading from 'components/common/PageHeading/PageHeading'; import useAppParams from 'lib/hooks/useAppParams'; import { useCreateTopic } from 'lib/hooks/api/topics'; +import { TopicFormData } from 'lib/interfaces/topic'; enum Filters { NAME = 'name', diff --git a/frontend/src/components/Topics/Topic/Edit/Edit.tsx b/frontend/src/components/Topics/Topic/Edit/Edit.tsx index b8a576649..598ac5982 100644 --- a/frontend/src/components/Topics/Topic/Edit/Edit.tsx +++ b/frontend/src/components/Topics/Topic/Edit/Edit.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { TopicConfigByName, TopicFormData } from 'redux/interfaces'; import { useForm, FormProvider } from 'react-hook-form'; import TopicForm from 'components/Topics/shared/Form/TopicForm'; import { RouteParamsClusterTopic } from 'lib/paths'; @@ -16,6 +15,7 @@ import { } from 'lib/hooks/api/topics'; import DangerZone from 'components/Topics/Topic/Edit/DangerZone/DangerZone'; import { ConfigSource } from 'generated-sources'; +import { TopicConfigByName, TopicFormData } from 'lib/interfaces/topic'; export const TOPIC_EDIT_FORM_DEFAULT_PROPS = { partitions: 1, diff --git a/frontend/src/components/Topics/Topic/__test__/Topic.spec.tsx b/frontend/src/components/Topics/Topic/__test__/Topic.spec.tsx index 4ec45c3a5..7b5731767 100644 --- a/frontend/src/components/Topics/Topic/__test__/Topic.spec.tsx +++ b/frontend/src/components/Topics/Topic/__test__/Topic.spec.tsx @@ -22,7 +22,6 @@ import { useRecreateTopic, useTopicDetails, } from 'lib/hooks/api/topics'; -import { useAppDispatch } from 'lib/hooks/redux'; const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ @@ -36,14 +35,8 @@ jest.mock('lib/hooks/api/topics', () => ({ useClearTopicMessages: jest.fn(), })); -const unwrapMock = jest.fn(); const clearTopicMessages = jest.fn(); -jest.mock('lib/hooks/redux', () => ({ - ...jest.requireActual('lib/hooks/redux'), - useAppDispatch: jest.fn(), -})); - jest.mock('components/Topics/Topic/Overview/Overview', () => () => ( <>OverviewMock )); @@ -105,9 +98,6 @@ describe('Details', () => { (useClearTopicMessages as jest.Mock).mockImplementation(() => ({ mutateAsync: clearTopicMessages, })); - (useAppDispatch as jest.Mock).mockImplementation(() => () => ({ - unwrap: unwrapMock, - })); }); describe('Action Bar', () => { describe('when it has readonly flag', () => { diff --git a/frontend/src/components/Topics/Topics.tsx b/frontend/src/components/Topics/Topics.tsx index d6557a858..81e7869c8 100644 --- a/frontend/src/components/Topics/Topics.tsx +++ b/frontend/src/components/Topics/Topics.tsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { Route, Routes } from 'react-router-dom'; +import PageLoader from 'components/common/PageLoader/PageLoader'; import { clusterTopicCopyRelativePath, clusterTopicNewRelativePath, @@ -8,24 +9,26 @@ import { } from 'lib/paths'; import SuspenseQueryComponent from 'components/common/SuspenseQueryComponent/SuspenseQueryComponent'; -import New from './New/New'; -import ListPage from './List/ListPage'; -import Topic from './Topic/Topic'; +const New = React.lazy(() => import('./New/New')); +const ListPage = React.lazy(() => import('./List/ListPage')); +const Topic = React.lazy(() => import('./Topic/Topic')); const Topics: React.FC = () => ( - - } /> - } /> - } /> - - - - } - /> - + }> + + } /> + } /> + } /> + + + + } + /> + + ); export default Topics; diff --git a/frontend/src/components/Topics/__tests__/Topics.spec.tsx b/frontend/src/components/Topics/__tests__/Topics.spec.tsx index 0e077445f..e4bc71942 100644 --- a/frontend/src/components/Topics/__tests__/Topics.spec.tsx +++ b/frontend/src/components/Topics/__tests__/Topics.spec.tsx @@ -35,14 +35,14 @@ describe('Topics Component', () => { { initialEntries: [path] } ); - it('should check if the page is Topics List rendered', () => { + it('should check if the page is Topics List rendered', async () => { setUpComponent(clusterTopicsPath(clusterName)); - expect(screen.getByText(listContainer)).toBeInTheDocument(); + expect(await screen.findByText(listContainer)).toBeInTheDocument(); }); - it('should check if the page is New Topic rendered', () => { + it('should check if the page is New Topic rendered', async () => { setUpComponent(clusterTopicNewPath(clusterName)); - expect(screen.getByText(newCopyContainer)).toBeInTheDocument(); + expect(await screen.findByText(newCopyContainer)).toBeInTheDocument(); }); it('should check if the page is Copy Topic rendered', () => { @@ -50,8 +50,8 @@ describe('Topics Component', () => { expect(screen.getByText(newCopyContainer)).toBeInTheDocument(); }); - it('should check if the page is Topic page rendered', () => { + it('should check if the page is Topic page rendered', async () => { setUpComponent(clusterTopicPath(clusterName, topicName)); - expect(screen.getByText(topicContainer)).toBeInTheDocument(); + expect(await screen.findByText(topicContainer)).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx b/frontend/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx index 95d3bcb15..75baca10e 100644 --- a/frontend/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx +++ b/frontend/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx @@ -2,7 +2,6 @@ import React, { useRef } from 'react'; import { ErrorMessage } from '@hookform/error-message'; import { TOPIC_CUSTOM_PARAMS } from 'lib/constants'; import { FieldArrayWithId, useFormContext, Controller } from 'react-hook-form'; -import { TopicConfigParams, TopicFormData } from 'redux/interfaces'; import { InputLabel } from 'components/common/Input/InputLabel.styled'; import { FormError } from 'components/common/Input/Input.styled'; import Select from 'components/common/Select/Select'; @@ -11,6 +10,7 @@ import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper'; import CloseCircleIcon from 'components/common/Icons/CloseCircleIcon'; import * as C from 'components/Topics/shared/Form/TopicForm.styled'; import { ConfigSource } from 'generated-sources'; +import { TopicConfigParams, TopicFormData } from 'lib/interfaces/topic'; import * as S from './CustomParams.styled'; diff --git a/frontend/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx b/frontend/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx index bd000b00f..7895bffbf 100644 --- a/frontend/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx +++ b/frontend/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { TopicConfigParams, TopicFormData } from 'redux/interfaces'; import { useFieldArray, useFormContext, useWatch } from 'react-hook-form'; import { Button } from 'components/common/Button/Button'; import { TOPIC_CUSTOM_PARAMS_PREFIX } from 'lib/constants'; import PlusIcon from 'components/common/Icons/PlusIcon'; +import { TopicConfigParams, TopicFormData } from 'lib/interfaces/topic'; import CustomParamField from './CustomParamField'; import * as S from './CustomParams.styled'; diff --git a/frontend/src/components/Topics/shared/Form/TopicForm.tsx b/frontend/src/components/Topics/shared/Form/TopicForm.tsx index 14ca69ed2..47a9ac187 100644 --- a/frontend/src/components/Topics/shared/Form/TopicForm.tsx +++ b/frontend/src/components/Topics/shared/Form/TopicForm.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { useFormContext, Controller } from 'react-hook-form'; import { NOT_SET, BYTES_IN_GB } from 'lib/constants'; -import { ClusterName, TopicConfigParams, TopicName } from 'redux/interfaces'; import { ErrorMessage } from '@hookform/error-message'; import Select from 'components/common/Select/Select'; import Input from 'components/common/Input/Input'; @@ -12,6 +11,8 @@ import { StyledForm } from 'components/common/Form/Form.styled'; import { clusterTopicPath } from 'lib/paths'; import { useNavigate } from 'react-router-dom'; import useAppParams from 'lib/hooks/useAppParams'; +import { TopicConfigParams, TopicName } from 'lib/interfaces/topic'; +import { ClusterName } from 'lib/interfaces/cluster'; import CustomParams from './CustomParams/CustomParams'; import TimeToRetain from './TimeToRetain'; diff --git a/frontend/src/components/__tests__/App.spec.tsx b/frontend/src/components/__tests__/App.spec.tsx index feb41e6a8..9a1213dcf 100644 --- a/frontend/src/components/__tests__/App.spec.tsx +++ b/frontend/src/components/__tests__/App.spec.tsx @@ -33,7 +33,7 @@ describe('App', () => { }); it('Renders navigation', async () => { - expect(screen.getByText('Navigation')).toBeInTheDocument(); + expect(await screen.findByText('Navigation')).toBeInTheDocument(); }); it('Renders NavBar', async () => { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 5ca52f75b..3d3672d2a 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,10 +1,8 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; -import { Provider } from 'react-redux'; import { ThemeModeProvider } from 'components/contexts/ThemeModeContext'; import App from 'components/App'; -import { store } from 'redux/store'; import 'lib/constants'; import 'theme/index.scss'; @@ -13,11 +11,9 @@ const container = const root = createRoot(container); root.render( - - - - - - - + + + + + ); diff --git a/frontend/src/lib/functions/nanoid.ts b/frontend/src/lib/functions/nanoid.ts new file mode 100644 index 000000000..14b2e49de --- /dev/null +++ b/frontend/src/lib/functions/nanoid.ts @@ -0,0 +1,22 @@ +// Borrowed from https://github.com/ai/nanoid/blob/3.0.2/non-secure/index.js +// This alphabet uses `A-Za-z0-9_-` symbols. A genetic algorithm helped +// optimize the gzip compression for this alphabet. +const urlAlphabet = + 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW'; + +/** + * + * @public + */ +export function nanoid(size = 21) { + let id = ''; + // A compact alternative for `for (var i = 0; i < step; i++)`. + let i = size; + // eslint-disable-next-line no-plusplus + while (i--) { + // `| 0` is more compact and faster than `Math.floor()`. + // eslint-disable-next-line no-bitwise + id += urlAlphabet[(Math.random() * 64) | 0]; + } + return id; +} diff --git a/frontend/src/lib/hooks/api/__tests__/schema.spec.ts b/frontend/src/lib/hooks/api/__tests__/schema.spec.ts new file mode 100644 index 000000000..eea7c94b8 --- /dev/null +++ b/frontend/src/lib/hooks/api/__tests__/schema.spec.ts @@ -0,0 +1,148 @@ +import { + expectQueryWorks, + renderQueryHook, + TestQueryClientProvider, +} from 'lib/testHelpers'; +import fetchMock from 'fetch-mock'; +import * as hooks from 'lib/hooks/api/schemas'; +import { act } from 'react-dom/test-utils'; +import { renderHook, waitFor } from '@testing-library/react'; +import { CompatibilityLevelCompatibilityEnum } from 'generated-sources'; +import { schemaVersion } from 'components/Schemas/Edit/__tests__/fixtures'; +import { + jsonSchema, + versionPayload, +} from 'components/Schemas/Details/__test__/fixtures'; +import { schemasPayload } from 'components/Schemas/List/__test__/fixtures'; + +const clusterName = 'test-cluster'; +const { subject } = schemaVersion; + +const schemasAPIUrl = `/api/clusters/${clusterName}/schemas`; +const schemasWithSubjectAPIUrl = `${schemasAPIUrl}/${subject}`; +const schemasAPILatestUrl = `${schemasWithSubjectAPIUrl}/latest`; +const schemasAPIVersionsUrl = `${schemasWithSubjectAPIUrl}/versions`; +const schemaCompatibilityUrl = `${schemasAPIUrl}/compatibility`; +const schemaCompatibilityWithSubjectUrl = `${schemasWithSubjectAPIUrl}/compatibility`; + +describe('Schema hooks', () => { + beforeEach(() => fetchMock.restore()); + + describe('Get Queries', () => { + describe('useGetSchemas', () => { + it('returns the correct data', async () => { + const mock = fetchMock.getOnce(schemasAPIUrl, schemasPayload); + const { result } = renderQueryHook(() => + hooks.useGetSchemas({ clusterName }) + ); + await expectQueryWorks(mock, result); + }); + }); + + describe('useGetLatestSchema', () => { + it('returns the correct data', async () => { + const mock = fetchMock.getOnce(schemasAPILatestUrl, schemaVersion); + const { result } = renderQueryHook(() => + hooks.useGetLatestSchema({ clusterName, subject }) + ); + await expectQueryWorks(mock, result); + }); + }); + + describe('useGetSchemasVersions', () => { + it('returns the correct data', async () => { + const mock = fetchMock.getOnce(schemasAPIVersionsUrl, versionPayload); + const { result } = renderQueryHook(() => + hooks.useGetSchemasVersions({ clusterName, subject }) + ); + await expectQueryWorks(mock, result); + }); + }); + + describe('useGetGlobalCompatibilityLayer', () => { + it('returns the correct data', async () => { + const mock = fetchMock.getOnce(schemaCompatibilityUrl, { + compatibility: CompatibilityLevelCompatibilityEnum.FULL, + }); + const { result } = renderQueryHook(() => + hooks.useGetGlobalCompatibilityLayer(clusterName) + ); + await expectQueryWorks(mock, result); + }); + }); + }); + + describe('Mutations', () => { + describe('useCreateSchema', () => { + it('returns the correct data', async () => { + const mock = fetchMock.postOnce(schemasAPIUrl, jsonSchema); + const { result } = renderHook( + () => hooks.useCreateSchema(clusterName), + { wrapper: TestQueryClientProvider } + ); + await act(async () => { + await result.current.mutateAsync(jsonSchema); + }); + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + expect(mock.calls()).toHaveLength(1); + }); + }); + + describe('useUpdateSchemaCompatibilityLayer', () => { + it('returns the correct data', async () => { + const mock = fetchMock.putOnce(schemaCompatibilityWithSubjectUrl, 200); + const { result } = renderHook( + () => + hooks.useUpdateSchemaCompatibilityLayer({ clusterName, subject }), + { wrapper: TestQueryClientProvider } + ); + await act(async () => { + await result.current.mutateAsync({ + compatibilityLevel: { + compatibility: CompatibilityLevelCompatibilityEnum.BACKWARD, + }, + }); + }); + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + expect(mock.calls()).toHaveLength(1); + }); + }); + + describe('useUpdateGlobalSchemaCompatibilityLevel', () => { + it('returns the correct data', async () => { + const mock = fetchMock.putOnce(schemaCompatibilityUrl, { + body: { + compatibility: CompatibilityLevelCompatibilityEnum.BACKWARD, + }, + }); + const { result } = renderHook( + () => hooks.useUpdateGlobalSchemaCompatibilityLevel(clusterName), + { wrapper: TestQueryClientProvider } + ); + + await act(() => + result.current.mutateAsync({ + compatibilityLevel: { + compatibility: CompatibilityLevelCompatibilityEnum.BACKWARD, + }, + }) + ); + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + expect(mock.calls()).toHaveLength(1); + }); + }); + + describe('useDeleteSchema', () => { + it('returns the correct data', async () => { + const mock = fetchMock.deleteOnce(schemasWithSubjectAPIUrl, 200); + const { result } = renderHook( + () => hooks.useDeleteSchema({ clusterName, subject }), + { wrapper: TestQueryClientProvider } + ); + await act(() => result.current.mutateAsync()); + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + expect(mock.calls()).toHaveLength(1); + }); + }); + }); +}); diff --git a/frontend/src/lib/hooks/api/__tests__/topics.spec.ts b/frontend/src/lib/hooks/api/__tests__/topics.spec.ts index 802368495..6007f3356 100644 --- a/frontend/src/lib/hooks/api/__tests__/topics.spec.ts +++ b/frontend/src/lib/hooks/api/__tests__/topics.spec.ts @@ -7,8 +7,8 @@ import { import * as hooks from 'lib/hooks/api/topics'; import fetchMock from 'fetch-mock'; import { externalTopicPayload, topicConfigPayload } from 'lib/fixtures/topics'; -import { TopicFormData, TopicFormDataRaw } from 'redux/interfaces'; import { CreateTopicMessage } from 'generated-sources'; +import { TopicFormData, TopicFormDataRaw } from 'lib/interfaces/topic'; const clusterName = 'test-cluster'; const topicName = 'test-topic'; diff --git a/frontend/src/lib/hooks/api/acl.ts b/frontend/src/lib/hooks/api/acl.ts index 498b34191..318768781 100644 --- a/frontend/src/lib/hooks/api/acl.ts +++ b/frontend/src/lib/hooks/api/acl.ts @@ -5,7 +5,7 @@ import { useQuery, useQueryClient, } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { showSuccessAlert } from 'lib/errorHandling'; import { CreateConsumerAcl, diff --git a/frontend/src/lib/hooks/api/brokers.ts b/frontend/src/lib/hooks/api/brokers.ts index 073e2eb75..064bb0d66 100644 --- a/frontend/src/lib/hooks/api/brokers.ts +++ b/frontend/src/lib/hooks/api/brokers.ts @@ -1,6 +1,6 @@ import { brokersApiClient as api } from 'lib/api'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { BrokerConfigItem } from 'generated-sources'; interface UpdateBrokerConfigProps { diff --git a/frontend/src/lib/hooks/api/clusters.ts b/frontend/src/lib/hooks/api/clusters.ts index 443363d3f..1d3f2c05d 100644 --- a/frontend/src/lib/hooks/api/clusters.ts +++ b/frontend/src/lib/hooks/api/clusters.ts @@ -1,6 +1,6 @@ import { clustersApiClient as api } from 'lib/api'; import { useQuery } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; export function useClusters() { return useQuery(['clusters'], () => api.getClusters(), { suspense: false }); diff --git a/frontend/src/lib/hooks/api/consumers.ts b/frontend/src/lib/hooks/api/consumers.ts index c0089e1f0..85ae048a9 100644 --- a/frontend/src/lib/hooks/api/consumers.ts +++ b/frontend/src/lib/hooks/api/consumers.ts @@ -1,6 +1,6 @@ import { consumerGroupsApiClient as api } from 'lib/api'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { ConsumerGroup, ConsumerGroupOffsetsReset, diff --git a/frontend/src/lib/hooks/api/kafkaConnect.ts b/frontend/src/lib/hooks/api/kafkaConnect.ts index 1d01d4919..c17b381e4 100644 --- a/frontend/src/lib/hooks/api/kafkaConnect.ts +++ b/frontend/src/lib/hooks/api/kafkaConnect.ts @@ -7,7 +7,7 @@ import { import { kafkaConnectApiClient as api } from 'lib/api'; import sortBy from 'lodash/sortBy'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { showSuccessAlert } from 'lib/errorHandling'; interface UseConnectorProps { diff --git a/frontend/src/lib/hooks/api/ksqlDb.tsx b/frontend/src/lib/hooks/api/ksqlDb.tsx index 366141e82..e562e7c2f 100644 --- a/frontend/src/lib/hooks/api/ksqlDb.tsx +++ b/frontend/src/lib/hooks/api/ksqlDb.tsx @@ -1,6 +1,6 @@ import { ksqlDbApiClient as api } from 'lib/api'; import { useMutation, useQueries } from '@tanstack/react-query'; -import { ClusterName } from 'redux/interfaces'; +import { ClusterName } from 'lib/interfaces/cluster'; import { BASE_PARAMS } from 'lib/constants'; import React from 'react'; import { fetchEventSource } from '@microsoft/fetch-event-source'; diff --git a/frontend/src/lib/hooks/api/schemas.ts b/frontend/src/lib/hooks/api/schemas.ts new file mode 100644 index 000000000..7082d180a --- /dev/null +++ b/frontend/src/lib/hooks/api/schemas.ts @@ -0,0 +1,177 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + GLOBAL_COMPATIBILITY_SCHEMAS_QUERY_KEY, + LATEST_SCHEMA_QUERY_KEY, + SCHEMA_QUERY_KEY, + SCHEMAS_VERSION_QUERY_KEY, +} from 'lib/queries'; +import { + CompatibilityLevel, + GetAllVersionsBySubjectRequest, + GetLatestSchemaRequest, + GetSchemasRequest, + NewSchemaSubject, + SchemaSubject, + SchemaSubjectsResponse, + UpdateGlobalSchemaCompatibilityLevelRequest, + UpdateSchemaCompatibilityLevelRequest, +} from 'generated-sources'; +import { schemasApiClient } from 'lib/api'; +import { ClusterName } from 'lib/interfaces/cluster'; + +export function useGetLatestSchema(param: GetLatestSchemaRequest) { + return useQuery({ + queryKey: [ + SCHEMA_QUERY_KEY, + LATEST_SCHEMA_QUERY_KEY, + param.clusterName, + param.subject, + ], + queryFn: () => schemasApiClient.getLatestSchema(param), + }); +} + +export function useGetSchemas({ + clusterName, + page, + perPage, + search, +}: GetSchemasRequest) { + return useQuery({ + queryKey: [SCHEMA_QUERY_KEY, clusterName, page, perPage, search], + queryFn: () => + schemasApiClient.getSchemas({ + clusterName, + page, + perPage, + search: search || undefined, + }), + }); +} + +export function useGetSchemasVersions({ + clusterName, + subject, +}: GetAllVersionsBySubjectRequest) { + return useQuery>({ + queryKey: [SCHEMAS_VERSION_QUERY_KEY, clusterName, subject], + queryFn: () => + schemasApiClient.getAllVersionsBySubject({ + clusterName, + subject, + }), + }); +} + +export function useGetGlobalCompatibilityLayer(clusterName: ClusterName) { + return useQuery({ + queryKey: [GLOBAL_COMPATIBILITY_SCHEMAS_QUERY_KEY, clusterName], + queryFn: () => + schemasApiClient.getGlobalSchemaCompatibilityLevel({ + clusterName, + }), + }); +} + +export function useCreateSchema(clusterName: ClusterName) { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ subject, schema, schemaType }) => + schemasApiClient.createNewSchema({ + clusterName, + newSchemaSubject: { subject, schema, schemaType }, + }), + onSuccess: () => { + return queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === SCHEMA_QUERY_KEY && + query.queryKey[1] === clusterName, + }); + }, + }); +} + +export function useUpdateSchemaCompatibilityLayer({ + clusterName, + subject, +}: { + clusterName: ClusterName; + subject: string; +}) { + const queryClient = useQueryClient(); + return useMutation< + void, + void, + Omit + >({ + mutationFn: ({ compatibilityLevel }) => + schemasApiClient.updateSchemaCompatibilityLevel({ + clusterName, + subject, + compatibilityLevel, + }), + onSuccess: () => { + return queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === SCHEMA_QUERY_KEY && + query.queryKey[1] === clusterName, + }); + }, + }); +} + +export function useUpdateGlobalSchemaCompatibilityLevel( + clusterName: ClusterName +) { + const queryClient = useQueryClient(); + + return useMutation< + void, + void, + Omit + >({ + mutationFn: ({ compatibilityLevel }) => + schemasApiClient.updateGlobalSchemaCompatibilityLevel({ + clusterName, + compatibilityLevel, + }), + onSuccess: () => { + return Promise.all([ + queryClient.invalidateQueries([ + GLOBAL_COMPATIBILITY_SCHEMAS_QUERY_KEY, + clusterName, + ]), + queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === SCHEMA_QUERY_KEY && + query.queryKey[1] === clusterName, + }), + ]); + }, + }); +} + +export function useDeleteSchema({ + clusterName, + subject, +}: { + clusterName: ClusterName; + subject: string; +}) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => + schemasApiClient.deleteSchema({ + clusterName, + subject, + }), + onSuccess: () => { + return queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === SCHEMA_QUERY_KEY && + query.queryKey[1] === clusterName, + }); + }, + }); +} diff --git a/frontend/src/lib/hooks/api/topicMessages.tsx b/frontend/src/lib/hooks/api/topicMessages.tsx index cf22148ac..e552c0dbf 100644 --- a/frontend/src/lib/hooks/api/topicMessages.tsx +++ b/frontend/src/lib/hooks/api/topicMessages.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useRef } from 'react'; import { fetchEventSource } from '@microsoft/fetch-event-source'; import { BASE_PARAMS, MESSAGES_PER_PAGE } from 'lib/constants'; -import { ClusterName, TopicName } from 'redux/interfaces'; import { GetSerdesRequest, PollingMode, @@ -17,6 +16,8 @@ import { useSearchParams } from 'react-router-dom'; import { MessagesFilterKeys } from 'lib/hooks/useMessagesFilters'; import { convertStrToPollingMode } from 'lib/hooks/filterUtils'; import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore'; +import { TopicName } from 'lib/interfaces/topic'; +import { ClusterName } from 'lib/interfaces/cluster'; interface UseTopicMessagesProps { clusterName: ClusterName; diff --git a/frontend/src/lib/hooks/api/topics.ts b/frontend/src/lib/hooks/api/topics.ts index 3f68a1709..b0aac312d 100644 --- a/frontend/src/lib/hooks/api/topics.ts +++ b/frontend/src/lib/hooks/api/topics.ts @@ -5,12 +5,6 @@ import { messagesApiClient, } from 'lib/api'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { - ClusterName, - TopicFormData, - TopicFormDataRaw, - TopicFormFormattedParams, -} from 'redux/interfaces'; import { CreateTopicMessage, GetTopicDetailsRequest, @@ -21,6 +15,12 @@ import { TopicUpdate, } from 'generated-sources'; import { showServerError, showSuccessAlert } from 'lib/errorHandling'; +import { ClusterName } from 'lib/interfaces/cluster'; +import { + TopicFormData, + TopicFormDataRaw, + TopicFormFormattedParams, +} from 'lib/interfaces/topic'; export const topicKeys = { all: (clusterName: ClusterName) => diff --git a/frontend/src/lib/hooks/filterUtils.ts b/frontend/src/lib/hooks/filterUtils.ts index 87482992e..9e9d23fc6 100644 --- a/frontend/src/lib/hooks/filterUtils.ts +++ b/frontend/src/lib/hooks/filterUtils.ts @@ -1,8 +1,8 @@ import { PollingMode } from 'generated-sources'; export const ModeOptions = [ - { value: PollingMode.LATEST, label: 'Oldest' }, - { value: PollingMode.EARLIEST, label: 'Newest' }, + { value: PollingMode.EARLIEST, label: 'Oldest' }, + { value: PollingMode.LATEST, label: 'Newest' }, { value: PollingMode.TAILING, label: 'Live' }, { value: PollingMode.FROM_OFFSET, label: 'From offset' }, { value: PollingMode.TO_OFFSET, label: 'To offset' }, diff --git a/frontend/src/lib/hooks/redux.ts b/frontend/src/lib/hooks/redux.ts deleted file mode 100644 index 40471e04a..000000000 --- a/frontend/src/lib/hooks/redux.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import { AppDispatch, RootState } from 'redux/interfaces'; - -export const useAppDispatch = () => useDispatch(); -export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/frontend/src/redux/interfaces/cluster.ts b/frontend/src/lib/interfaces/cluster.ts similarity index 100% rename from frontend/src/redux/interfaces/cluster.ts rename to frontend/src/lib/interfaces/cluster.ts diff --git a/frontend/src/redux/interfaces/schema.ts b/frontend/src/lib/interfaces/schema.ts similarity index 100% rename from frontend/src/redux/interfaces/schema.ts rename to frontend/src/lib/interfaces/schema.ts diff --git a/frontend/src/redux/interfaces/topic.ts b/frontend/src/lib/interfaces/topic.ts similarity index 76% rename from frontend/src/redux/interfaces/topic.ts rename to frontend/src/lib/interfaces/topic.ts index bdc25ee0c..126294a1f 100644 --- a/frontend/src/redux/interfaces/topic.ts +++ b/frontend/src/lib/interfaces/topic.ts @@ -1,10 +1,4 @@ -import { - Topic, - TopicConfig, - TopicCreation, - TopicMessage, - TopicMessageConsuming, -} from 'generated-sources'; +import { Topic, TopicConfig, TopicCreation } from 'generated-sources'; export type TopicName = Topic['name']; @@ -50,11 +44,3 @@ export interface TopicFormData { value: string; }[]; } - -export interface TopicMessagesState { - messages: TopicMessage[]; - phase?: string; - meta: TopicMessageConsuming; - messageEventType?: string; - isFetching: boolean; -} diff --git a/frontend/src/lib/paths.ts b/frontend/src/lib/paths.ts index 62b19568c..9f961b261 100644 --- a/frontend/src/lib/paths.ts +++ b/frontend/src/lib/paths.ts @@ -1,8 +1,10 @@ import { Broker, Connect, Connector } from 'generated-sources'; -import { ClusterName, SchemaName, TopicName } from 'redux/interfaces'; import { GIT_REPO_LINK } from './constants'; import { ConsumerGroupID } from './hooks/api/consumers'; +import { ClusterName } from './interfaces/cluster'; +import { SchemaName } from './interfaces/schema'; +import { TopicName } from './interfaces/topic'; export const gitCommitPath = (commit: string) => `${GIT_REPO_LINK}/commit/${commit}`; diff --git a/frontend/src/lib/queries.ts b/frontend/src/lib/queries.ts new file mode 100644 index 000000000..4bbf9e59a --- /dev/null +++ b/frontend/src/lib/queries.ts @@ -0,0 +1,8 @@ +// TODO all the other query key should be transferred here + +// SCHEMAS +export const SCHEMA_QUERY_KEY = 'schemas'; +export const LATEST_SCHEMA_QUERY_KEY = 'latest_schemas'; +export const SCHEMAS_VERSION_QUERY_KEY = 'schemas_version'; +export const GLOBAL_COMPATIBILITY_SCHEMAS_QUERY_KEY = + 'global_compatibility_schemas'; diff --git a/frontend/src/lib/testHelpers.tsx b/frontend/src/lib/testHelpers.tsx index 06fcfcad4..1a6732c06 100644 --- a/frontend/src/lib/testHelpers.tsx +++ b/frontend/src/lib/testHelpers.tsx @@ -6,7 +6,6 @@ import { Routes, } from 'react-router-dom'; import fetchMock from 'fetch-mock'; -import { Provider } from 'react-redux'; import { ThemeProvider } from 'styled-components'; import { theme } from 'theme/theme'; import { @@ -15,10 +14,6 @@ import { RenderOptions, waitFor, } from '@testing-library/react'; -import { AnyAction, Store } from 'redux'; -import { RootState } from 'redux/interfaces'; -import { configureStore } from '@reduxjs/toolkit'; -import rootReducer from 'redux/reducers'; import { QueryClient, QueryClientProvider, @@ -32,8 +27,6 @@ import { UserInfoRolesAccessContext } from 'components/contexts/UserInfoRolesAcc import { RolesType, modifyRolesData } from './permissions'; interface CustomRenderOptions extends Omit { - preloadedState?: Partial; - store?: Store, AnyAction>; initialEntries?: MemoryRouterProps['initialEntries']; userInfo?: { roles?: RolesType; @@ -107,11 +100,6 @@ const TestUserInfoProvider: React.FC< const customRender = ( ui: ReactElement, { - preloadedState, - store = configureStore({ - reducer: rootReducer, - preloadedState, - }), initialEntries, userInfo, globalSettings, @@ -129,14 +117,12 @@ const customRender = ( - - -
- {children} - -
-
-
+ +
+ {children} + +
+
diff --git a/frontend/src/redux/interfaces/consumerGroup.ts b/frontend/src/redux/interfaces/consumerGroup.ts deleted file mode 100644 index 45412e264..000000000 --- a/frontend/src/redux/interfaces/consumerGroup.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - ConsumerGroup, - ConsumerGroupOffsetsResetType, -} from 'generated-sources'; - -import { ClusterName } from './cluster'; - -export interface ConsumerGroupResetOffsetRequestParams { - clusterName: ClusterName; - consumerGroupID: ConsumerGroup['groupId']; - requestBody: { - topic: string; - resetType: ConsumerGroupOffsetsResetType; - partitionsOffsets?: { offset: string; partition: number }[]; - resetToTimestamp?: Date; - partitions: number[]; - }; -} diff --git a/frontend/src/redux/interfaces/index.ts b/frontend/src/redux/interfaces/index.ts deleted file mode 100644 index 0c8ddf234..000000000 --- a/frontend/src/redux/interfaces/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import rootReducer from 'redux/reducers'; -import { store } from 'redux/store'; - -export * from './topic'; -export * from './cluster'; -export * from './consumerGroup'; -export * from './schema'; -export * from './loader'; - -export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; diff --git a/frontend/src/redux/interfaces/loader.ts b/frontend/src/redux/interfaces/loader.ts deleted file mode 100644 index 67066399e..000000000 --- a/frontend/src/redux/interfaces/loader.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { AsyncRequestStatus } from 'lib/constants'; - -export interface LoaderSliceState { - [key: string]: AsyncRequestStatus; -} diff --git a/frontend/src/redux/reducers/index.ts b/frontend/src/redux/reducers/index.ts deleted file mode 100644 index 6385e0710..000000000 --- a/frontend/src/redux/reducers/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { combineReducers } from '@reduxjs/toolkit'; -import loader from 'redux/reducers/loader/loaderSlice'; -import schemas from 'redux/reducers/schemas/schemasSlice'; - -export default combineReducers({ - loader, - schemas, -}); diff --git a/frontend/src/redux/reducers/loader/loaderSlice.ts b/frontend/src/redux/reducers/loader/loaderSlice.ts deleted file mode 100644 index 294b35911..000000000 --- a/frontend/src/redux/reducers/loader/loaderSlice.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { - UnknownAsyncThunkFulfilledAction, - UnknownAsyncThunkPendingAction, - UnknownAsyncThunkRejectedAction, -} from '@reduxjs/toolkit/dist/matchers'; -import { AsyncRequestStatus } from 'lib/constants'; -import { LoaderSliceState } from 'redux/interfaces'; - -const initialState: LoaderSliceState = {}; - -const loaderSlice = createSlice({ - name: 'loader', - initialState, - reducers: { - resetLoaderById: ( - state: LoaderSliceState, - { payload }: PayloadAction - ) => { - delete state[payload]; - }, - }, - extraReducers: (builder) => { - builder - .addMatcher( - (action): action is UnknownAsyncThunkPendingAction => - action.type.endsWith('/pending'), - (state, { type }) => { - state[type.replace('/pending', '')] = AsyncRequestStatus.pending; - } - ) - .addMatcher( - (action): action is UnknownAsyncThunkFulfilledAction => - action.type.endsWith('/fulfilled'), - (state, { type }) => { - state[type.replace('/fulfilled', '')] = AsyncRequestStatus.fulfilled; - } - ) - .addMatcher( - (action): action is UnknownAsyncThunkRejectedAction => - action.type.endsWith('/rejected'), - (state, { type }) => { - state[type.replace('/rejected', '')] = AsyncRequestStatus.rejected; - } - ); - }, -}); - -export const { resetLoaderById } = loaderSlice.actions; - -export default loaderSlice.reducer; diff --git a/frontend/src/redux/reducers/loader/selectors.ts b/frontend/src/redux/reducers/loader/selectors.ts deleted file mode 100644 index 2decd3446..000000000 --- a/frontend/src/redux/reducers/loader/selectors.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RootState } from 'redux/interfaces'; -import { AsyncRequestStatus } from 'lib/constants'; - -export const createFetchingSelector = (action: string) => (state: RootState) => - state.loader[action] || AsyncRequestStatus.initial; diff --git a/frontend/src/redux/reducers/schemas/schemasSlice.ts b/frontend/src/redux/reducers/schemas/schemasSlice.ts deleted file mode 100644 index 018775ced..000000000 --- a/frontend/src/redux/reducers/schemas/schemasSlice.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { - createAsyncThunk, - createEntityAdapter, - createSelector, - createSlice, -} from '@reduxjs/toolkit'; -import { - SchemaSubject, - SchemaSubjectsResponse, - GetSchemasRequest, - GetLatestSchemaRequest, -} from 'generated-sources'; -import { schemasApiClient } from 'lib/api'; -import { AsyncRequestStatus } from 'lib/constants'; -import { getResponse, showServerError } from 'lib/errorHandling'; -import { ClusterName, RootState } from 'redux/interfaces'; -import { createFetchingSelector } from 'redux/reducers/loader/selectors'; - -export const SCHEMA_LATEST_FETCH_ACTION = 'schemas/latest/fetch'; -export const fetchLatestSchema = createAsyncThunk< - SchemaSubject, - GetLatestSchemaRequest ->(SCHEMA_LATEST_FETCH_ACTION, async (schemaParams, { rejectWithValue }) => { - try { - return await schemasApiClient.getLatestSchema(schemaParams); - } catch (error) { - showServerError(error as Response); - return rejectWithValue(await getResponse(error as Response)); - } -}); - -export const SCHEMAS_FETCH_ACTION = 'schemas/fetch'; -export const fetchSchemas = createAsyncThunk< - SchemaSubjectsResponse, - GetSchemasRequest ->( - SCHEMAS_FETCH_ACTION, - async ({ clusterName, page, perPage, search }, { rejectWithValue }) => { - try { - return await schemasApiClient.getSchemas({ - clusterName, - page, - perPage, - search: search || undefined, - }); - } catch (error) { - showServerError(error as Response); - return rejectWithValue(await getResponse(error as Response)); - } - } -); - -export const SCHEMAS_VERSIONS_FETCH_ACTION = 'schemas/versions/fetch'; -export const fetchSchemaVersions = createAsyncThunk< - SchemaSubject[], - { clusterName: ClusterName; subject: SchemaSubject['subject'] } ->( - SCHEMAS_VERSIONS_FETCH_ACTION, - async ({ clusterName, subject }, { rejectWithValue }) => { - try { - return await schemasApiClient.getAllVersionsBySubject({ - clusterName, - subject, - }); - } catch (error) { - showServerError(error as Response); - return rejectWithValue(await getResponse(error as Response)); - } - } -); - -const schemaVersionsAdapter = createEntityAdapter({ - selectId: ({ id }) => id, - sortComparer: (a, b) => b.id - a.id, -}); -const schemasAdapter = createEntityAdapter({ - selectId: ({ subject }) => subject, -}); - -const SCHEMAS_PAGE_COUNT = 1; - -const initialState = { - totalPages: SCHEMAS_PAGE_COUNT, - ...schemasAdapter.getInitialState(), - versions: { - ...schemaVersionsAdapter.getInitialState(), - latest: null, - }, -}; - -const schemasSlice = createSlice({ - name: 'schemas', - initialState, - reducers: { - schemaAdded: schemasAdapter.addOne, - schemaUpdated: schemasAdapter.upsertOne, - }, - extraReducers: (builder) => { - builder.addCase(fetchSchemas.fulfilled, (state, { payload }) => { - state.totalPages = payload.pageCount || SCHEMAS_PAGE_COUNT; - schemasAdapter.setAll(state, payload.schemas || []); - }); - builder.addCase(fetchLatestSchema.fulfilled, (state, { payload }) => { - state.versions.latest = payload; - }); - builder.addCase(fetchSchemaVersions.fulfilled, (state, { payload }) => { - schemaVersionsAdapter.setAll(state.versions, payload); - }); - }, -}); - -export const { selectAll: selectAllSchemas } = - schemasAdapter.getSelectors((state) => state.schemas); - -export const { selectAll: selectAllSchemaVersions } = - schemaVersionsAdapter.getSelectors( - (state) => state.schemas.versions - ); - -const getSchemaVersions = (state: RootState) => state.schemas.versions; -export const getSchemaLatest = createSelector( - getSchemaVersions, - (state) => state.latest -); - -export const { schemaAdded, schemaUpdated } = schemasSlice.actions; - -export const getAreSchemasFulfilled = createSelector( - createFetchingSelector(SCHEMAS_FETCH_ACTION), - (status) => status === AsyncRequestStatus.fulfilled -); - -export const getAreSchemaLatestFulfilled = createSelector( - createFetchingSelector(SCHEMA_LATEST_FETCH_ACTION), - (status) => status === AsyncRequestStatus.fulfilled -); -export const getAreSchemaLatestRejected = createSelector( - createFetchingSelector(SCHEMA_LATEST_FETCH_ACTION), - (status) => status === AsyncRequestStatus.rejected -); - -export const getAreSchemaVersionsFulfilled = createSelector( - createFetchingSelector(SCHEMAS_VERSIONS_FETCH_ACTION), - (status) => status === AsyncRequestStatus.fulfilled -); - -export default schemasSlice.reducer; diff --git a/frontend/src/redux/store/index.ts b/frontend/src/redux/store/index.ts deleted file mode 100644 index 7d37a77f4..000000000 --- a/frontend/src/redux/store/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit'; -import { RootState } from 'redux/interfaces'; -import rootReducer from 'redux/reducers'; - -export const store = configureStore({ - reducer: rootReducer, -}); diff --git a/pom.xml b/pom.xml index 26eeaf215..1b4bdb599 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ 2.14.0 3.5.2 1.5.5.Final - 1.18.30 + 1.18.32 3.23.3 2.13.9 2.0