From 52300a3339860c730cab50637ad6350b0a1a7ea6 Mon Sep 17 00:00:00 2001 From: Meis Date: Wed, 6 Nov 2024 09:51:26 -0700 Subject: [PATCH 1/9] fix: [ViewInstitutionProfile] Types of FI: Avoid blank display for empty array --- .../Filing/ViewInstitutionProfile/IdentifyingInformation.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/Filing/ViewInstitutionProfile/IdentifyingInformation.tsx b/src/pages/Filing/ViewInstitutionProfile/IdentifyingInformation.tsx index b676eebc2..0921222fd 100644 --- a/src/pages/Filing/ViewInstitutionProfile/IdentifyingInformation.tsx +++ b/src/pages/Filing/ViewInstitutionProfile/IdentifyingInformation.tsx @@ -45,7 +45,9 @@ export function IdentifyingInformation({ }, ); - const institutionTypeNamesString = institutionTypeNamesArray?.join(', '); + const institutionTypeNamesString = institutionTypeNamesArray?.length + ? institutionTypeNamesArray.join(', ') + : null; return ( From 04f0135723f23e093c4ccc164660da968b677157 Mon Sep 17 00:00:00 2001 From: Meis Date: Wed, 6 Nov 2024 09:52:16 -0700 Subject: [PATCH 2/9] e2e: [InstitutionProfile] Update tests to match latest language changes --- .../InstitutionProfile.spec.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts index def884cde..1de4f306e 100644 --- a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts +++ b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts @@ -78,7 +78,7 @@ test('Institution Profile Page', async ({ await expect( page.locator('#main h3').nth(3).locator('xpath=../p[1]'), 'LEI status is correct', - ).toContainText('Active'); + ).toContainText('Issued'); }); // Email domains @@ -147,8 +147,14 @@ test('Institution Profile Page', async ({ ).toContainText('Type of financial institution'); await expect( page.locator('#main h3').nth(8).locator('xpath=../p[1]'), - 'Type is correct', - ).toContainText('Not available'); + 'Type is correct when not provided', + ).toContainText('Not provided'); + await expect( + page.locator('#main h3').nth(8).locator('xpath=../div/span[2]/p'), + 'Alert for unprovided Type of FI', + ).toContainText( + 'You must provide your type of financial institution to file.', + ); }); }); From 5078eb37dec8954f8e441fa71ae7459311833781 Mon Sep 17 00:00:00 2001 From: Meis Date: Wed, 6 Nov 2024 10:05:27 -0700 Subject: [PATCH 3/9] deps: Upgrade to Node v22 --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 3f430af82..53d1c14db 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18 +v22 From 86dcf8c38dd7cac4c186a72e43d0f1e180e7c510 Mon Sep 17 00:00:00 2001 From: Meis Date: Fri, 8 Nov 2024 11:26:23 -0700 Subject: [PATCH 4/9] task: [README] Instruct to use Node v22 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ebac233b3..603c17e4a 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ The frontend of the Small Business Lending Data Filing Platform. - Write unit and integration tests with [Vitest](https://vitest.dev/) and [React Testing Library](https://testing-library.com/). - Write e2e tests with [Playwright](https://playwright.dev/). -## Getting started (Updated 12/13/2023) +## Getting started (Updated 11/08/2024) -1. Install Node v18.2+: `nvm install 18 && nvm use 18` +1. Install Node v22+: `nvm install 22 && nvm use 22` 1. Enable [corepack](https://yarnpkg.com/getting-started/install): `corepack enable`. 1. [Docker](https://docs.docker.com/get-docker/) engine version 1.13.0+ with docker compose version 3.0+ support needs to be installed to run all the containerized support services. 1. Have the six repos [sbl-frontend](https://github.com/cfpb/sbl-frontend), [sbl-project](https://github.com/cfpb/sbl-project), [regtech-user-fi-management](https://github.com/cfpb/regtech-user-fi-management), [sbl-filing-api](https://github.com/cfpb/sbl-filing-api/), [regtech-mail-api](https://github.com/cfpb/regtech-mail-api), and [regtech-cleanup-api](https://github.com/cfpb/regtech-cleanup-api) as **sibling directories**. From f449e968912591069515e55e6001f98129b2491a Mon Sep 17 00:00:00 2001 From: Meis Date: Fri, 8 Nov 2024 11:26:57 -0700 Subject: [PATCH 5/9] task: [package.json] Update node engine to Node v22+ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dad21a382..048b2cdb7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "version": "0.0.0", "engines": { - "node": ">=18.20.4 <19.0.0", + "node": ">=22", "yarn": ">=3.6.1" }, "scripts": { From c16d2062180142675dcba4ee5ccfa96476d78e81 Mon Sep 17 00:00:00 2001 From: Meis Date: Fri, 8 Nov 2024 11:28:58 -0700 Subject: [PATCH 6/9] task: [Github workflows] Update to use Node v22 --- .github/workflows/.pre.yml | 2 +- .github/workflows/lint.yml | 8 ++++---- .github/workflows/test.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/.pre.yml b/.github/workflows/.pre.yml index a91c9cdd4..88349d192 100644 --- a/.github/workflows/.pre.yml +++ b/.github/workflows/.pre.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock - name: Clean Yarn cache diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ce1a0258c..620fa3866 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock @@ -38,7 +38,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock @@ -58,7 +58,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock @@ -78,7 +78,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a12214a28..83aa64289 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: - uses: ./.github/actions/setvars - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock @@ -30,7 +30,7 @@ jobs: - uses: ./.github/actions/setvars - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'yarn' cache-dependency-path: ./yarn.lock From e4bee3d9e5e389ec227c28ca32941b6cd8543754 Mon Sep 17 00:00:00 2001 From: Meis Date: Wed, 13 Nov 2024 11:42:41 -0700 Subject: [PATCH 7/9] task: [Dockerfile] Update base Node image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5fc407dad..746bd7737 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/cfpb/regtech/sbl/nodejs-alpine:3.20 as build-stage +FROM ghcr.io/cfpb/regtech/sbl/node-js-alpine:3.20 as build-stage WORKDIR /usr/src/app ARG DOCKER_TAG="latest" From 5b7565727d8b1463fc132122bbc6c8e8c9f09cc1 Mon Sep 17 00:00:00 2001 From: Meis Date: Thu, 14 Nov 2024 11:35:25 -0700 Subject: [PATCH 8/9] task: [Dockerfile] Add note about the need to explore `pkg` alternatives --- Dockerfile | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 648764431..d7a403c4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,9 @@ ARG DOCKER_TAG="latest" # build import-meta-env for alpine for later env var injection RUN npm i -D @import-meta-env/cli RUN npm i -D @import-meta-env/unplugin + +# TODO: Find a way to update the target node version for import-meta-env +# https://github.com/cfpb/sbl-frontend/issues/1061 RUN npx pkg ./node_modules/@import-meta-env/cli/bin/import-meta-env.js \ -t node18-alpine-x64 \ -o import-meta-env-alpine @@ -27,32 +30,32 @@ COPY --from=build-stage /usr/src/app/dist /usr/share/nginx/html # copy necessary import-meta-env-alpine files for env var injection COPY --from=build-stage \ - /usr/src/app/import-meta-env-alpine \ - /usr/src/app/nginx-entrypoint.sh \ - /usr/src/app/.env.example.public \ - /usr/share/nginx/html/ + /usr/src/app/import-meta-env-alpine \ + /usr/src/app/nginx-entrypoint.sh \ + /usr/src/app/.env.example.public \ + /usr/share/nginx/html/ # copy nginx configuration into template folder for env var injection COPY nginx/nginx.conf /etc/nginx/templates/nginx.conf.template - + # Security Basline - Meets requirement 9 RUN find /etc/nginx -type d | xargs chmod 750 && \ - find /etc/nginx -type f | xargs chmod 640 + find /etc/nginx -type f | xargs chmod 640 # Security Basline - The `sed` was added to meet requirement 17 RUN sed -i '/Faithfully yours/d' /usr/share/nginx/html/50x.html && \ - addgroup -S $NGINX_USER && \ - adduser -S $NGINX_USER -G $NGINX_USER && \ - # We need to come back and reconcile the multiple pids. - touch /run/nginx.pid && \ - touch /var/run/nginx.pid && \ - touch /var/run/nginx.pid && \ - chown -R $NGINX_USER:$NGINX_USER \ - /etc/nginx \ - /run/nginx.pid \ - /var/cache/nginx/ \ - /var/run/nginx.pid \ - /usr/share/nginx/html + addgroup -S $NGINX_USER && \ + adduser -S $NGINX_USER -G $NGINX_USER && \ + # We need to come back and reconcile the multiple pids. + touch /run/nginx.pid && \ + touch /var/run/nginx.pid && \ + touch /var/run/nginx.pid && \ + chown -R $NGINX_USER:$NGINX_USER \ + /etc/nginx \ + /run/nginx.pid \ + /var/cache/nginx/ \ + /var/run/nginx.pid \ + /usr/share/nginx/html EXPOSE 8080 USER svc_nginx_sbl From e480bff1608d30c268c7e6550a02dcc3ca51687d Mon Sep 17 00:00:00 2001 From: tanner-ricks <182143365+tanner-ricks@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:10:36 -0600 Subject: [PATCH 9/9] Merging from origin/main --- .env.example.public | 2 +- .github/variables/.env | 56 +++---- .github/workflows/test.yml | 147 ++++++++++++++++++ .gitignore | 2 + ENV-GUIDE.md | 12 +- e2e/fixtures/testFixture.ts | 24 +-- .../InstitutionProfile.spec.ts | 82 +++++----- e2e/utils/createInstitution.ts | 6 +- e2e/utils/testFixture.cleanup.ts | 2 +- playwright.config.ts | 39 ++++- src/api/common.ts | 2 +- src/components/AssociatedInstitution.tsx | 3 +- .../Filing/FilingApp/FilingErrors/index.tsx | 6 +- .../Filing/FilingApp/FilingWarnings/index.tsx | 7 +- .../Filing/FilingApp/InstitutionHeading.tsx | 20 ++- .../Filing/UpdateFinancialProfile/UfpForm.tsx | 80 ++++++---- .../UpdateIdentifyingInformation.tsx | 28 ++-- .../Filing/UpdateFinancialProfile/index.tsx | 8 +- .../AffiliateInformation.tsx | 8 +- .../AlertInstitutionApiUnreachable.tsx | 38 +++++ .../ViewInstitutionProfile/DisplayField.tsx | 17 +- .../FinancialInstitutionDetails.tsx | 12 +- .../ViewInstitutionProfile/PageIntro.tsx | 2 +- .../Filing/ViewInstitutionProfile/index.tsx | 32 ++-- src/pages/Filing/formHelpers.ts | 2 +- src/types/filingTypes.ts | 1 + src/types/formTypes.ts | 9 +- src/utils/formatting.tsx | 6 + 28 files changed, 451 insertions(+), 202 deletions(-) create mode 100644 src/pages/Filing/ViewInstitutionProfile/AlertInstitutionApiUnreachable.tsx diff --git a/.env.example.public b/.env.example.public index c8bbde5c2..9c630db0f 100644 --- a/.env.example.public +++ b/.env.example.public @@ -10,7 +10,7 @@ SBL_OIDC_REDIRECT_URI="http://localhost:${SBL_DEV_PORT}/filing" SBL_REGTECH_BASE_URL="http://localhost:8881" SBL_FILING_BASE_URL="http://localhost:8882" SBL_CLEANUP_BASE_URL="http://localhost:8883" -SBL_MAIL_BASE_URL="http://localhost:8765" +SBL_MAIL_BASE_URL="http://localhost:8765/public/case" SBL_LOGOUT_REDIRECT_URL="" SBL_VALIDATION_TIMEOUT_SECONDS="1200" SBL_LONGPOLLING_DELAY_SECONDS="backoff" diff --git a/.github/variables/.env b/.github/variables/.env index bc99864d3..66234cd4c 100644 --- a/.github/variables/.env +++ b/.github/variables/.env @@ -3,18 +3,18 @@ # WITH AND VISIBLE TO THE CLIENT BELOW ############################################################### -SBL_DEV_PORT="8899" -SBL_OIDC_AUTHORITY="http://localhost:8880/realms/regtech" -SBL_OIDC_CLIENT_ID="regtech-client" -SBL_OIDC_REDIRECT_URI="http://localhost:${SBL_DEV_PORT}/filing" -SBL_REGTECH_BASE_URL="http://localhost:8881" -SBL_FILING_BASE_URL="http://localhost:8882" -SBL_CLEANUP_BASE_URL="http://localhost:8883" -SBL_MAIL_BASE_URL="http://localhost:8765" -SBL_LOGOUT_REDIRECT_URL="" -SBL_VALIDATION_TIMEOUT_SECONDS="1200" -SBL_LONGPOLLING_DELAY_SECONDS="backoff" -SBL_UPLOAD_FILE_SIZE_LIMIT_BYTES="50000000" +SBL_DEV_PORT=8898 +SBL_OIDC_AUTHORITY=http://localhost:8880/realms/regtech +SBL_OIDC_CLIENT_ID=regtech-client +SBL_OIDC_REDIRECT_URI=http://localhost:8898/filing +SBL_REGTECH_BASE_URL=http://localhost:8881 +SBL_FILING_BASE_URL=http://localhost:8882 +SBL_CLEANUP_BASE_URL=http://localhost:8883 +SBL_MAIL_BASE_URL=http://localhost:8765/public/case +SBL_LOGOUT_REDIRECT_URL= +SBL_VALIDATION_TIMEOUT_SECONDS=1200 +SBL_LONGPOLLING_DELAY_SECONDS=backoff +SBL_UPLOAD_FILE_SIZE_LIMIT_BYTES=50000000 SBL_ENABLE_PLAYWRIGHT_TEST_SETTINGS=false @@ -22,20 +22,20 @@ SBL_ENABLE_PLAYWRIGHT_TEST_SETTINGS=false # ONLY ADD ENVIRONMENT VARIABLES THAT ARE NOT MEANT TO BE # PACKAGED WITH AND VISIBLE TO THE CLIENT BELOW ############################################################### -NODE_EXTRA_CA_CERTS="./e2e/certs/entrust_chain.crt.pem" +NODE_EXTRA_CA_CERTS=./e2e/certs/entrust_chain.crt.pem -SBL_PLAYWRIGHT_TEST_TARGET="http://localhost:${SBL_DEV_PORT}" -SBL_PLAYWRIGHT_TEST_REGTECH_TARGET="${SBL_REGTECH_BASE_URL}" -SBL_PLAYWRIGHT_TEST_FILING_TARGET="${SBL_FILING_BASE_URL}" -SBL_PLAYWRIGHT_TEST_CLEANUP_TARGET="${SBL_CLEANUP_BASE_URL}" -SBL_PLAYWRIGHT_TEST_MAIL_TARGET="${SBL_MAIL_BASE_URL}" -SBL_PLAYWRIGHT_TEST_KC_TARGET="http://localhost:8880/" -SBL_PLAYWRIGHT_TEST_KC_REALM="regtech" -SBL_PLAYWRIGHT_TEST_KC_CLI_USERNAME="admin" -SBL_PLAYWRIGHT_TEST_KC_CLI_CLIENT_ID="admin-cli" -SBL_PLAYWRIGHT_TEST_KC_CLI_CLIENT_SECRET="local_test_only" -SBL_PLAYWRIGHT_TEST_KC_CLI_GRANT_TYPE="client_credentials" -SBL_PLAYWRIGHT_TEST_KC_ADMIN_USERNAME="admin1" -SBL_PLAYWRIGHT_TEST_KC_ADMIN_PASSWORD="admin" -SBL_PLAYWRIGHT_TEST_KC_ADMIN_CLIENT_ID="regtech-client" -SBL_PLAYWRIGHT_TEST_KC_ADMIN_GRANT_TYPE="password" \ No newline at end of file +SBL_PLAYWRIGHT_TEST_TARGET=http://localhost:8898 +SBL_PLAYWRIGHT_TEST_REGTECH_TARGET=http://localhost:8881 +SBL_PLAYWRIGHT_TEST_FILING_TARGET=http://localhost:8882 +SBL_PLAYWRIGHT_TEST_CLEANUP_TARGET=http://localhost:8883 +SBL_PLAYWRIGHT_TEST_MAIL_TARGET=http://localhost:8765 +SBL_PLAYWRIGHT_TEST_KC_TARGET=http://localhost:8880/ +SBL_PLAYWRIGHT_TEST_KC_REALM=regtech +SBL_PLAYWRIGHT_TEST_KC_CLI_USERNAME=admin +SBL_PLAYWRIGHT_TEST_KC_CLI_CLIENT_ID=admin-cli +SBL_PLAYWRIGHT_TEST_KC_CLI_CLIENT_SECRET=local_test_only +SBL_PLAYWRIGHT_TEST_KC_CLI_GRANT_TYPE=client_credentials +SBL_PLAYWRIGHT_TEST_KC_ADMIN_USERNAME=admin1 +SBL_PLAYWRIGHT_TEST_KC_ADMIN_PASSWORD=admin +SBL_PLAYWRIGHT_TEST_KC_ADMIN_CLIENT_ID=regtech-client +SBL_PLAYWRIGHT_TEST_KC_ADMIN_GRANT_TYPE=password \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83aa64289..0ece7ac01 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,3 +39,150 @@ jobs: - name: Run React tests run: yarn test:e2e:ci + + playwright: + name: Playwright + runs-on: ubuntu-latest + + steps: + # Log some evironment stuff and fix some permissions + - name: Echo and permissions + run: | + cat /etc/os-release + bash --version + + mkdir -p /tmp/filing_uploads + chmod 777 /tmp/filing_uploads + + # Checkout resources + - name: Checkout Frontend + uses: actions/checkout@v4 + with: + path: 'sbl-frontend' + - name: Checkout Cleanup API + uses: actions/checkout@v4 + with: + repository: 'cfpb/regtech-cleanup-api' + path: 'regtech-cleanup-api' + - name: Checkout Mail API + uses: actions/checkout@v4 + with: + repository: 'cfpb/regtech-mail-api' + path: 'regtech-mail-api' + - name: Checkout User Fi + uses: actions/checkout@v4 + with: + repository: 'cfpb/regtech-user-fi-management' + path: 'regtech-user-fi-management' + - name: Checkout Filing API + uses: actions/checkout@v4 + with: + repository: 'cfpb/sbl-filing-api' + path: 'sbl-filing-api' + - name: Checkout SBL Project + uses: actions/checkout@v4 + with: + repository: 'cfpb/sbl-project' + path: 'sbl-project' + + # Set the environment + - name: Set Environment + uses: ./sbl-frontend/.github/actions/setvars + with: + varFilePath: ./sbl-frontend/.github/variables/.env + + # Build images + - name: Build Frontend + run: | + cd sbl-frontend + docker build -t sbl-project-sbl-frontend:latest . + - name: Build Cleanup API + run: | + cd regtech-cleanup-api + docker build -t sbl-project-cleanup:latest . + - name: Build Mail API + run: | + cd regtech-mail-api + docker build -t sbl-project-mail-api:latest . + - name: Build User Fi API + run: | + cd regtech-user-fi-management + docker build -t sbl-project-user-fi:latest . + - name: Build Filing API + run: | + cd sbl-filing-api + docker build -t sbl-project-filing:latest . + + # Setup node stuff + - name: Setup Node 20 + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'yarn' + cache-dependency-path: ./sbl-frontend/yarn.lock + + # Standup stack + - name: Standup Stack + run: | + cd sbl-project + docker compose --profile="backend" --profile="frontend" up -d --remove-orphans --build + + - name: Check running containers + run: | + ls -alh /tmp/filing_uploads + docker ps + + # Setup for test + - name: Install Local Yarn Dependencies + run: | + cd sbl-frontend + yarn + yarn playwright install --with-deps + + - name: Seed Database + run: | + cd sbl-project/dev_setup/mock_data/ + bash create_institutions.sh + bash insert_filing_period.sh + + # Run tests + - name: Run Playwright Tests + id: run-tests + run: | + cd sbl-frontend + sed "s/8899/8898/" ./.env.example.public > ./.env.example.public + sed "s/8899/8898/" ./.env.example.private > ./.env.example.private + cat ./.env.example.public > ./.env + cat ./.env.example.private >> ./.env + yarn playwright test --workers 4 + + # Store artifact test results + - name: Export docker logs + if: '!cancelled()' + run: | + mkdir -p docker-logs + docker logs sbl-project-sbl-frontend-1 &> ./docker-logs/frontend.log + docker logs sbl-project-cleanup-1 &> ./docker-logs/cleanup-api.log + docker logs sbl-project-user-fi-1 &> ./docker-logs/user-api.log + docker logs sbl-project-mail-api-1 &> ./docker-logs/mail-api.log + docker logs sbl-project-filing-1 &> ./docker-logs/filing-api.log + docker logs sbl-project-keycloak-1 &> ./docker-logs/keycloak.log + docker logs sbl-project-mailpit-1 &> ./docker-logs/mailpit.log + docker logs sbl-project-pg-1 &> ./docker-logs/postgres.log + + - name: Archive Test Results + uses: actions/upload-artifact@v4 + if: '!cancelled()' + with: + name: playwright-reports + path: | + sbl-frontend/playwright-reports + + - name: Archive Docker Logs + uses: actions/upload-artifact@v4 + if: '!cancelled()' + with: + name: docker-logs + path: | + docker-logs + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5f3ae5b16..a58460459 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,9 @@ e2e/utils/downloads/* !.yarn/versions /test-results/ /playwright-report/ +/playwright-reports/ /blob-report/ +/results.json /playwright/.cache/ playwright/.auth diff --git a/ENV-GUIDE.md b/ENV-GUIDE.md index 6ce0474f7..12eee7e40 100644 --- a/ENV-GUIDE.md +++ b/ENV-GUIDE.md @@ -13,7 +13,7 @@ SBL_OIDC_REDIRECT_URI="http://localhost:${SBL_DEV_PORT}/filing" SBL_REGTECH_BASE_URL="http://localhost:8881" SBL_FILING_BASE_URL="http://localhost:8882" SBL_CLEANUP_BASE_URL="http://localhost:8883" -SBL_MAIL_BASE_URL="http://localhost:8765" +SBL_MAIL_BASE_URL="http://localhost:8765/public/case" SBL_LOGOUT_REDIRECT_URL="" SBL_VALIDATION_TIMEOUT_SECONDS="1200" SBL_LONGPOLLING_DELAY_SECONDS="backoff" @@ -25,11 +25,11 @@ SBL_ENABLE_PLAYWRIGHT_TEST_SETTINGS=false ```env NODE_EXTRA_CA_CERTS="./e2e/certs/entrust_chain.crt.pem" -SBL_PLAYWRIGHT_TEST_TARGET="http://localhost:${SBL_DEV_PORT}" -SBL_PLAYWRIGHT_TEST_REGTECH_TARGET="${SBL_REGTECH_BASE_URL}" -SBL_PLAYWRIGHT_TEST_FILING_TARGET="${SBL_FILING_BASE_URL}" -SBL_PLAYWRIGHT_TEST_CLEANUP_TARGET="${SBL_CLEANUP_BASE_URL}" -SBL_PLAYWRIGHT_TEST_MAIL_TARGET="${SBL_MAIL_BASE_URL}" +SBL_PLAYWRIGHT_TEST_TARGET="http://localhost:8899" +SBL_PLAYWRIGHT_TEST_REGTECH_TARGET="http://localhost:8881" +SBL_PLAYWRIGHT_TEST_FILING_TARGET="http://localhost:8882" +SBL_PLAYWRIGHT_TEST_CLEANUP_TARGET="http://localhost:8883" +SBL_PLAYWRIGHT_TEST_MAIL_TARGET="http://localhost:8765" SBL_PLAYWRIGHT_TEST_KC_TARGET="http://localhost:8880/" SBL_PLAYWRIGHT_TEST_KC_REALM="regtech" SBL_PLAYWRIGHT_TEST_KC_CLI_USERNAME="admin" diff --git a/e2e/fixtures/testFixture.ts b/e2e/fixtures/testFixture.ts index 2d994c2a5..fc7a30501 100644 --- a/e2e/fixtures/testFixture.ts +++ b/e2e/fixtures/testFixture.ts @@ -99,11 +99,13 @@ export const test = baseTest.extend<{ await createDomainAssociation({ adminToken, testEmailDomain, testLei }); } - // console.log the ephemeral user data for debugging - // eslint-disable-next-line no-console - console.log('testUsername :>>', testUsername); - // eslint-disable-next-line no-console - console.log('testUserPassword :>>', testUserPassword); + if (!process.env.CI) { + // console.log the ephemeral user data for debugging + // eslint-disable-next-line no-console + console.log('testUsername :>>', testUsername); + // eslint-disable-next-line no-console + console.log('testUserPassword :>>', testUserPassword); + } await test.step('Unauthenticated homepage: navigate to keycloak', async () => { await page.goto('/'); @@ -155,11 +157,15 @@ export const test = baseTest.extend<{ await use(); - const healthy = await cleanupHealthcheck({ adminToken }); - if (healthy) { - await cleanup({ adminToken, testLei }); + if (!process.env.CI) { + const cadminToken = await getAdminKeycloakToken(); + + const healthy = await cleanupHealthcheck({ adminToken: cadminToken }); + if (healthy) { + await cleanup({ adminToken: cadminToken, testLei }); + } + await deleteKeycloakUser({ id: testUserId }); } - await deleteKeycloakUser({ id: testUserId }); }, { auto: true }, ], diff --git a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts index 1de4f306e..3e6cb5aa7 100644 --- a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts +++ b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts @@ -2,11 +2,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/testFixture'; import { clickLinkWithRetry } from '../../utils/clickExternalLinkWithRetry'; -test('Institution Profile Page', async ({ - page, - context, - navigateToFilingHome, -}) => { +test('Institution Profile Page', async ({ page, navigateToFilingHome }) => { // Go to Profile page await test.step('User Profile Page', async () => { navigateToFilingHome; @@ -74,7 +70,7 @@ test('Institution Profile Page', async ({ await expect( page.locator('#main h3').nth(3), 'Label is correct', - ).toContainText('LEI status'); + ).toContainText('LEI registration status'); await expect( page.locator('#main h3').nth(3).locator('xpath=../p[1]'), 'LEI status is correct', @@ -288,50 +284,64 @@ test('Institution Profile Page', async ({ 'Get an LEI: Find LEI Issuing Organizations - LEI – GLEIF', ); await page.goBack(); + await expect(page.locator('h1'), 'h1 is correct').toContainText( + 'View your financial institution profile', + ); }); // Update Financial Institution links - const fipLinks = await page - .getByRole('link', { + await test.step('FIP Links', async () => { + const fipLinksLocator = await page.getByRole('link', { name: 'Update your financial institution profile', - }) - .all(); + }); + const fipLinks = await fipLinksLocator.all(); - for (const [index, fipLink] of fipLinks.entries()) { - // eslint-disable-next-line no-await-in-loop - await test.step(`fipLink: ${index + 1}`, async () => { - await test.step('Click: link', async () => { - await fipLink.click(); + for (const [index, fipLink] of fipLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + await test.step(`fipLink: ${index + 1}`, async () => { + await test.step('Click: link', async () => { + await clickLinkWithRetry({ + page, + target: fipLink, + }); + }); + await expect(page.locator('h1'), 'h1 is correct').toContainText( + 'Update your financial institution profile', + ); + await page.goBack(); + await expect(page.locator('h1'), 'h1 is correct').toContainText( + 'View your financial institution profile', + ); }); - await expect(page.locator('h1'), 'h1 is correct').toContainText( - 'Update your financial institution profile', - ); - await page.goBack(); - }); - } + } + }); // Federal Reserve Board links - const frbLinks = await page - .getByRole('link', { + await test.step('FRB Links', async () => { + const frbLinksLocator = await page.getByRole('link', { name: 'Federal Reserve Board', - }) - .all(); + }); + const frbLinks = await frbLinksLocator.all(); - await Promise.all( - frbLinks.map(async (frbLink, index) => { + for (const [index, frbLink] of frbLinks.entries()) { + // eslint-disable-next-line no-await-in-loop await test.step(`frbLink: ${index + 1}`, async () => { - const [frbExternalLink] = await Promise.all([ - context.waitForEvent('page'), - frbLink.click(), - ]); - - await expect(frbExternalLink, 'Resolves correctly').toHaveURL( + await test.step('Click: link', async () => { + await clickLinkWithRetry({ + page, + target: frbLink, + }); + }); + await expect(page, 'Resolves correctly').toHaveURL( 'https://www.federalreserve.gov/apps/reportingforms/Report/Index/FR_Y-10', ); - await frbExternalLink.close(); + await page.goBack(); + await expect(page.locator('h1'), 'h1 is correct').toContainText( + 'View your financial institution profile', + ); }); - }), - ); + } + }); }); // Test Breadcrumb diff --git a/e2e/utils/createInstitution.ts b/e2e/utils/createInstitution.ts index 778b7a86f..13c4435e1 100644 --- a/e2e/utils/createInstitution.ts +++ b/e2e/utils/createInstitution.ts @@ -23,15 +23,17 @@ export default async function createInstitution({ url: `${process.env.SBL_PLAYWRIGHT_TEST_REGTECH_TARGET}/v1/institutions`, headers: { Authorization: `Bearer ${adminToken}` }, data: { - name: testInstitutionName, lei: testLei, - is_active: true, + name: testInstitutionName, + lei_status_code: 'ISSUED', tax_id: testTaxId, rssd_id: testRssdId, primary_federal_regulator_id: 'OCC', hmda_institution_type_id: '1', hq_address_street_1: 'Test Address Street 1', hq_address_street_2: '', + hq_address_street_3: '', + hq_address_street_4: '', hq_address_city: 'Test City 1', hq_address_state_code: 'GA', hq_address_zip: '00000', diff --git a/e2e/utils/testFixture.cleanup.ts b/e2e/utils/testFixture.cleanup.ts index 36bddf5e9..0aacbd4b4 100644 --- a/e2e/utils/testFixture.cleanup.ts +++ b/e2e/utils/testFixture.cleanup.ts @@ -47,6 +47,6 @@ export default async function cleanup({ await axios.request(options); } catch (error) { // eslint-disable-next-line no-console - console.error('error when calling cleanup api :>>', error); + console.warn('error when calling cleanup api :>>', error); } } diff --git a/playwright.config.ts b/playwright.config.ts index 26ba8d00a..f6dbf02d5 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,8 @@ -import { PlaywrightTestConfig, devices } from '@playwright/test'; +import { + PlaywrightTestConfig, + ReporterDescription, + devices, +} from '@playwright/test'; /** * Read environment variables from file. @@ -6,9 +10,28 @@ import { PlaywrightTestConfig, devices } from '@playwright/test'; */ require('dotenv').config(); -const TIMEOUT_GLOBAL = 60 * 60 * 1000; -const TIMEOUT_TEST = 5 * 60 * 1000; -const TIMEOUT_EXPECT = 60 * 1000; +// Test specific timeouts are configured here +const CI_TIMEOUT_MULTIPLIER = 1; + +const TIMEOUT_GLOBAL = + 60 * 60 * 1000 * (process.env.CI ? CI_TIMEOUT_MULTIPLIER : 1); +const TIMEOUT_TEST = + 5 * 60 * 1000 * (process.env.CI ? CI_TIMEOUT_MULTIPLIER : 1); +const TIMEOUT_EXPECT = 60 * 1000 * (process.env.CI ? CI_TIMEOUT_MULTIPLIER : 1); + +// Reporter configs. See https://playwright.dev/docs/test-reporters +const BASE_REPORTERS: ReporterDescription[] = [ + ['html', { outputFolder: 'playwright-reports/html', open: 'on-failure' }], + ['blob', { outputFile: 'playwright-reports/blob.zip' }], + ['json', { outputFile: 'playwright-reports/json.json' }], + ['junit', { outputFile: 'playwright-reports/junit.xml' }], + ['list', { printSteps: true }], +]; + +if (process.env.CI) { + BASE_REPORTERS[0][1].open = 'never'; + BASE_REPORTERS.push(['github']); +} /** * See https://playwright.dev/docs/test-configuration. @@ -20,15 +43,17 @@ const config: PlaywrightTestConfig = { /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, + // retries: process.env.CI ? 2 : 0, + retries: 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + // workers: process.env.CI ? 1 : undefined, + workers: undefined, /* Timeout for the entire test suite */ globalTimeout: TIMEOUT_GLOBAL, /* Timeout for per test */ timeout: TIMEOUT_TEST, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: BASE_REPORTERS, /* Expect specific settings */ expect: { /* Timeout for expects */ diff --git a/src/api/common.ts b/src/api/common.ts index 69cf04280..e891d3010 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -22,7 +22,7 @@ export const FILING_URL = `${ }`; export const MAIL_BASE_URL = `${ - import.meta.env.SBL_MAIL_BASE_URL || 'http://localhost:8765' + import.meta.env.SBL_MAIL_BASE_URL || 'http://localhost:8765/public/case' }`; export const LOGOUT_REDIRECT_URL = `${ diff --git a/src/components/AssociatedInstitution.tsx b/src/components/AssociatedInstitution.tsx index 9dd5c0407..ec0f869c3 100644 --- a/src/components/AssociatedInstitution.tsx +++ b/src/components/AssociatedInstitution.tsx @@ -1,5 +1,6 @@ /* eslint-disable react/require-default-props */ import { ListLink } from 'components/Link'; +import { formatPipeSeparatedString } from 'utils/formatting'; import type { InstitutionDetailsApiType } from 'types/formTypes'; export function AssociatedInstitution({ @@ -12,7 +13,7 @@ export function AssociatedInstitution({ if (lei) { href = `/institution/${lei}`; - text = [name, lei].filter(Boolean).join(' | '); + text = formatPipeSeparatedString([name, lei]); } return ( diff --git a/src/pages/Filing/FilingApp/FilingErrors/index.tsx b/src/pages/Filing/FilingApp/FilingErrors/index.tsx index 0ab60261f..9f58079f7 100644 --- a/src/pages/Filing/FilingApp/FilingErrors/index.tsx +++ b/src/pages/Filing/FilingApp/FilingErrors/index.tsx @@ -204,14 +204,16 @@ function FilingErrors(): JSX.Element { corrections to your register, and upload a new file. )} - {errorState && actualDataGetSubmissionLatest?.id ? ( + {errorState && + actualDataGetSubmissionLatest?.id && + actualDataGetSubmissionLatest?.counter ? ( ) : null} diff --git a/src/pages/Filing/FilingApp/FilingWarnings/index.tsx b/src/pages/Filing/FilingApp/FilingWarnings/index.tsx index 75e219f07..7e5c779f8 100644 --- a/src/pages/Filing/FilingApp/FilingWarnings/index.tsx +++ b/src/pages/Filing/FilingApp/FilingWarnings/index.tsx @@ -115,7 +115,7 @@ function FilingWarnings(): JSX.Element { } const response = await mutateSubmitWarningsAccept({ - submissionId: submission?.id, + submissionId: submission?.counter, }); // @ts-expect-error Part of code cleanup for post-mvp see: https://github.com/cfpb/sbl-frontend/issues/717 @@ -160,14 +160,15 @@ function FilingWarnings(): JSX.Element { {hasWarnings && // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/prefer-optional-chain - submission?.id ? ( + submission?.id && + submission?.counter ? ( ) : null} diff --git a/src/pages/Filing/FilingApp/InstitutionHeading.tsx b/src/pages/Filing/FilingApp/InstitutionHeading.tsx index 6c6de0b22..9c6086ead 100644 --- a/src/pages/Filing/FilingApp/InstitutionHeading.tsx +++ b/src/pages/Filing/FilingApp/InstitutionHeading.tsx @@ -1,4 +1,5 @@ import { Heading } from 'design-system-react'; +import { formatPipeSeparatedString } from '../../../utils/formatting'; import type { HeadingType } from 'design-system-react/dist/components/Headings/Heading'; import type { InstitutionDataType } from './InstitutionCard.types'; @@ -10,16 +11,13 @@ function InstitutionHeading({ headingType = '5', // eslint-disable-next-line react/require-default-props }: InstitutionDataType & { headingType?: HeadingType }): JSX.Element { - const content: (number | string)[] = []; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - for (const item of [name || lei, filingPeriod]) { - if (item) { - content.push(item); - } - } - const contentUsed = content - .filter(Boolean) - .join(`${'\u00A0\u00A0'}|${'\u00A0\u00A0'}`); - return {contentUsed}; + return ( + + { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + formatPipeSeparatedString([name || lei, filingPeriod]) + } + + ); } export default InstitutionHeading; diff --git a/src/pages/Filing/UpdateFinancialProfile/UfpForm.tsx b/src/pages/Filing/UpdateFinancialProfile/UfpForm.tsx index cc7625013..d6d512c42 100644 --- a/src/pages/Filing/UpdateFinancialProfile/UfpForm.tsx +++ b/src/pages/Filing/UpdateFinancialProfile/UfpForm.tsx @@ -13,6 +13,7 @@ import { Paragraph, TextIntroduction } from 'design-system-react'; import type { JSXElement } from 'design-system-react/dist/types/jsxElement'; import type { UpdateInstitutionType } from 'pages/Filing/UpdateFinancialProfile/types'; import { UpdateInstitutionSchema } from 'pages/Filing/UpdateFinancialProfile/types'; +import { AlertInstitutionApiUnreachable } from 'pages/Filing/ViewInstitutionProfile/AlertInstitutionApiUnreachable'; import { scrollToElement } from 'pages/ProfileForm/ProfileFormUtils'; import { scenarios } from 'pages/Summary/Summary.data'; import { useMemo } from 'react'; @@ -31,14 +32,19 @@ import { formErrorsOrder } from './formErrorsOrder'; export default function UFPForm({ data, + isError = false, }: { data: InstitutionDetailsApiType; + isError: boolean; }): JSXElement { const { lei } = useParams(); const isRoutingEnabled = getIsRoutingEnabled(); const navigate = useNavigate(); - const defaultValues = useMemo(() => buildProfileFormDefaults(data), [data]); + const defaultValues = useMemo( + () => (isError ? {} : buildProfileFormDefaults(data)), + [data, isError], + ); const { trigger, @@ -52,7 +58,9 @@ export default function UFPForm({ defaultValues, }); - const changedData = collectChangedData(watch(), dirtyFields, data); + const changedData = isError + ? null + : collectChangedData(watch(), dirtyFields, data); // Used for error scrolling const formErrorHeaderId = 'UFPFormErrorHeader'; @@ -139,40 +147,44 @@ export default function UFPForm({ } /> - - alertHeading='There was a problem updating your financial institution profile' - errors={orderedFormErrorsObject} - id={formErrorHeaderId} - formErrorHeaderObject={IdFormHeaderErrors} - keyLogicFunc={updateFinancialProfileKeyLogic} - /> - - - - -