Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/cleanup-test-users.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Cleanup Test Users

on:
schedule:
# Run daily at 3 AM UTC
- cron: '0 3 * * *'
workflow_dispatch: # Allow manual triggering

jobs:
cleanup:
name: Remove Old Test Users
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Cleanup test users older than 1 day
run: npm run cleanup:test-users -- --execute --yes --days=1 --limit=500
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}

- name: Notify on failure
if: failure()
run: |
echo "::warning::Test user cleanup failed. Check logs for details."
37 changes: 37 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Code Quality

on:
pull_request:
branches: [main, develop]
workflow_dispatch: # Allow manual triggering

jobs:
code-quality:
name: Code Quality Checks
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run linting
run: npm run lint

- name: Run type checking
run: npx tsc --noEmit

- name: Build check
run: npm run build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
Comment on lines +33 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid relying on secrets for PR builds.

On pull_request runs from forks, repository secrets resolve to empty strings, so this build step will fail and block external contributors. Please gate the build behind a same-repo check, switch to non-secret action variables, or provide a public fallback .env to keep the step green for forks.

🤖 Prompt for AI Agents
.github/workflows/code-quality.yml lines 33-37: the Build check step uses
repository secrets which are empty for forked PR runs and will cause the job to
fail; update the workflow to skip or alter this step for pull_request events
from forks by adding a same-repo conditional (e.g., only:
github.repository_owner == github.actor) or gate it behind paths/branches so it
only runs for same-repo pushes, or replace the secret usage with non-secret
action inputs or a committed public .env fallback used when secrets are empty;
make the build step read env variables with a fallback mechanism and/or add a
condition to prevent running the secret-backed build for external PRs.

3 changes: 0 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ jobs:
env:
CYPRESS_INSTALL_BINARY: 0

- name: Run linting
run: npm run lint

- name: Run tests with coverage
run: npm run test:coverage
env:
Expand Down
114 changes: 111 additions & 3 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,84 @@ test.describe('Feature Name', () => {
- Use `test.skip()` for temporarily disabled tests
- Use `test.only()` for debugging (remove before committing!)

### Test Data & User Management

**Dynamic User Creation:**
- Tests create fresh user accounts dynamically using unique timestamp-based emails (e.g., `test-1234567890@example.com`)
- No need to pre-seed test users in the database
- Each test run is isolated and independent
- Users are created via the actual signup flow, providing true E2E testing

**Example:**
```typescript
async function createTestUser(authPage: AuthPage) {
const timestamp = Date.now()
const user = {
name: 'Test User',
email: `test-${timestamp}@example.com`,
password: 'TestPassword123!',
}

// Create user via signup
await authPage.goto()
await authPage.switchToSignUp()
await authPage.signUp(user.name, user.email, user.password)

// Wait for redirect and clear session
await authPage.page.waitForURL(/\/dashboard/, { timeout: 10000 })
await authPage.page.context().clearCookies()

return user
}
```

**Test User Cleanup:**

Test users accumulate in the database over time. A secure cleanup script is provided:

```bash
# Dry-run (shows what would be deleted, safe to run anytime)
npm run cleanup:test-users

# Actually delete test users (requires confirmation)
npm run cleanup:test-users -- --execute

# Delete users older than 14 days
npm run cleanup:test-users -- --execute --days=14

# Limit to 50 users max
npm run cleanup:test-users -- --execute --limit=50

# Skip confirmation (for CI/CD automation)
npm run cleanup:test-users -- --execute --yes
```

**Safety Features:**
- ✅ Dry-run mode by default (won't delete unless `--execute` is specified)
- ✅ Only deletes users matching exact pattern: `test-{timestamp}@example.com`
- ✅ Refuses to run in production environment (`NODE_ENV=production`)
- ✅ Warns if Supabase URL doesn't look like localhost
- ✅ Age-based filtering (default: only deletes users older than 7 days)
- ✅ Batch size limiting (default: 100 users, max: 500)
- ✅ Requires confirmation prompt before deletion
- ✅ Detailed logging of all operations
- ✅ Uses Supabase Admin API for proper user deletion

**Requirements:**
- Add `SUPABASE_SERVICE_ROLE_KEY` to `.env.local` (found in Supabase Dashboard > Settings > API)
- This key has elevated permissions - keep it secret and never commit it

**For CI/CD:**
- A daily cron job runs automatically via GitHub Actions (3 AM UTC)
- Deletes test users older than 1 day
- No manual intervention required
- Can trigger manually if needed via Actions tab

**Test Data Fixtures:**
- Static test data stored in `e2e/fixtures/*.json`
- Use for non-user test data (settings, configurations, etc.)
- Avoid using fixtures for user accounts - create them dynamically instead

## Debugging

### VS Code Integration
Expand Down Expand Up @@ -341,11 +419,41 @@ npm run test:e2e:debug
### GitHub Actions

Tests run automatically on:
- Pull requests
- Push to main branch
- Pull requests to main/develop branches
- Manual workflow dispatch

Configuration in `.github/workflows/playwright.yml` (to be created)
**Workflows:**
- `.github/workflows/e2e.yml` - Runs E2E tests on PRs
- `.github/workflows/cleanup-test-users.yml` - Daily cleanup of old test users

### Required GitHub Secrets

For E2E tests and cleanup to work in CI/CD, add these secrets to your repository:

Navigate to: Settings → Secrets and variables → Actions → New repository secret

1. `NEXT_PUBLIC_SUPABASE_URL`
- Your Supabase project URL
- Found in: Supabase Dashboard → Settings → API → Project URL

2. `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- Your Supabase anonymous/public key
- Found in: Supabase Dashboard → Settings → API → Project API keys → anon/public

3. `SUPABASE_SERVICE_ROLE_KEY` ⚠️
- Your Supabase service role key (has admin permissions)
- Found in: Supabase Dashboard → Settings → API → Project API keys → service_role
- **IMPORTANT:** This key has elevated permissions - never expose it publicly

### Automated Test User Cleanup

**Daily Scheduled Cleanup:**
- Runs daily at 3 AM UTC via GitHub Actions cron
- Deletes test users older than 1 day
- Processes up to 500 users per run
- Uses `--execute --yes --days=1 --limit=500` flags
- Can be triggered manually via "Actions" tab → "Cleanup Test Users" → "Run workflow"
- Keeps database clean without manual intervention

### Parallel Execution

Expand Down
Loading
Loading