From 52011fe6e69082a2f1bb67c52b303cb340835b9f Mon Sep 17 00:00:00 2001 From: Justin-MacIntosh Date: Tue, 2 Dec 2025 16:35:47 -0500 Subject: [PATCH 1/2] WIP playwright --- e2e/.github/workflows/playwright.yml | 27 ++++++++ e2e/.gitignore | 8 +++ e2e/package-lock.json | 97 ++++++++++++++++++++++++++++ e2e/package.json | 14 ++++ e2e/playwright.config.ts | 79 ++++++++++++++++++++++ e2e/tests/example.spec.ts | 18 ++++++ 6 files changed, 243 insertions(+) create mode 100644 e2e/.github/workflows/playwright.yml create mode 100644 e2e/.gitignore create mode 100644 e2e/package-lock.json create mode 100644 e2e/package.json create mode 100644 e2e/playwright.config.ts create mode 100644 e2e/tests/example.spec.ts diff --git a/e2e/.github/workflows/playwright.yml b/e2e/.github/workflows/playwright.yml new file mode 100644 index 0000000..3eb1314 --- /dev/null +++ b/e2e/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/e2e/.gitignore b/e2e/.gitignore new file mode 100644 index 0000000..335bd46 --- /dev/null +++ b/e2e/.gitignore @@ -0,0 +1,8 @@ + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/e2e/package-lock.json b/e2e/package-lock.json new file mode 100644 index 0000000..2d07d32 --- /dev/null +++ b/e2e/package-lock.json @@ -0,0 +1,97 @@ +{ + "name": "e2e", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "e2e", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.57.0", + "@types/node": "^24.10.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000..9922da5 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,14 @@ +{ + "name": "e2e", + "version": "1.0.0", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@playwright/test": "^1.57.0", + "@types/node": "^24.10.1" + } +} diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 0000000..3b86f94 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,79 @@ +import { defineConfig } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* 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, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { browserName: 'chromium' }, + }, + + // { + // name: 'firefox', + // use: { browserName: 'firefox' }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://localhost:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/e2e/tests/example.spec.ts b/e2e/tests/example.spec.ts new file mode 100644 index 0000000..54a906a --- /dev/null +++ b/e2e/tests/example.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); From f14478b997964bb1c2ae40b1cdd7c5249424b366 Mon Sep 17 00:00:00 2001 From: Justin-MacIntosh Date: Wed, 3 Dec 2025 11:23:30 -0500 Subject: [PATCH 2/2] WIP github action for running services. --- .github/workflows/deploy-builder-api.yml | 2 +- .github/workflows/run-e2e-tests.yml | 123 ++++++++++++++++++ devbox.json | 3 +- devbox.lock | 48 +++++++ e2e/.auth/user.json | 4 + e2e/.github/workflows/playwright.yml | 27 ---- .../auth_export/accounts.json | 25 ++++ e2e/e2e-emulator-data/auth_export/config.json | 1 + .../firebase-export-metadata.json | 16 +++ .../all_namespaces_all_kinds.export_metadata | Bin 0 -> 52 bytes .../all_namespaces/all_kinds/output-0 | Bin 0 -> 711 bytes .../firestore_export.overall_export_metadata | Bin 0 -> 95 bytes .../storage_export/buckets.json | 7 + e2e/playwright.config.ts | 45 +++---- e2e/tests/example.spec.ts | 18 --- e2e/tests/smokeTest.spec.ts | 33 +++++ .../auth_export/accounts.json | 1 + .../auth_export/config.json | 1 + .../all_namespaces_all_kinds.export_metadata | Bin 0 -> 52 bytes .../all_namespaces/all_kinds/output-0 | Bin 0 -> 106391 bytes .../firestore_export.overall_export_metadata | Bin 0 -> 96 bytes 21 files changed, 278 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/run-e2e-tests.yml create mode 100644 e2e/.auth/user.json delete mode 100644 e2e/.github/workflows/playwright.yml create mode 100644 e2e/e2e-emulator-data/auth_export/accounts.json create mode 100644 e2e/e2e-emulator-data/auth_export/config.json create mode 100644 e2e/e2e-emulator-data/firebase-export-metadata.json create mode 100644 e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata create mode 100644 e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/output-0 create mode 100644 e2e/e2e-emulator-data/firestore_export/firestore_export.overall_export_metadata create mode 100644 e2e/e2e-emulator-data/storage_export/buckets.json delete mode 100644 e2e/tests/example.spec.ts create mode 100644 e2e/tests/smokeTest.spec.ts create mode 100644 firebase-export-1764791128917hV0xYz/auth_export/accounts.json create mode 100644 firebase-export-1764791128917hV0xYz/auth_export/config.json create mode 100644 firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata create mode 100644 firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/output-0 create mode 100644 firebase-export-1764791128917hV0xYz/firestore_export/firestore_export.overall_export_metadata diff --git a/.github/workflows/deploy-builder-api.yml b/.github/workflows/deploy-builder-api.yml index f5fa3d5..1c18727 100644 --- a/.github/workflows/deploy-builder-api.yml +++ b/.github/workflows/deploy-builder-api.yml @@ -50,7 +50,7 @@ jobs: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('builder-api/pom.xml') }} restore-keys: | - ${{ runner.os }}-maven- + ${{ runner.os }}-maven-${{ hashFiles('builder-api/pom.xml') }} # Configure Workload Identity Federation and generate an access token - id: 'auth' diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml new file mode 100644 index 0000000..d57dfc8 --- /dev/null +++ b/.github/workflows/run-e2e-tests.yml @@ -0,0 +1,123 @@ +name: Run E2e Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +env: + PROJECT_ID: 'benefit-decision-toolkit-play' + WORKLOAD_IDENTITY_PROVIDER: 'projects/1034049717668/locations/global/workloadIdentityPools/github-actions-google-cloud/providers/github' + +jobs: + run-e2e-tests: + runs-on: 'ubuntu-latest' + + # Add these permissions for Workload Identity Federation + permissions: + contents: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: lts/* + + # Devbox Setup # + - name: 'Create .env file' # Devbox needs a .env file to exist, even if it's empty + run: touch .env + + - name: Rename env files + run: | + mv builder-frontend/.env.example builder-frontend/.env + mv builder-api/.env.example builder-api/.env + + - name: 'Install devbox' # Setup devbox which includes Node.js, Firebase CLI, and Google Cloud SDK + uses: 'jetify-com/devbox-install-action@v0.12.0' + with: + enable-cache: true + + # Cache Maven dependencies to speed up builds + - name: 'Cache Maven dependencies' + uses: 'actions/cache@v4' + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('builder-api/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - id: 'auth' # Configure Workload Identity Federation and generate an access token + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v2' + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + service_account: cicd-build-deploy-api@benefit-decision-toolkit-play.iam.gserviceaccount.com + project_id: ${{ env.PROJECT_ID }} + + - name: Cache node modules + uses: actions/cache@v4 + with: + path: builder-frontend/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('builder-frontend/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install frontend dependencies + working-directory: builder-frontend + run: devbox run install-builder-frontend-ci + + - name: Load E2E emulator data + run: | + rm -rf emulator-data + cp -r e2e/e2e-emulator-data emulator-data + + - name: Run all Devbox services + run: devbox services up -b + continue-on-error: true + + # E2E Testing # + - name: Install Playwright dependencies + run: npm ci + working-directory: e2e + + - name: Install Playwright Browsers + run: npx playwright install --with-deps + working-directory: e2e + + # - name: Wait for Firebase emulators to be available + # uses: nev7n/wait_for_response@v1 + # with: + # url: 'http://localhost:4000/' + # responseCode: 200 + # timeout: 90000 + # interval: 1000 + # continue-on-error: true + + - name: Wait for App to be available + uses: nev7n/wait_for_response@v1 + with: + url: 'http://localhost:5173/' + responseCode: 200 + timeout: 90000 + interval: 1000 + continue-on-error: true + + - name: Run Playwright tests + run: npx playwright test + working-directory: e2e + continue-on-error: true + + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: e2e/playwright-report/ + retention-days: 30 + + # Devbox Cleanup # + - name: Stop all Devbox services + run: devbox services stop + continue-on-error: true diff --git a/devbox.json b/devbox.json index 276b7bf..3719688 100644 --- a/devbox.json +++ b/devbox.json @@ -7,7 +7,8 @@ "firebase-tools@latest", "google-cloud-sdk@latest", "nodejs@22", - "bruno-cli@latest" + "bruno-cli@latest", + "process-compose@latest" ], "env_from": ".env", "shell": { diff --git a/devbox.lock b/devbox.lock index f731282..07cb54a 100644 --- a/devbox.lock +++ b/devbox.lock @@ -314,6 +314,54 @@ } } }, + "process-compose@latest": { + "last_modified": "2025-11-23T21:50:36Z", + "resolved": "github:NixOS/nixpkgs/ee09932cedcef15aaf476f9343d1dea2cb77e261#process-compose", + "source": "devbox-search", + "version": "1.78.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/d00iad83k88x0fq045h5pzhfj9ibdd3a-process-compose-1.78.0", + "default": true + } + ], + "store_path": "/nix/store/d00iad83k88x0fq045h5pzhfj9ibdd3a-process-compose-1.78.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/zx59c11mchqfjpl0yvv2fad87pf3p8mn-process-compose-1.78.0", + "default": true + } + ], + "store_path": "/nix/store/zx59c11mchqfjpl0yvv2fad87pf3p8mn-process-compose-1.78.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hwipxpsgzpv8d51xvqdjyx3vbzaaf3rr-process-compose-1.78.0", + "default": true + } + ], + "store_path": "/nix/store/hwipxpsgzpv8d51xvqdjyx3vbzaaf3rr-process-compose-1.78.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vw9d8v9r0kp44lmizysj7idmqyf9747l-process-compose-1.78.0", + "default": true + } + ], + "store_path": "/nix/store/vw9d8v9r0kp44lmizysj7idmqyf9747l-process-compose-1.78.0" + } + } + }, "quarkus@latest": { "last_modified": "2025-08-11T16:06:55Z", "resolved": "github:NixOS/nixpkgs/4e942f9ef5b35526597c354d1ded817d1c285ef1#quarkus", diff --git a/e2e/.auth/user.json b/e2e/.auth/user.json new file mode 100644 index 0000000..f4ec355 --- /dev/null +++ b/e2e/.auth/user.json @@ -0,0 +1,4 @@ +{ + "cookies": [], + "origins": [] +} \ No newline at end of file diff --git a/e2e/.github/workflows/playwright.yml b/e2e/.github/workflows/playwright.yml deleted file mode 100644 index 3eb1314..0000000 --- a/e2e/.github/workflows/playwright.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Playwright Tests -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - run: npx playwright test - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/e2e/e2e-emulator-data/auth_export/accounts.json b/e2e/e2e-emulator-data/auth_export/accounts.json new file mode 100644 index 0000000..4bf0586 --- /dev/null +++ b/e2e/e2e-emulator-data/auth_export/accounts.json @@ -0,0 +1,25 @@ +{ + "kind": "identitytoolkit#DownloadAccountResponse", + "users": [ + { + "localId": "E2uB0h1FpSUKq5rGObr9jvmbn15E", + "lastLoginAt": "1766942325287", + "emailVerified": false, + "email": "test@example.com", + "salt": "fakeSaltirMZBjlsDBLmp2MfKjTv", + "passwordHash": "fakeHash:salt=fakeSaltirMZBjlsDBLmp2MfKjTv:password=testpassword123", + "passwordUpdatedAt": 1766942325288, + "validSince": "1766942325", + "createdAt": "1766942325287", + "providerUserInfo": [ + { + "providerId": "password", + "email": "test@example.com", + "federatedId": "test@example.com", + "rawId": "test@example.com" + } + ], + "lastRefreshAt": "2025-12-28T17:18:45.288Z" + } + ] +} \ No newline at end of file diff --git a/e2e/e2e-emulator-data/auth_export/config.json b/e2e/e2e-emulator-data/auth_export/config.json new file mode 100644 index 0000000..6f240f7 --- /dev/null +++ b/e2e/e2e-emulator-data/auth_export/config.json @@ -0,0 +1 @@ +{"signIn":{"allowDuplicateEmails":false},"emailPrivacyConfig":{"enableImprovedEmailPrivacy":false}} \ No newline at end of file diff --git a/e2e/e2e-emulator-data/firebase-export-metadata.json b/e2e/e2e-emulator-data/firebase-export-metadata.json new file mode 100644 index 0000000..a6f1f92 --- /dev/null +++ b/e2e/e2e-emulator-data/firebase-export-metadata.json @@ -0,0 +1,16 @@ +{ + "version": "14.11.2", + "firestore": { + "version": "1.19.8", + "path": "firestore_export", + "metadata_file": "firestore_export/firestore_export.overall_export_metadata" + }, + "auth": { + "version": "14.11.2", + "path": "auth_export" + }, + "storage": { + "version": "14.11.2", + "path": "storage_export" + } +} \ No newline at end of file diff --git a/e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata b/e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata new file mode 100644 index 0000000000000000000000000000000000000000..dc98eba82e6dc29a32642c41a4e29faf31cb1c97 GIT binary patch literal 52 zcmd;5;Sxy8EJ`gd$uCNcPpv4(FDenZvhB#yrw=AFOEg@CFobxx7=$?TOG^q$OLPqY DE!q_L literal 0 HcmV?d00001 diff --git a/e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/output-0 b/e2e/e2e-emulator-data/firestore_export/all_namespaces/all_kinds/output-0 new file mode 100644 index 0000000000000000000000000000000000000000..cf1cd1b132bacf1772d74a7449ce12fdd4f8d0fa GIT binary patch literal 711 zcmex(llO5XBV(3rmOx5sSzStMZoY0(N{KFzQ>4i)#9y9Yl%1KE9-Lg1nwpndq$E;Y z=HuzGX=FEKY2$YYmcO-n3JOR18Q;!a5|PA}(&Cc*Tqj5fD-q>y co+3UD2`RSZjMU`pVxYGNJ|;+t22z3<0POeC8vp*B}Az-at#=26W621YJFE*1_Z31%V3#GIV?yu{qp;)2BF)M9-QH#;*g1;Ro| d#KR@@QY#Abi%Q~iQ%e$45=#;#m=un)0sy { - await page.goto('https://playwright.dev/'); - - // Expect a title "to contain" a substring. - await expect(page).toHaveTitle(/Playwright/); -}); - -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); - - // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); - - // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); -}); diff --git a/e2e/tests/smokeTest.spec.ts b/e2e/tests/smokeTest.spec.ts new file mode 100644 index 0000000..00c3e1d --- /dev/null +++ b/e2e/tests/smokeTest.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Smoke Tests', () => { + test.beforeEach(async ({ page }) => { + // Navigate to login page + await page.goto('/login'); + + // Fill in credentials (using Firebase emulator test user) + await page.locator('#email').fill('test@example.com'); + await page.locator('#password').fill('testpassword123'); + + // Click sign in button + await page.getByRole('button', { name: 'Sign In' }).click(); + + // Wait for navigation to home page after successful login + await expect(page).toHaveURL('/'); + }); + + test('user can view landing page after login', async ({ page }) => { + // Already on home page after beforeEach login + + // Verify the page title + await expect(page).toHaveTitle(/Benefit Decision Toolkit/); + + // Verify key elements of the landing page are visible + // Header with logout button indicates user is authenticated + await expect(page.getByText('Logout')).toBeVisible(); + + // Navigation tabs should be visible + await expect(page.getByRole('button', { name: 'Screeners' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Eligibility checks' })).toBeVisible(); + }); +}); diff --git a/firebase-export-1764791128917hV0xYz/auth_export/accounts.json b/firebase-export-1764791128917hV0xYz/auth_export/accounts.json new file mode 100644 index 0000000..f3a008d --- /dev/null +++ b/firebase-export-1764791128917hV0xYz/auth_export/accounts.json @@ -0,0 +1 @@ +{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"hLDb1FqwfMGKZJLCjwNpNjmal3JC","createdAt":"1761583652308","lastLoginAt":"1764189369529","displayName":"Chicken Orange","providerUserInfo":[{"providerId":"google.com","rawId":"6608011541666817252291145694249152281893","federatedId":"6608011541666817252291145694249152281893","displayName":"Chicken Orange","email":"chicken.orange.260@example.com","screenName":"orange_chicken"}],"validSince":"1764791128","email":"chicken.orange.260@example.com","emailVerified":true,"disabled":false}]} \ No newline at end of file diff --git a/firebase-export-1764791128917hV0xYz/auth_export/config.json b/firebase-export-1764791128917hV0xYz/auth_export/config.json new file mode 100644 index 0000000..6f240f7 --- /dev/null +++ b/firebase-export-1764791128917hV0xYz/auth_export/config.json @@ -0,0 +1 @@ +{"signIn":{"allowDuplicateEmails":false},"emailPrivacyConfig":{"enableImprovedEmailPrivacy":false}} \ No newline at end of file diff --git a/firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata b/firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata new file mode 100644 index 0000000000000000000000000000000000000000..77fdec5f9446c48999f334e2c6684d446ec01490 GIT binary patch literal 52 zcmd;5;Sxy8EJ`gd$uCNcPpv4(FDemu@U?64^hFbyCEoAo1~Y_sxEO>u@=Hq!N=tMN E02);k1^@s6 literal 0 HcmV?d00001 diff --git a/firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/output-0 b/firebase-export-1764791128917hV0xYz/firestore_export/all_namespaces/all_kinds/output-0 new file mode 100644 index 0000000000000000000000000000000000000000..1394c678eda108b8a09d97b9e27f291c0526a78e GIT binary patch literal 106391 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 literal 0 HcmV?d00001