From bdcf07bc5515bd7cebdfcdd4801f42889dadbd2b Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sat, 28 Mar 2026 10:56:48 -0500 Subject: [PATCH 01/32] add initial workflow --- .github/workflows/e2e.yml | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/e2e.yml diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..30b1094 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,62 @@ +name: End-to-End Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + e2e-testing: + timeout-minutes: 60 + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.19.4' + + - name: Install Dependencies + run: | + npm install -g wait-on + npm ci + cd client && npm ci + cd ../server && npm ci + + - name: Install Playwright Browsers + run: npx playwright install --with-deps + + - name: Start Express Server + env: + SUPABASE_URL: ${{ secrets.TEST_SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.TEST_SUPABASE_SERVICE_ROLE_KEY }} + PORT: 5000 + run: | + cd server + npm run start & + wait-on http://localhost:5000 + + - name: Start React Client + env: + REACT_APP_SUPABASE_URL: ${{ secrets.TEST_SUPABASE_URL }} + REACT_APP_SUPABASE_ANON_KEY: ${{ secrets.TEST_SUPABASE_ANON_KEY }} + PORT: 3000 + run: | + cd client + npm run dev & + wait-on http://localhost:3000 + + - name: Run Playwright Tests + run: npx playwright test + + - name: Upload Playwright Report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 \ No newline at end of file From 0da4b9e4869898d3fc7062d10c35e456f486bf04 Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sat, 28 Mar 2026 11:57:40 -0500 Subject: [PATCH 02/32] added playwright smoke tests --- .gitignore | 7 ++++ package-lock.json | 88 ++++++++++++++++++++++++++++++++++++-------- package.json | 6 ++- playwright.config.ts | 24 ++++++++++++ server/server.js | 5 +++ tests/smoke.spec.js | 19 ++++++++++ 6 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 playwright.config.ts create mode 100644 tests/smoke.spec.js diff --git a/.gitignore b/.gitignore index 0572417..b56c6a5 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,10 @@ dist .yarn/install-state.gz .pnp.* client/src/components/AddToTree/sample-data.json + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/package-lock.json b/package-lock.json index 9c6c515..3a793cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,9 @@ "web-vitals": "^2.1.4" }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@playwright/test": "^1.58.2", + "@types/node": "^25.5.0" } }, "node_modules/@adobe/css-tools": { @@ -3480,6 +3482,21 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", @@ -4695,12 +4712,11 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "license": "MIT", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~7.18.0" } }, "node_modules/@types/node-forge": { @@ -14707,6 +14723,50 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -19021,17 +19081,16 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "license": "Apache-2.0", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { @@ -19059,10 +19118,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "license": "MIT" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", diff --git a/package.json b/package.json index 286c8ac..17ad2a3 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "react-scripts test", + "test": "npx playwright test", "eject": "react-scripts eject" }, "eslintConfig": { @@ -39,6 +39,8 @@ ] }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@playwright/test": "^1.58.2", + "@types/node": "^25.5.0" } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..ffb9c38 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + // Fail the build on CI if you accidentally left test.only in the code + forbidOnly: !!process.env.CI, + // Retry failing tests twice on CI to handle random network flakes + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); \ No newline at end of file diff --git a/server/server.js b/server/server.js index f2539c3..e68cfaf 100644 --- a/server/server.js +++ b/server/server.js @@ -17,6 +17,11 @@ const port = process.env.PORT || 5000; app.use(express.json()); app.use(cors()); +// Status check +app.get('/', (req, res) => { + res.status(200).json(); +}); + app.use('/api/share-trees', sharedTreeRoutes) app.use('/api/family-members', treeMemberRoutes); app.use('/api/relationships', relationshipRoutes); diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js new file mode 100644 index 0000000..b721fdb --- /dev/null +++ b/tests/smoke.spec.js @@ -0,0 +1,19 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Client/Server Smoke Tests', () => { + + test('React frontend is running and accessible', async ({ page }) => { + // Navigates to http://localhost:3000/ + await page.goto('/'); + + await expect(page).toHaveTitle(/KinTree/i); + }); + + test('Express backend API is reachable', async ({ request }) => { + // Ping the backend directly on port 5000 to ensure the Express server is up + const response = await request.get('http://localhost:5000/'); + + expect(response.ok()).toBeTruthy(); + }); + +}); \ No newline at end of file From 4734fe4bf25362ac7dccccf9b8890fe6cce1c792 Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sat, 28 Mar 2026 12:10:44 -0500 Subject: [PATCH 03/32] edit react runner --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 30b1094..ed782d6 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -47,7 +47,7 @@ jobs: PORT: 3000 run: | cd client - npm run dev & + npm start & wait-on http://localhost:3000 - name: Run Playwright Tests From 9a9b48de58190d05755c426f7b6ee9d0f77ffc84 Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sun, 5 Apr 2026 13:30:58 -0500 Subject: [PATCH 04/32] moved into e2e folder --- {tests => e2e}/smoke.spec.js | 0 package-lock.json | 24 ++++++++++++------------ package.json | 2 +- playwright.config.ts | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) rename {tests => e2e}/smoke.spec.js (100%) diff --git a/tests/smoke.spec.js b/e2e/smoke.spec.js similarity index 100% rename from tests/smoke.spec.js rename to e2e/smoke.spec.js diff --git a/package-lock.json b/package-lock.json index 3a793cb..193739a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@playwright/test": "^1.58.2", + "@playwright/test": "^1.59.1", "@types/node": "^25.5.0" } }, @@ -3483,12 +3483,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, "dependencies": { - "playwright": "1.58.2" + "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -14724,12 +14724,12 @@ } }, "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "dependencies": { - "playwright-core": "1.58.2" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -14742,9 +14742,9 @@ } }, "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/package.json b/package.json index 17ad2a3..9165a95 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@playwright/test": "^1.58.2", + "@playwright/test": "^1.59.1", "@types/node": "^25.5.0" } } diff --git a/playwright.config.ts b/playwright.config.ts index ffb9c38..290b11a 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,7 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ - testDir: './tests', + testDir: './e2e', fullyParallel: true, // Fail the build on CI if you accidentally left test.only in the code forbidOnly: !!process.env.CI, From f7fc4048928072cbc1dbcb1a8ecdd49e342cf4f0 Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sun, 5 Apr 2026 13:43:40 -0500 Subject: [PATCH 05/32] doc: test plan for structured tests --- TESTPLAN.md | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 TESTPLAN.md diff --git a/TESTPLAN.md b/TESTPLAN.md new file mode 100644 index 0000000..b1243ec --- /dev/null +++ b/TESTPLAN.md @@ -0,0 +1,454 @@ +# KinTree End-to-End Test Plan + +## 1. Purpose +This plan documents the primary user flows in KinTree and defines how each flow should be tested. It is designed to guide Playwright UI tests plus supporting API/integration checks. + +## 2. Scope +Application areas covered: +- Authentication and account lifecycle +- Profile management and account security +- Family member management and relationship linking +- Tree visualization and shared tree workflows +- Family event dashboard +- Settings, help, and chat pages + +## 3. Test Environments +- Client app: http://localhost:3000 +- Server API: http://localhost:5000 +- Auth/storage provider: Supabase + +Baseline test users: +- User A: existing registered account with at least one family member +- User B: existing registered account not yet linked to User A +- User C: account with shared trees received +- User D: account with MFA enabled + +## 4. Flow Catalog + +### Flow 1: Register a New Account + +Routes: +- /register +- /login + +APIs touched: +- POST /api/auth/sync + +Steps: +1. Open /register. +2. Fill required fields with valid values. +3. Submit Create Account. +4. Verify redirect to /login. + +Expected results: +- Registration succeeds and account metadata is persisted. +- Login page loads after registration. + +Negative coverage: +- Invalid email format rejected. +- Duplicate email returns visible error. + +### Flow 2: Login with Email and Password + +Routes: +- /login + +Auth behavior: +- Optional remember-me (stores email in local storage) +- Optional MFA challenge for users with verified TOTP + +Steps: +1. Open /login. +2. Enter valid credentials. +3. Submit Sign In. + +Expected results: +- Successful users land on /. +- 'Remember me' restores saved email on next login screen load. + +Negative coverage: +- Invalid credentials show error text. +- Protected routes redirect to /login if not authenticated. + +### Flow 3: Login with MFA (TOTP) + +Routes: +- /login + +Auth behavior: +- If a verified TOTP factor exists, user must verify a 6-digit code. + +Steps: +1. Login with valid username/password for MFA-enabled account. +2. Verify MFA challenge UI appears. +3. Enter valid TOTP code and submit. + +Expected results: +- User is redirected to / after successful verification. + +Negative coverage: +- Invalid code shows verification error and remains on MFA step. + +### Flow 4: Password Reset and Update Password + +Routes: +- /reset-password +- /update-password +- /login + +Steps: +1. Open /reset-password and submit a valid email. +2. Follow reset link to /update-password. +3. Enter a valid new password and submit. +4. Re-authenticate at /login with new password. + +Expected results: +- Reset request shows success message. +- Password update succeeds and redirects to /login. +- New password works for login. + +### Flow 5: View Own Profile Data + +Routes: +- /account/:id (own id) + +APIs touched: +- GET /api/auth/user/email/:email +- GET /api/auth/user/:id + +Steps: +1. Navigate to own account page. +2. Verify profile, personal info, and address sections render. + +Expected results: +- Profile fields are populated from database values. +- Page loads without fallback errors. + +### Flow 6: Edit Own Profile and Persist Changes + +Routes: +- /account/:id + +APIs touched: +- PUT /api/auth/profile +- POST /api/auth/upload-profile-picture + +Steps: +1. Enter edit mode for profile/personal/address sections. +2. Update fields and save. +3. Refresh page. + +Expected results: +- Saved values persist after refresh. +- Success feedback is shown. + +Negative coverage: +- Oversized image upload rejected. +- Non-image upload rejected. + +### Flow 7: Manage Account Security (MFA + Email Verification) + +Routes: +- /account/:id (own account) + +Steps: +1. Start authenticator setup. +2. Scan QR or use pending setup and enter TOTP code. +3. Verify authenticator status becomes enabled. +4. Disable authenticator and verify status toggles. +5. If email unverified, trigger resend verification email. + +Expected results: +- MFA state transitions are correct and visible. +- Verification resend returns success status. + +### Flow 8: Delete Account + +Routes: +- /account/:id +- /login + +APIs touched: +- DELETE /api/auth/remove/:id + +Steps: +1. Open own account page. +2. Click Delete account. +3. Confirm deletion in modal. + +Expected results: +- Account and related data are removed. +- User is logged out and redirected to /login. + +Negative coverage: +- Backend failure surfaces user-visible error and keeps modal flow recoverable. + +### Flow 9: View Family Directory and Open Member Profile + +Routes: +- /family +- /account/:id + +APIs touched: +- GET /api/family-members/user/:userId + +Steps: +1. Open /family. +2. Search for a member by first/last name. +3. Sort list and verify ordering. +4. Open member profile with View link. + +Expected results: +- Matching members appear in list. +- Navigation lands on target member profile. + +### Flow 10: Add Existing User as Family Member + +Routes: +- /tree (Add Family Member popup) +- /account/:id + +APIs touched: +- GET /api/family-members/user/:userId +- GET /api/auth/users +- GET /api/family-members/active/:id +- POST /api/family-members +- POST /api/relationships + +Steps: +1. Open tree page and launch Add Family Member popup. +2. Search and select an existing registered user. +3. Choose relationship type (and maternal/paternal side when required). +4. Submit add. + +Expected results: +- New tree member record is created. +- Relationship record is created. +- User is redirected to selected member profile. + +Negative coverage: +- Cannot add self. +- Cannot add a duplicate existing family member. + +### Flow 11: Add Manual (Non-Registered) Family Member + +Routes: +- /tree (Add Family Member popup) +- /account/:id + +APIs touched: +- GET /api/family-members/active/:id +- POST /api/family-members +- POST /api/relationships + +Steps: +1. In Add Family Member popup, switch to manual mode. +2. Enter member details and relationship. +3. Submit add. + +Expected results: +- Member is created with no linked user account. +- Relationship is created. +- User can edit the family member. + +### Flow 12: Remove Family Member. + +Routes: +- /account/:id + +APIs touched: +- GET /api/tree-info/:id +- GET /api/family-members/active/:id +- POST /api/family-members +- DELETE /api/relationships + +Steps: +1. Open /family. +2. Click on a member's 'View' button to open their account details. +3. Submit and verify delete. + +Expected results: +- Family member is removed from the tree and user's family page. +- Tree and member persist on failure with visible error message. + +### Flow 13: Share Tree with Another Member + +Routes: +- /tree/sharetree + +APIs touched: +- GET /api/family-members/user/:userId +- GET /api/auth/users +- GET /api/tree-info/:id +- POST /api/share-trees/share + +Steps: +1. Open share tree page. +2. Search/select a family member or send through email. +3. Submit share. + +Expected results: +- Shared tree record is created. +- Email is sent if selected. +- Sender is redirected to home/dashboard. + +Negative coverage: +- Missing required selected member or email blocks submit. + +### Flow 14: View Incoming Shared Trees and Open One + +Routes: +- /tree/viewsharedtrees +- /sharedtree/:id + +APIs touched: +- GET /api/share-trees/receiver/:id +- GET /api/auth/users +- GET /api/share-trees/:id + +Steps: +1. Login as receiving user. +2. Open shared trees list page. +3. Verify list includes incoming shares. +4. Click View Tree for a share. + +Expected results: +- Receiver can navigate to shared tree view. +- Sender identity and tree content display. + +### Flow 15: View Relationship Badge on Another User Profile + +Routes: +- /account/:id (other user) + +APIs touched: +- GET /api/relationships/between/:viewerId/:profileId + +Steps: +1. Open profile of another user/family member. +2. Observe relationship tag. + +Expected results: +- Relationship badge appears for linked members. +- Missing relationship returns neutral UI without crash. + +### Flow 15A: Add Existing Profile to Tree Structure + +Routes: +- /account/:id (other profile) +- /tree + +APIs touched: +- GET /api/family-members/user/:userId +- GET /api/tree-info/:id +- PUT /api/tree-info/:id + +Steps: +1. Open another profile not yet placed in your current tree structure. +2. Launch Add to Tree popup. +3. Search and choose an existing related member already in the tree. +4. Select relationship direction/type and submit. + +Expected results: +- Selected profile is added into tree object with correct relationship edge. +- Navigating to /tree shows the newly linked profile. + +Negative coverage: +- Prevent adding a profile already present in tree object. +- Show useful error if tree update fails. + +### Flow 16: Event Dashboard CRUD + +Routes: +- / +- /useractivitydash + +APIs touched: +- GET /api/events/:auth_uid +- POST /api/events +- PUT /api/events/:id +- DELETE /api/events/:id + +Steps: +1. Open dashboard and create a new event. +2. Verify event appears in list. +3. Edit event and verify updates. +4. Delete event and verify removal. +5. Search by title/date and toggle sort order. + +Expected results: +- CRUD operations persist correctly. +- Search and sort update list presentation as expected. + +### Flow 17: Backup and Restore (Unimplemented) + +Routes: +- Backend/API flow (UI trigger pending in Settings) + +APIs touched: +- POST /api/backup/:id +- POST /api/backup/restore/:id + +Steps: +1. Trigger backup for test user. +2. Mutate user family data. +3. Trigger restore. +4. Re-open family/tree views and verify restoration. + +Expected results: +- Backup payload captures account-related records. +- Restore rehydrates prior data snapshot. + +### Flow 17A: Merge Shared Tree Members and Assign Relationships (Unimplemented) + +Routes: +- /sharedtree/:id + +APIs touched: +- POST /api/share-trees/merge/:sharedTreeId +- POST /api/share-trees/assign-relationship + +Steps: +1. Open a shared tree as the receiver. +2. Select shared members to merge into receiver base tree. +3. Submit merge request. +4. Assign relationship types to newly merged members. + +Expected results: +- Merged members are persisted to receiver data. +- Relationship assignments are saved and reflected in tree/profile views. + +Negative coverage: +- Empty merge selection is rejected. +- Invalid sharedTreeId returns handled error. + +### Flow 18: Navigation and Informational Pages + +Routes: +- /websitesettings +- /help +- /chat + +Steps: +1. Open each route as authenticated user. +2. Verify core page sections load and navigation remains functional. + +Expected results: +- Settings renders controls (including dark mode toggle). +- Help FAQ content is visible. +- Chat placeholder content is visible. + +## 5. Cross-Cutting Assertions +Apply these checks in most UI flows: +- Auth guard behavior for protected routes +- User-friendly error messaging for failed API requests +- No console exceptions that break flow completion (crash) + +## 6. Traceability Matrix (Route/API to Flow) +- Auth and account: Flows 1-8 +- Family and profile viewing: Flows 9, 15 +- Tree and membership: Flows 10-12 +- Shared trees: Flows 13-14 +- Events: Flow 16 +- Backup/restore and shared merge operations: Flows 17-17A +- Support/settings placeholders: Flow 18 From fe0efefc2807a653abdec3103c42a761057ebf98 Mon Sep 17 00:00:00 2001 From: aambrose1 Date: Sun, 5 Apr 2026 14:08:56 -0500 Subject: [PATCH 06/32] added registration tests --- .../src/pages/CreateAccount/CreateAccount.js | 18 ++- e2e/pages/CreateAccount.js | 89 +++++++++++++++ e2e/pages/Login.js | 96 ++++++++++++++++ e2e/registration.spec.js | 107 ++++++++++++++++++ 4 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 e2e/pages/CreateAccount.js create mode 100644 e2e/pages/Login.js create mode 100644 e2e/registration.spec.js diff --git a/client/src/pages/CreateAccount/CreateAccount.js b/client/src/pages/CreateAccount/CreateAccount.js index 5928cd0..8054e19 100644 --- a/client/src/pages/CreateAccount/CreateAccount.js +++ b/client/src/pages/CreateAccount/CreateAccount.js @@ -11,8 +11,13 @@ const yupValidation = yup.object().shape( { firstname: yup.string().required("First name is a required field."), lastname: yup.string().required("Last name is a required field."), - birthdate: yup.date().required("Birthdate is a required field."), - gender: yup.string().oneOf(['M', 'F'], 'Please select a valid option').required('Gender field is required'), + birthdate: yup + .date() + .transform((value, originalValue) => (originalValue === '' ? null : value)) + .nullable() + .typeError("Birthdate is a required field.") + .required("Birthdate is a required field."), + gender: yup.string().oneOf(['M', 'F'], 'Gender field is required').required('Gender field is required'), email: yup.string().required("Email is a required field.") .matches( "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" @@ -43,7 +48,10 @@ const yupValidation = yup.object().shape( ); const CreateAccount = () => { - const { register, handleSubmit, formState: { errors } } = useForm({ resolver: yupResolver(yupValidation) }); + const { register, handleSubmit, formState: { errors } } = useForm({ + resolver: yupResolver(yupValidation), + defaultValues: { gender: '' } + }); const [errorMessage, setErrorMessage] = useState(""); const [isHovering, setIsHovering] = useState(false); @@ -125,8 +133,8 @@ const CreateAccount = () => {