From 9bd347455aa446993e3c3ef7c6bedd2e14bf35b7 Mon Sep 17 00:00:00 2001 From: Justin Pham Date: Sat, 25 Jul 2026 13:42:57 -0600 Subject: [PATCH 1/9] docs: add portal end-to-end testing findings and implementation plan --- docs/portal-e2e-implementation-plan.md | 282 ++++++++++++++++++ docs/portal-e2e-testing-findings-and-plan.md | 283 +++++++++++++++++++ 2 files changed, 565 insertions(+) create mode 100644 docs/portal-e2e-implementation-plan.md create mode 100644 docs/portal-e2e-testing-findings-and-plan.md diff --git a/docs/portal-e2e-implementation-plan.md b/docs/portal-e2e-implementation-plan.md new file mode 100644 index 0000000..ebbbc8b --- /dev/null +++ b/docs/portal-e2e-implementation-plan.md @@ -0,0 +1,282 @@ +# Portal E2E implementation plan + +## Objective + +Add a reliable Playwright end-to-end suite for the authenticated portal. The initial release will exercise Recruitment CRUD through the real UI and run on every trusted pull request using a fresh PostgreSQL database in GitHub Actions. + +## Chosen architecture + +```text +GitHub Actions (public repository; standard Ubuntu runner) +├── PostgreSQL 15 service container +├── Prisma generate + migrate deploy +├── Next.js application started by Playwright +└── Playwright Chromium + └── Clerk development test administrator +``` + +The database is job-scoped and destroyed with the runner. Tests must never connect to the production database, production Clerk application, or production Supabase storage. + +## Proof of concept — required before full implementation + +The POC validates the four uncertain integrations before the team invests in broad portal coverage: + +1. GitHub Actions can start the application and a temporary PostgreSQL 15 database together. +2. Prisma migrations work against an empty job-scoped database. +3. Clerk's Playwright helper can sign in a dedicated development administrator. +4. A browser test can make and remove one Recruitment record through the real portal UI. + +### POC boundaries + +The POC is intentionally small. It should contain one Chromium project, one manually dispatched GitHub Actions workflow, and two tests: + +| Test | What it proves | +| --- | --- | +| Signed-in administrator opens `/portal/recruitment` | Clerk authentication, role metadata, portal authorization, and application startup work together. | +| Administrator creates a uniquely named Recruitment record and deletes it | The rendered UI, server action, Prisma, temporary PostgreSQL database, and confirmation dialog work end to end. | + +The POC does **not** need an update flow, a required PR check, cross-browser testing, retries, Vercel previews, Supabase uploads, or Clerk invitation coverage. It should be launched with `workflow_dispatch` and run locally before being connected to normal pull-request triggers. + +### POC deliverables + +- Playwright and Clerk testing dependencies. +- Minimal `playwright.config.ts` with Chromium and a local Next.js web server. +- One authentication setup file that stores test state only in ignored Playwright output. +- One `portal-poc.spec.ts` file containing the two tests above. +- One `.github/workflows/portal-e2e-poc.yml` workflow with Postgres 15, migrations, and manual dispatch. +- Failure-only HTML report and trace artifacts with short retention. +- A short README note listing the required GitHub secrets and local command. + +### POC success criteria and decision gate + +The POC is successful when all of the following are true: + +- [ ] The workflow starts from a manual dispatch and completes on a standard public-repository GitHub runner. +- [ ] The test database starts empty and is discarded after the job. +- [ ] Prisma migrations complete without using a production or shared database URL. +- [ ] The Clerk development administrator reaches `/portal/recruitment`. +- [ ] The Recruitment record is visible after creation and absent after deletion. +- [ ] A forced failure produces a useful trace or HTML report. +- [ ] The workflow completes consistently across three consecutive runs. + +If any criterion fails, stop expansion and resolve that integration first. Only after this gate passes should the team add update coverage, normal PR triggers, retries, and the broader portal suite. + +## Scope + +### In scope for the full initial implementation + +- Playwright configuration and Chromium-only execution. +- Clerk-assisted administrator sign-in using a dedicated development account. +- A Postgres 15 service container in a GitHub Actions workflow. +- Prisma generation and migration deployment against the temporary database. +- Portal authentication and authorization smoke tests. +- One full Recruitment CRUD test: create, read, update, and delete. +- Failure artifacts: Playwright HTML report, trace, screenshot, and video. + +### Deferred + +- Supabase image uploads. +- Clerk invitations, Clerk user deletion, and any other externally destructive identity operations. +- Cross-browser coverage. +- Parallel workers and sharding. +- Vercel preview-deployment testing. +- Replacing Clerk, Supabase, Vercel, or the project database architecture. + +## Prerequisites and decisions + +### 1. Normalize the package manager + +The project uses Yarn 4.18.0 through Corepack with the `node-modules` linker. Keep `packageManager`, `.yarnrc.yml`, the lockfile, README, and CI setup aligned. The E2E workflow must enable Corepack and use immutable installation. + +### 2. Create a Clerk development test application + +Create a separate Clerk **development** application specifically for browser tests. Create one permanent user and assign an existing portal administrator role through `publicMetadata.role`. + +Required GitHub repository secrets: + +| Secret | Purpose | +| --- | --- | +| `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Loads the Clerk frontend in the test app. | +| `CLERK_SECRET_KEY` | Lets Clerk's Playwright helper establish a test session. | +| `E2E_ADMIN_EMAIL` | Identifies the dedicated administrator account. | + +The current environment schema also expects Supabase, webhook, bucket, and flag values. Supply non-production test values only where the application requires a value during startup. Do not enable image-upload tests until a dedicated test storage bucket and cleanup policy exist. + +### 3. Protect test credentials + +- Never commit `.env` files, browser storage state, test passwords, or Clerk keys. +- Run the authenticated workflow only for trusted repository branches and pull requests. Forked pull requests do not receive secrets. +- Do not use `pull_request_target` to expose secrets to untrusted pull-request code. +- Keep the Clerk test application free of production users and meaningful data. + +## Planned repository changes + +| Area | Planned change | +| --- | --- | +| `package.json` and lockfile | Add `@playwright/test` and `@clerk/testing`; add `test:e2e`, `test:e2e:ui`, and `test:e2e:ci` scripts. | +| `playwright.config.ts` | Configure Chromium, `baseURL`, a Next.js `webServer`, one CI worker, retries, traces, screenshots, video, and HTML reporting. | +| `tests/e2e/setup.ts` | Sign in with the Clerk test administrator and save temporary test storage state. | +| `tests/e2e/portal/auth.spec.ts` | Verify protected portal routes redirect while signed out and load for an administrator. | +| `tests/e2e/portal/recruitment.spec.ts` | Verify Recruitment create, read, update, and delete through the UI. | +| `.github/workflows/portal-e2e.yml` | Start Postgres, apply migrations, install the browser, run E2E tests, and upload failure artifacts. | +| `.gitignore` | Ignore Playwright output and temporary authentication state if not already ignored. | +| README or testing documentation | Document local prerequisites, commands, secrets, and debugging steps. | + +## Phase 1 — POC foundation + +### Dependencies + +Add Playwright and Clerk's Playwright testing package as development dependencies. Pin or lock versions through the project lockfile. Playwright’s installed browser version must match the test package version. + +### Playwright configuration + +Create `playwright.config.ts` with these POC policies: + +- Run Chromium only. +- Use one worker in CI; CRUD tests mutate shared application state. +- Do not add retries during the POC; fix any first-run instability directly. +- Start the application with Playwright `webServer` rather than requiring a manually started server. +- Reuse a manually running local server when not in CI. +- Capture a trace on failure. +- Capture screenshot and video only on failure. +- Output the HTML report to `playwright-report/` and raw test data to `test-results/`. + +Use `http://127.0.0.1:3000` as the initial base URL. Do not test a Vercel preview in this phase. + +### Authentication setup + +Use Clerk's Playwright helper rather than filling the sign-in UI: + +1. Open a public route that loads Clerk. +2. Establish a test session for `E2E_ADMIN_EMAIL`. +3. Save storage state to a Playwright output directory, never to a tracked file. +4. Configure authenticated portal projects to use that state. + +The test administrator must have a role recognized by the portal’s existing `adminClerkRoles` guard. Validate this before writing CRUD tests. + +### Environment strategy + +CI must set: + +```text +NODE_ENV=test +DATABASE_URL=postgresql://postgres:password@127.0.0.1:5432/portal_e2e +DIRECT_URL=postgresql://postgres:password@127.0.0.1:5432/portal_e2e +NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= +CLERK_SECRET_KEY= +E2E_ADMIN_EMAIL= +``` + +Set any remaining required runtime variables to dedicated non-production values. Prefer valid dummy URLs and dedicated bucket names over bypassing environment validation globally. + +## Phase 2 — POC workflow + +Create `.github/workflows/portal-e2e-poc.yml`. + +### POC trigger + +- `workflow_dispatch` for manual investigation. + +Do not add `pull_request` or `push` triggers until the POC success criteria are met. + +### Job outline + +1. Run on `ubuntu-latest` using a standard GitHub-hosted runner. +2. Start a `postgres:15` service container with a database health check. +3. Check out the revision. +4. Set up the selected Node and Yarn version. +5. Install locked dependencies. +6. Run `prisma generate` and `prisma migrate deploy` using the service database URLs. +7. Install the Playwright Chromium browser and its Linux dependencies. +8. Run `yarn test:e2e:ci`. +9. Upload `playwright-report/` and `test-results/` only when the test job fails. + +Set artifact retention to five to seven days. The public repository receives free use of standard GitHub-hosted runners; the job should not request a larger runner. + +## Phase 3 — POC tests + +### Authentication and access test + +Create a test that verifies: + +1. A signed-out visit to `/portal/recruitment` redirects to the Clerk sign-in route. +2. A signed-in administrator can open `/portal/recruitment`. +3. The Recruitment table and its primary controls are visible. + +### Recruitment create-and-delete test + +Use an identifier such as `E2E Recruitment -` to ensure every run creates unique data. + +1. Navigate to `/portal/recruitment` as the administrator. +2. Activate the control labelled `Add recruitment form`. +3. Fill Header, Description, Link, and Expires At. +4. Save and assert the creation success toast and newly created table row. +5. Open Delete on that row, confirm deletion, and assert the deletion success toast and absence of the row. + +Use role, label, heading, and button-name locators. Do not use CSS module class names or positional selectors. Scope Edit and Delete interactions to the unique table row so unrelated data cannot be changed. + +### Cleanup + +The temporary CI database is automatically discarded. For local runs, perform the deletion through the UI and add defensive `afterEach` cleanup if a test can fail before its delete step. The suite must not use the existing Faker seed because its safety guard only accepts the developer-local database URL. + +## Phase 4 — Promote the POC to the full suite + +After the POC passes its decision gate: + +1. Rename the workflow to `portal-e2e.yml` and add trusted `pull_request` and default-branch `push` triggers. +2. Add two CI retries, trace-on-first-retry behaviour, and the final artifact policy. +3. Split the POC test into focused authentication and Recruitment CRUD specs. +4. Add the Recruitment update assertion. +5. Make the workflow a required pull-request check after several stable runs. + +## Phase 5 — Quality gates + +Before marking the first implementation complete: + +- [ ] `yarn test:e2e` runs locally against a local temporary or dedicated test Postgres database. +- [ ] `yarn test:e2e:ci` works with only CI environment variables and no manually running server. +- [ ] Prisma migrations apply successfully to an empty Postgres 15 database. +- [ ] Signed-out protection is verified. +- [ ] Administrator access is verified. +- [ ] Recruitment CRUD, including update, passes reliably through the rendered portal. +- [ ] A forced test failure produces a readable HTML report, trace, and screenshot/video artifact. +- [ ] No test points at production database, Clerk, or Supabase resources. +- [ ] The workflow duration and artifact size are recorded after several runs. + +## Phase 6 — Incremental coverage + +Add one area at a time after the Recruitment flow is stable: + +1. Our Work/timeline CRUD without image upload. +2. Sponsor CRUD without image replacement. +3. Alumni CRUD. +4. Team-member update and soft deletion. +5. Member, unverified, and administrator authorization boundaries. + +For each area, add tests only after identifying stable accessible controls. If an operation is difficult to target safely, first add semantic labels or `data-testid` attributes to the UI rather than relying on implementation-specific selectors. + +## Future integration suite + +Create a separate manually dispatched or scheduled workflow before testing external mutations: + +- Use a dedicated Supabase test project or bucket for file uploads. +- Generate unique upload paths and remove every uploaded object during teardown. +- Use a dedicated Clerk development application for invitations and user administration. +- Record external test data identifiers for cleanup and auditability. +- Keep this suite separate from pull-request merge checks. + +## Risks and mitigations + +| Risk | Mitigation | +| --- | --- | +| Clerk configuration or account role is incorrect | Add the authenticated navigation smoke test before CRUD coverage. | +| Secrets are unavailable to fork PRs | Run authenticated tests only for trusted changes; provide a separate non-secret smoke check for forks if needed. | +| Flaky tests due to shared mutation | Use one worker, unique data, row-scoped locators, and retry evidence. | +| CI environment validation fails | Provide complete dedicated test values; do not broadly skip validation. | +| Database changes leak to a shared service | Use only the service-container connection URL in CI. | +| Artifact storage grows | Upload only on failure and use short retention. | +| Yarn version drift | Resolve the package-manager mismatch before CI becomes required. | + +## Definition of done + +The first implementation is complete when the Recruitment CRUD test runs reliably on trusted pull requests and the default branch, each run starts with a fresh Postgres database, failure artifacts are available for debugging, and the job remains within the public-repository standard-runner free tier. diff --git a/docs/portal-e2e-testing-findings-and-plan.md b/docs/portal-e2e-testing-findings-and-plan.md new file mode 100644 index 0000000..3b9afd5 --- /dev/null +++ b/docs/portal-e2e-testing-findings-and-plan.md @@ -0,0 +1,283 @@ +# Portal End-to-End Testing + +## Findings, cost assessment, and implementation plan + +**Audience:** Calgary Solar Car website team +**Repository:** `UCSolarCarTeam/Solar-Car-Website-Next` (public) +**Decision:** Adopt the best-value GitHub Actions approach +**Expected cost:** $0 per month under current assumptions +**Prepared:** July 25, 2026 + +## Recommendation + +Run Playwright and a temporary PostgreSQL database together on standard GitHub-hosted Ubuntu runners and use a Clerk development application for authentication. + +This gives us browser-level coverage of the portal’s highest-risk workflows without introducing recurring infrastructure costs or exposing production data. + +## Executive summary + +The portal contains several administrator-facing create, read, update, and delete workflows. Browser-level tests are appropriate because they exercise the complete path from the rendered interface through server actions and Prisma to PostgreSQL. This catches integration failures that component tests alone may miss. + +The repository is public and is expected to remain public. Standard GitHub-hosted runners are therefore free for this workload. A PostgreSQL service container can live for exactly one workflow job and disappear afterward, eliminating both hosted-database cost and the risk of modifying production data. + +The recommended first milestone is one complete Recruitment CRUD flow. It provides meaningful coverage without invoking Supabase image storage or destructive Clerk administration. Once reliable, the same pattern can expand to timeline entries, sponsors, alumni, team records, and role-based authorization. + +## Decision and expected cost + +The selected approach is designed to have no recurring infrastructure cost. It uses services already present in the project only where they provide essential behaviour. + +| Component | Selected approach | Expected monthly cost | +| --- | --- | ---: | +| CI runner | Standard Ubuntu GitHub-hosted runner | $0 | +| Browser tests | Playwright with Chromium | $0 | +| Test database | PostgreSQL 15 service container | $0 | +| Application server | Next.js started inside the runner | $0 | +| Authentication | Clerk development app and one test admin | $0 | +| Object storage | Excluded from initial CRUD suite | $0 | +| **Total** | **Recommended initial implementation** | **$0/month** | + +This estimate assumes the repository remains public, uses standard runners, uploads artifacts only after failures, and authenticates against a dedicated development environment. + +### GitHub Actions limits + +GitHub provides free standard hosted-runner usage for public repositories. Larger runners remain billable and are not required for this test suite. + +Artifact storage is the more relevant constraint. To remain comfortably within the included allowance: + +- Upload traces, screenshots, and videos only when a test fails. +- Retain failure artifacts for five to seven days. +- Do not upload the Next.js build as an artifact. +- Do not record video for successful tests. + +The expected workflow duration is approximately 5–12 minutes: + +- Dependency installation: 1–3 minutes +- Prisma generation and migrations: less than 1 minute +- Next.js build or startup: 2–5 minutes +- Chromium tests: 1–4 minutes + +Actual duration should be measured after the first test is implemented. + +## Why the database should follow the runner lifecycle + +- **Isolation:** Every job receives a clean database and cannot collide with another run. +- **Safety:** Destructive tests cannot alter production or shared staging data. +- **Migration coverage:** Prisma migrations are applied from zero during CI, exposing broken migrations early. +- **Cost control:** PostgreSQL consumes resources already included in the runner instead of requiring a continuously hosted database. +- **Fidelity:** Tests use PostgreSQL 15, matching the project’s database family instead of substituting SQLite. + +The database should be created as a GitHub Actions service container. It starts with the job and is automatically destroyed when the job ends. No database backup, persistent volume, or external database account is required. + +## Proposed test architecture + +A single GitHub Actions job contains the application, browser, and database. The only external dependency required by the core suite is a low-value Clerk development application used to establish an authenticated administrator session. + +```text +GitHub Actions job +├── Playwright + Chromium +│ └── Drives portal workflows and captures failure evidence +├── Next.js application +│ └── Serves the exact pull-request revision under test +└── PostgreSQL 15 service container + └── Stores isolated test data and validates Prisma migrations + +External development dependency +└── Clerk development application + └── Authenticates a dedicated test administrator +``` + +## CI execution sequence + +1. Check out the pull-request revision. +2. Install the project’s locked Node and Yarn dependencies. +3. Start PostgreSQL 15 and wait for its health check. +4. Generate the Prisma client and apply all migrations. +5. Start the Next.js application through Playwright’s web-server configuration. +6. Authenticate a dedicated administrator through Clerk’s testing helper. +7. Run the Chromium portal suite with one worker. +8. Upload traces, screenshots, and video only when a test fails. +9. Destroy the runner and its database automatically when the job ends. + +## Authentication model + +Create one permanent administrator in a Clerk development application and assign the public metadata role required by the portal. + +CI receives these values through GitHub Actions secrets: + +- Clerk development publishable key +- Clerk development secret key +- Test administrator email + +Playwright uses Clerk’s official testing helper to establish a session. No password or saved browser state is committed. + +The Clerk test application must contain no production users or valuable data. Authenticated tests should run only for trusted repository changes. Forked pull requests do not receive repository secrets and therefore cannot run the authenticated suite without an explicit trusted workflow. + +## Test scope and rollout + +### Phase 1 — Foundation + +Add: + +- Playwright configuration +- Clerk authentication setup +- PostgreSQL CI service +- Failure artifacts +- An unauthenticated portal redirect test +- An authenticated portal navigation test + +**Outcome:** One repeatable test command that works locally and in CI. + +### Phase 2 — First CRUD flow + +Cover the Recruitment portal: + +1. Sign in as the test administrator. +2. Open `/portal/recruitment`. +3. Create a uniquely named recruitment form. +4. Confirm it appears in the table. +5. Edit its header or description. +6. Confirm the updated value appears. +7. Delete it through the confirmation dialog. +8. Confirm it no longer appears. + +Recruitment is the preferred first flow because it does not require image storage or destructive Clerk operations. + +**Outcome:** One complete browser-to-database vertical slice. + +### Phase 3 — Core portal expansion + +Add coverage incrementally for: + +- Our Work and timeline entries without image uploads +- Sponsors without image replacement +- Alumni records +- Team-member database records + +Each test should create uniquely named data and clean it up. + +**Outcome:** Coverage of the portal’s highest-value database workflows. + +### Phase 4 — Authorization + +Confirm: + +- Administrators can access administrator routes. +- Ordinary members are redirected or denied. +- Unauthenticated visitors are redirected to sign-in. +- Unverified accounts cannot access protected portal content. + +**Outcome:** Protection against role, middleware, and routing regressions. + +### Phase 5 — Optional external integrations + +Add separately controlled tests for: + +- Supabase image uploads +- Clerk invitations +- Clerk user management + +These tests should not run as part of every pull request. They mutate external services and require additional cleanup and credentials. + +**Outcome:** External-service coverage without destabilizing the core suite. + +## Test design standards + +- Use accessible locators such as roles, labels, headings, and button names instead of CSS-module class names. +- Give every created record a unique identifier so concurrent or repeated runs cannot collide. +- Run CRUD tests with one Playwright worker until test-data partitioning supports safe parallelism. +- Clean up through the user interface when deletion is part of the behaviour under test. +- Add defensive teardown where practical so an interrupted test does not contaminate later local runs. +- Treat unexpected page exceptions, failed server actions, and relevant unhandled browser console errors as failures. +- Record traces on the first retry. +- Retain screenshots or video only for failures. +- Prefer deterministic test fixtures over the existing Faker-based local seed. +- Never depend on production data being present. + +## What is deliberately excluded at first + +Image-upload behaviour is excluded because it introduces Supabase storage, file cleanup, and additional credentials. + +Clerk invitation and user deletion are excluded from the ordinary pull-request suite because they mutate an external identity system. These behaviours can be added later through dedicated test environments and separately triggered workflows. + +Vercel preview deployments are not required for the main suite. Testing the application built directly inside CI provides faster feedback and keeps the temporary application and database in one lifecycle. + +A small read-only smoke suite may eventually run after deployment if deployment-specific assurance becomes valuable. + +## Portability across CI providers + +GitHub Actions and Azure Pipelines both use YAML, but their workflow schemas are not interchangeable. Portability should live in the project command, not in the YAML. + +The repository should expose one command that: + +1. Applies database migrations. +2. Starts the application. +3. Runs Playwright. +4. Returns a standard success or failure exit code. + +A future Azure, CircleCI, or AWS workflow should start a PostgreSQL service and invoke the same command. + +If stronger portability becomes necessary, the application, browser, and database can later be packaged with Docker Compose. That additional complexity is not required for the initial GitHub implementation. + +## Service decisions + +### GitHub Actions + +**Selected.** + +The repository is permanently public, so standard GitHub-hosted runner usage is free. It is already the repository’s CI platform and has first-class PostgreSQL service-container support. + +### Temporary PostgreSQL + +**Selected.** + +PostgreSQL runs inside the GitHub job and disappears when the job ends. This is safer and cheaper than using a shared hosted test database. + +### Clerk + +**Selected for authentication.** + +Use a dedicated development application and one test administrator. There is no reason to replace Clerk or purchase a paid plan solely for this test suite. + +## Cost controls and operational safeguards + +| Area | Safeguard | +| --- | --- | +| Runner | Use standard Ubuntu runners; larger runners are billable. | +| Triggering | Run once per pull-request revision and on the main branch; avoid duplicate push and PR runs. | +| Artifacts | Upload only failure evidence and retain it for five to seven days. | +| Database | Use the job-scoped PostgreSQL container; never provide a production database URL. | +| Authentication | Use a dedicated Clerk development application and minimal test account. | +| External mutations | Keep invitations and identity deletion in a controlled, separately triggered suite. | + +## Implementation checklist + +- [ ] Create a dedicated Clerk development application and test administrator. +- [ ] Add Playwright and Clerk testing dependencies. +- [ ] Configure Chromium, one CI worker, retries, traces, screenshots, and failure-only video. +- [ ] Add the PostgreSQL 15 service and Prisma migration step to GitHub Actions. +- [ ] Implement unauthenticated redirect and authenticated portal navigation checks. +- [ ] Implement Recruitment create, read, update, and delete. +- [ ] Set short artifact retention and protect authentication secrets. +- [ ] Measure actual workflow duration before expanding coverage. +- [ ] Add the remaining database-backed portal areas incrementally. + +## Definition of done + +- [ ] The same Playwright command runs locally and in GitHub Actions. +- [ ] Every CI run receives a fresh PostgreSQL database. +- [ ] All Prisma migrations apply successfully to an empty database. +- [ ] Recruitment CRUD passes through the real portal interface. +- [ ] No production Clerk, Supabase, or database credentials are present. +- [ ] Failure artifacts are available without exceeding routine storage allowances. +- [ ] The pull-request check is stable enough to serve as a merge requirement. +- [ ] Expected recurring infrastructure cost remains $0 per month. + +## Sources + +- [GitHub Actions billing and included usage](https://docs.github.com/en/billing/concepts/product-billing/github-actions) +- [GitHub PostgreSQL service containers](https://docs.github.com/en/actions/tutorials/use-containerized-services/create-postgresql-service-containers) +- [Playwright CI guidance](https://playwright.dev/docs/ci) +- [Playwright authentication guidance](https://playwright.dev/docs/auth) +- [Playwright web-server configuration](https://playwright.dev/docs/test-webserver) +- [Clerk Playwright test helpers](https://clerk.com/docs/guides/development/testing/playwright/test-helpers) +- [Clerk pricing](https://clerk.com/pricing) From 9275b9966a13064ba63171327af5e6c947740e12 Mon Sep 17 00:00:00 2001 From: Justin Pham Date: Wed, 29 Jul 2026 13:06:57 -0600 Subject: [PATCH 2/9] Refactor code structure for improved readability and maintainability --- .env.example | 3 + .gitignore | 4 + .yarnrc.yml | 2 + README.md | 5 +- next.config.js | 5 + package.json | 7 +- playwright.config.ts | 43 + src/app/_components/Modals/ConfirmModal.tsx | 11 +- .../EditRecruitmentFormCell/EditFormPopup.tsx | 12 +- .../EditRecruitmentFormCell/index.tsx | 11 +- .../Portal/recruitment/RecruitmentTable.tsx | 17 +- tests/e2e/auth.setup.ts | 29 + tests/e2e/portal-poc.spec.ts | 47 + yarn.lock | 16651 ++++++++++------ 14 files changed, 10395 insertions(+), 6452 deletions(-) create mode 100644 .yarnrc.yml create mode 100644 playwright.config.ts create mode 100644 tests/e2e/auth.setup.ts create mode 100644 tests/e2e/portal-poc.spec.ts diff --git a/.env.example b/.env.example index d988fc9..eb5481a 100644 --- a/.env.example +++ b/.env.example @@ -28,6 +28,9 @@ DIRECT_URL="postgresql://postgres:password@localhost:5432/solar-car-website-next FLAGS_SECRET=exampleFlagSecret FLAGS=exampleFlag +# Playwright +E2E_ADMIN_EMAIL="admin@example.com" + # If you run Postgres locally via `docker-compose up -d`, the example DATABASE_URL # and DIRECT_URL above will work as-is. To create a local runtime env file from # this example run: diff --git a/.gitignore b/.gitignore index 9c7ef68..fe006b5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,10 @@ # testing /coverage +/playwright-report/ +/test-results/ +/playwright/.auth/ +.yarn/install-state.gz # database /prisma/db.sqlite diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..9b53a57 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,2 @@ +enableScripts: true +nodeLinker: node-modules diff --git a/README.md b/README.md index 295da87..8d0b055 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ To ensure that Clerk syncs with our Supabase database, follow these steps for th - Navigate to: **Configure** > **Webhooks**. - Edit the Webhook URL: Append `/api/webhooks` to your public URL. **Example:** + ``` https://rngwz-XXX-XXX-XXX-XXX.a.free.pinggy.link/api/webhooks ``` @@ -167,7 +168,7 @@ To ensure that Clerk syncs with our Supabase database, follow these steps for th - **tRPC:** Provides end-to-end type safety between the client and the server. - **ESLint & Prettier:** Ensures high-quality, consistent code throughout the project. -- **Framer Motion:** Used to animate images and carousels. Find the documentation here --> https://motion.dev/ +- **Framer Motion:** Used to animate images and carousels. Find the documentation here --> ## 🛠️ Code Quality @@ -188,11 +189,13 @@ docker-compose up -d - Create a local `.env` from the example (then edit secrets if needed): PowerShell: + ```powershell copy .env.example .env ``` bash / WSL / git-bash: + ```bash cp .env.example .env ``` diff --git a/next.config.js b/next.config.js index 0040aa3..0ed7291 100644 --- a/next.config.js +++ b/next.config.js @@ -44,6 +44,11 @@ const config = { }, ], }, + allowedDevOrigins: [ + "http://localhost:3000", + "http://localhost:3001", + "127.0.0.1", + ], reactCompiler: true, reactStrictMode: true, transpilePackages: ["geist"], diff --git a/package.json b/package.json index f3cebe4..d5ce7a8 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,9 @@ "format": "biome format --write .", "lint": "biome check .", "start": "next start", + "test:e2e": "playwright test", + "test:e2e:ci": "playwright test --reporter=line,html", + "test:e2e:ui": "playwright test --ui", "typecheck": "tsc --noEmit" }, "dependencies": { @@ -74,8 +77,10 @@ }, "devDependencies": { "@biomejs/biome": "^2.5.3", + "@clerk/testing": "^2.2.14", "@faker-js/faker": "^8.0.2", "@next/bundle-analyzer": "^16.2.10", + "@playwright/test": "^1.62.0", "@types/node": "^20.14.10", "@types/pg": "^8.20.0", "@types/react": "19.2.10", @@ -90,5 +95,5 @@ "tsx": "^4.22.0", "typescript": "7.0.2" }, - "packageManager": "yarn@1.22.22" + "packageManager": "yarn@4.18.0" } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..2f2ad6f --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,43 @@ +import { defineConfig, devices } from "@playwright/test"; + +const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000"; +const authFile = "playwright/.auth/admin.json"; + +export default defineConfig({ + forbidOnly: Boolean(process.env.CI), + outputDir: "test-results", + reporter: [ + ["html", { open: "never", outputFolder: "playwright-report" }], + ["list"], + ], + testDir: "./tests/e2e", + timeout: 30_000, + use: { + baseURL, + screenshot: "only-on-failure", + trace: "retain-on-failure", + video: "retain-on-failure", + }, + webServer: { + command: "yarn dev --port 3000", + reuseExistingServer: !process.env.CI, + timeout: 120_000, + url: baseURL, + }, + workers: 1, + projects: [ + { + name: "setup", + testMatch: /.*\.setup\.ts/, + }, + { + dependencies: ["setup"], + name: "chromium", + testIgnore: /.*\.setup\.ts/, + use: { + ...devices["Desktop Chrome"], + storageState: authFile, + }, + }, + ], +}); diff --git a/src/app/_components/Modals/ConfirmModal.tsx b/src/app/_components/Modals/ConfirmModal.tsx index cb6c61c..4d567f7 100644 --- a/src/app/_components/Modals/ConfirmModal.tsx +++ b/src/app/_components/Modals/ConfirmModal.tsx @@ -24,9 +24,16 @@ const ConfirmModal = ({ if (!open) return null; return ( -
+
-

{title}

+

+ {title} +

{message}

diff --git a/src/app/_components/PortalComponents/EditRecruitmentFormCell/EditFormPopup.tsx b/src/app/_components/PortalComponents/EditRecruitmentFormCell/EditFormPopup.tsx index 1b79d10..969660c 100644 --- a/src/app/_components/PortalComponents/EditRecruitmentFormCell/EditFormPopup.tsx +++ b/src/app/_components/PortalComponents/EditRecruitmentFormCell/EditFormPopup.tsx @@ -104,10 +104,18 @@ const EditFormPopup = ({ }; return ( -
+
-

{newForm ? "New Form" : "Edit Form"}

+

+ {newForm ? "New Form" : "Edit Form"} +

{newRowData && (
diff --git a/src/app/_components/PortalComponents/EditRecruitmentFormCell/index.tsx b/src/app/_components/PortalComponents/EditRecruitmentFormCell/index.tsx index ae1b3ea..8baaab0 100644 --- a/src/app/_components/PortalComponents/EditRecruitmentFormCell/index.tsx +++ b/src/app/_components/PortalComponents/EditRecruitmentFormCell/index.tsx @@ -42,13 +42,14 @@ const EditRecruitmentFormCell = ({ if (newForm) { return ( <> - + type="button" + > +