diff --git a/.github/actions/post-deploy-smoke-test/action.yml b/.github/actions/post-deploy-smoke-test/action.yml new file mode 100644 index 0000000000..35236a7437 --- /dev/null +++ b/.github/actions/post-deploy-smoke-test/action.yml @@ -0,0 +1,19 @@ +name: Smoke test post deploy +description: Invoke a script that visits a deploy smoke check page that displays whether the backend / db are healthy. +inputs: + base_domain_name: + description: The domain where the application is deployed (e.g. "simplereport.gov" or "test.simplereport.gov") + required: true +runs: + using: composite + steps: + - name: create env file + shell: bash + working-directory: frontend + run: | + touch .env + echo REACT_APP_BASE_URL=${{ inputs.base_domain_name }}>> .env.production.local + - name: Run smoke test script + shell: bash + working-directory: frontend + run: yarn smoke:deploy:ci \ No newline at end of file diff --git a/.github/workflows/deployProd.yml b/.github/workflows/deployProd.yml index 53249ea09a..2b5f99b51a 100644 --- a/.github/workflows/deployProd.yml +++ b/.github/workflows/deployProd.yml @@ -98,3 +98,5 @@ jobs: :siren-gif: Deploy to ${{ env.DEPLOY_ENV }} failed. ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} :siren-gif: webhook_url: ${{ secrets.SR_ALERTS_SLACK_WEBHOOK_URL }} user_map: $${{ secrets.SR_ALERTS_GITHUB_SLACK_MAP }} + +# a post-prod health check workflow is defined in smokeTestDeployProd. See the Alert response wiki page for more details \ No newline at end of file diff --git a/.github/workflows/smokeTestDeployProd.yml b/.github/workflows/smokeTestDeployProd.yml new file mode 100644 index 0000000000..586fe3df79 --- /dev/null +++ b/.github/workflows/smokeTestDeployProd.yml @@ -0,0 +1,48 @@ +name: Smoke test deploy Prod +run-name: Smoke test the deploy for prod by @${{ github.actor }} + +on: + workflow_run: + workflows: [ "Deploy Prod" ] + types: + - completed + +env: + NODE_VERSION: 18 + +jobs: + smoke-test-front-and-back-end: + runs-on: ubuntu-latest + environment: Production + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{env.NODE_VERSION}} + - name: Cache yarn + uses: actions/cache@v3.3.2 + with: + path: ~/.cache/yarn + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + - name: Set up dependencies + working-directory: frontend + run: yarn install --prefer-offline + - name: Smoke test the env + uses: ./.github/actions/post-deploy-smoke-test + with: + base_domain_name: ${{ vars.BASE_DOMAIN_NAME }} + slack_alert: + runs-on: ubuntu-latest + if: failure() + needs: [ smoke-test-front-and-back-end ] + steps: + - uses: actions/checkout@v4 + - name: Send alert to Slack + uses: ./.github/actions/slack-message + with: + username: ${{ github.actor }} + description: | + :siren-gif: Post-deploy smoke test couldn't verify that the frontend is talking to the backend. ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} :siren-gif: + webhook_url: ${{ secrets.SR_ALERTS_SLACK_WEBHOOK_URL }} + user_map: $${{ secrets.SR_ALERTS_GITHUB_SLACK_MAP }} \ No newline at end of file diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java new file mode 100644 index 0000000000..0a8cb830b2 --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/heathcheck/BackendAndDatabaseHealthIndicator.java @@ -0,0 +1,41 @@ +package gov.cdc.usds.simplereport.api.heathcheck; + +import com.okta.sdk.resource.client.ApiException; +import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; +import gov.cdc.usds.simplereport.idp.repository.OktaRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.exception.JDBCConnectionException; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component("backend-and-db-smoke-test") +@Slf4j +@RequiredArgsConstructor +public class BackendAndDatabaseHealthIndicator implements HealthIndicator { + private final FeatureFlagRepository _ffRepo; + private final OktaRepository _oktaRepo; + public static final String ACTIVE_LITERAL = "ACTIVE"; + + @Override + public Health health() { + try { + _ffRepo.findAll(); + String oktaStatus = _oktaRepo.getApplicationStatusForHealthCheck(); + + if (!ACTIVE_LITERAL.equals(oktaStatus)) { + log.info("Okta status didn't return ACTIVE, instead returned " + oktaStatus); + return Health.down().build(); + } + + return Health.up().build(); + } catch (JDBCConnectionException e) { + return Health.down().build(); + // Okta API call errored + } catch (ApiException e) { + log.info(e.getMessage()); + return Health.down().build(); + } + } +} diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/config/SecurityConfiguration.java b/backend/src/main/java/gov/cdc/usds/simplereport/config/SecurityConfiguration.java index 15fdeee263..07a737850a 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/config/SecurityConfiguration.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/config/SecurityConfiguration.java @@ -1,6 +1,7 @@ package gov.cdc.usds.simplereport.config; import com.okta.spring.boot.oauth.Okta; +import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; import gov.cdc.usds.simplereport.service.model.IdentityAttributes; import gov.cdc.usds.simplereport.service.model.IdentitySupplier; import lombok.extern.slf4j.Slf4j; @@ -57,6 +58,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .requestMatchers(EndpointRequest.to(InfoEndpoint.class)) .permitAll() + .requestMatchers(EndpointRequest.to(BackendAndDatabaseHealthIndicator.class)) + .permitAll() // Patient experience authorization is handled in PatientExperienceController // If this configuration changes, please update the documentation on both sides .requestMatchers(HttpMethod.POST, WebConfiguration.PATIENT_EXPERIENCE) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java index d9ef3b5fd8..883963df9c 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java @@ -1,5 +1,7 @@ package gov.cdc.usds.simplereport.idp.repository; +import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; + import com.okta.sdk.resource.model.UserStatus; import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; import gov.cdc.usds.simplereport.api.model.errors.ConflictingUserException; @@ -431,4 +433,9 @@ public Integer getUsersInSingleFacility(Facility facility) { return accessCount; } + + @Override + public String getApplicationStatusForHealthCheck() { + return ACTIVE_LITERAL; + } } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index 7c61c63d3f..be897fd1cf 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -692,6 +692,11 @@ public PartialOktaUser findUser(String username) { .build(); } + @Override + public String getApplicationStatusForHealthCheck() { + return app.getStatus().toString(); + } + private Optional getOrganizationRoleClaimsFromAuthorities( Collection authorities) { List claims = extractor.convertClaims(authorities); diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java index 92cf4c1dfa..fc488573b7 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java @@ -76,4 +76,6 @@ List updateUserPrivilegesAndGroupAccess( Integer getUsersInSingleFacility(Facility facility); PartialOktaUser findUser(String username); + + String getApplicationStatusForHealthCheck(); } diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index 1df6cb8e0a..8f792d8cc0 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -78,6 +78,7 @@ management: endpoint.health.probes.enabled: true endpoint.info.enabled: true endpoints.web.exposure.include: health, info + endpoint.health.show-components: always okta: oauth2: issuer: https://hhs-prime.okta.com/oauth2/default diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java new file mode 100644 index 0000000000..ca35828467 --- /dev/null +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/healthcheck/BackendAndDatabaseHealthIndicatorTest.java @@ -0,0 +1,52 @@ +package gov.cdc.usds.simplereport.api.healthcheck; + +import static gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator.ACTIVE_LITERAL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; +import gov.cdc.usds.simplereport.db.repository.BaseRepositoryTest; +import gov.cdc.usds.simplereport.db.repository.FeatureFlagRepository; +import gov.cdc.usds.simplereport.idp.repository.OktaRepository; +import java.sql.SQLException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.hibernate.exception.JDBCConnectionException; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.mock.mockito.SpyBean; + +@RequiredArgsConstructor +@EnableConfigurationProperties +class BackendAndDatabaseHealthIndicatorTest extends BaseRepositoryTest { + + @SpyBean private FeatureFlagRepository mockFeatureFlagRepo; + @SpyBean private OktaRepository mockOktaRepo; + + @Autowired private BackendAndDatabaseHealthIndicator indicator; + + @Test + void health_succeedsWhenReposDoesntThrow() { + when(mockFeatureFlagRepo.findAll()).thenReturn(List.of()); + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn(ACTIVE_LITERAL); + + assertThat(indicator.health()).isEqualTo(Health.up().build()); + } + + @Test + void health_failsWhenFeatureFlagRepoDoesntThrow() { + JDBCConnectionException dbConnectionException = + new JDBCConnectionException( + "connection issue", new SQLException("some reason", "some state")); + when(mockFeatureFlagRepo.findAll()).thenThrow(dbConnectionException); + assertThat(indicator.health()).isEqualTo(Health.down().build()); + } + + @Test + void health_failsWhenOktaRepoDoesntReturnActive() { + when(mockOktaRepo.getApplicationStatusForHealthCheck()).thenReturn("INACTIVE"); + assertThat(indicator.health()).isEqualTo(Health.down().build()); + } +} diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java index 30e8144768..3ac3f7dd7d 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/test_util/SliceTestConfiguration.java @@ -5,6 +5,7 @@ import gov.cdc.usds.simplereport.api.CurrentOrganizationRolesContextHolder; import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; import gov.cdc.usds.simplereport.api.WebhookContextHolder; +import gov.cdc.usds.simplereport.api.heathcheck.BackendAndDatabaseHealthIndicator; import gov.cdc.usds.simplereport.api.pxp.CurrentPatientContextHolder; import gov.cdc.usds.simplereport.config.AuditingConfig; import gov.cdc.usds.simplereport.config.AuthorizationProperties; @@ -100,7 +101,8 @@ CurrentTenantDataAccessContextHolder.class, WebhookContextHolder.class, TenantDataAccessService.class, - PatientSelfRegistrationLinkService.class + PatientSelfRegistrationLinkService.class, + BackendAndDatabaseHealthIndicator.class }) @EnableConfigurationProperties({InitialSetupProperties.class, AuthorizationProperties.class}) public class SliceTestConfiguration { diff --git a/frontend/deploy-smoke.js b/frontend/deploy-smoke.js new file mode 100644 index 0000000000..966774839b --- /dev/null +++ b/frontend/deploy-smoke.js @@ -0,0 +1,35 @@ +// Script that does a simple Selenium scrape of +// - A frontend page with a simple status message that hits a health check backend +// endpoint which does a simple ping to a non-sensitive DB table to verify +// all the connections are good. +// https://github.com/CDCgov/prime-simplereport/pull/7057 + +require("dotenv").config(); +let { Builder } = require("selenium-webdriver"); +const Chrome = require("selenium-webdriver/chrome"); + +const appUrl = process.env.REACT_APP_BASE_URL.includes("localhost") + ? `${process.env.REACT_APP_BASE_URL}/health/deploy-smoke-test` + : `${process.env.REACT_APP_BASE_URL}/app/health/deploy-smoke-test`; + +console.log(`Running smoke test for ${appUrl}`); +const options = new Chrome.Options(); +const driver = new Builder() + .forBrowser("chrome") + .setChromeOptions(options.addArguments("--headless=new")) + .build(); +driver + .navigate() + .to(`${appUrl}`) + .then(() => { + let value = driver.findElement({ id: "root" }).getText(); + return value; + }) + .then((value) => { + driver.quit(); + return value; + }) + .then((value) => { + if (value.includes("success")) process.exit(0); + process.exit(1); + }); diff --git a/frontend/package.json b/frontend/package.json index 5534b1fb4b..dc09e3edf0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -75,7 +75,9 @@ "storybook": "yarn create-storybook-public && SASS_PATH=$(cd ./node_modules && pwd):$(cd ./node_modules/@uswds && pwd):$(cd ./node_modules/@uswds/uswds/packages && pwd):$(cd ./src/scss && pwd) storybook dev -p 6006 -s ../storybook_public", "build-storybook": "yarn create-storybook-public && REACT_APP_BACKEND_URL=http://localhost:8080 SASS_PATH=$(cd ./node_modules && pwd):$(cd ./node_modules/@uswds && pwd):$(cd ./node_modules/@uswds/uswds/packages && pwd):$(cd ./src/scss && pwd) storybook build -s storybook_public", "maintenance:start": "[ -z \"$MAINTENANCE_MESSAGE\" ] && echo \"MAINTENANCE_MESSAGE must be set!\" || (echo $MAINTENANCE_MESSAGE > maintenance.json && yarn maintenance:deploy && rm maintenance.json)", - "maintenance:deploy": "[ -z \"$MAINTENANCE_ENV\" ] && echo \"MAINTENANCE_ENV must be set!\" || az storage blob upload -f maintenance.json -n maintenance.json -c '$web' --account-name simplereport${MAINTENANCE_ENV}app --overwrite" + "maintenance:deploy": "[ -z \"$MAINTENANCE_ENV\" ] && echo \"MAINTENANCE_ENV must be set!\" || az storage blob upload -f maintenance.json -n maintenance.json -c '$web' --account-name simplereport${MAINTENANCE_ENV}app --overwrite", + "smoke:deploy:local": "node -r dotenv/config deploy-smoke.js dotenv_config_path=.env.local", + "smoke:deploy:ci": "node -r dotenv/config deploy-smoke.js dotenv_config_path=.env.production.local" }, "prettier": { "singleQuote": false @@ -205,6 +207,7 @@ "chromatic": "^6.10.2", "dayjs": "^1.10.7", "depcheck": "^1.4.3", + "dotenv": "^16.3.1", "eslint-config-prettier": "^8.8.0", "eslint-plugin-graphql": "^4.0.0", "eslint-plugin-import": "^2.29.0", @@ -224,6 +227,7 @@ "prettier": "^2.8.4", "redux-mock-store": "^1.5.4", "sass": "^1.63.6", + "selenium-webdriver": "^4.16.0", "storybook": "^7.5.2", "storybook-addon-apollo-client": "^5.0.0", "stylelint": "^13.13.1", diff --git a/frontend/src/app/DeploySmokeTest.test.tsx b/frontend/src/app/DeploySmokeTest.test.tsx new file mode 100644 index 0000000000..0d3a8cbde0 --- /dev/null +++ b/frontend/src/app/DeploySmokeTest.test.tsx @@ -0,0 +1,30 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import { FetchMock } from "jest-fetch-mock"; + +import DeploySmokeTest from "./DeploySmokeTest"; + +describe("DeploySmokeTest", () => { + beforeEach(() => { + (fetch as FetchMock).resetMocks(); + }); + + it("renders success when returned from the API endpoint", async () => { + (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "UP" })); + + render(); + await waitFor(() => + expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + ); + expect(screen.getByText("Status returned success :)")); + }); + + it("renders failure when returned from the API endpoint", async () => { + (fetch as FetchMock).mockResponseOnce(JSON.stringify({ status: "DOWN" })); + + render(); + await waitFor(() => + expect(screen.queryByText("Status loading...")).not.toBeInTheDocument() + ); + expect(screen.getByText("Status returned failure :(")); + }); +}); diff --git a/frontend/src/app/DeploySmokeTest.tsx b/frontend/src/app/DeploySmokeTest.tsx new file mode 100644 index 0000000000..1ddcbc3e9e --- /dev/null +++ b/frontend/src/app/DeploySmokeTest.tsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; + +import FetchClient from "./utils/api"; + +const api = new FetchClient(undefined, { mode: "cors" }); +const DeploySmokeTest = (): JSX.Element => { + const [success, setSuccess] = useState(); + useEffect(() => { + api + .getRequest("/actuator/health/backend-and-db-smoke-test") + .then((response) => { + const status = JSON.parse(response); + if (status.status === "UP") return setSuccess(true); + setSuccess(false); + }) + .catch((e) => { + console.error(e); + setSuccess(false); + }); + }, []); + + if (success === undefined) return <>Status loading...; + if (success) return <> Status returned success :) ; + return <> Status returned failure :( ; +}; + +export default DeploySmokeTest; diff --git a/frontend/src/app/HealthChecks.tsx b/frontend/src/app/HealthChecks.tsx index 608113e171..1f72c3a5b1 100644 --- a/frontend/src/app/HealthChecks.tsx +++ b/frontend/src/app/HealthChecks.tsx @@ -1,5 +1,7 @@ import { Route, Routes } from "react-router-dom"; +import DeploySmokeTest from "./DeploySmokeTest"; + const HealthChecks = () => ( pong} /> @@ -7,6 +9,7 @@ const HealthChecks = () => ( path="commit" element={
{process.env.REACT_APP_CURRENT_COMMIT}
} /> + } />
); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2667cfd408..4f9bc8205f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -9072,7 +9072,7 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dotenv@^16.0.0: +dotenv@^16.0.0, dotenv@^16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== @@ -11098,6 +11098,11 @@ ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + immer@^9.0.7: version "9.0.16" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.16.tgz#8e7caab80118c2b54b37ad43e05758cdefad0198" @@ -12874,6 +12879,16 @@ jsonpointer@^5.0.0: array-includes "^3.1.5" object.assign "^4.1.3" +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + jwt-decode@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" @@ -12952,6 +12967,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lilconfig@^2.0.3, lilconfig@^2.0.5, lilconfig@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" @@ -14207,6 +14229,11 @@ pako@~0.2.0: resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -16394,6 +16421,15 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== +selenium-webdriver@^4.16.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz#2f1a2426d876aa389d1c937b00f034c2c7808360" + integrity sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA== + dependencies: + jszip "^3.10.1" + tmp "^0.2.1" + ws ">=8.14.2" + selfsigned@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" @@ -17547,6 +17583,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -18879,6 +18922,11 @@ ws@8.14.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.1.tgz#4b9586b4f70f9e6534c7bb1d3dc0baa8b8cf01e0" integrity sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A== +ws@>=8.14.2: + version "8.15.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.0.tgz#db080a279260c5f532fc668d461b8346efdfcf86" + integrity sha512-H/Z3H55mrcrgjFwI+5jKavgXvwQLtfPCUEp6pi35VhoB0pfcHnSoyuTzkBEZpzq49g1193CUEwIvmsjcotenYw== + "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.6: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"