Implement Password Grant Flow for Authentication and Enhance User Experience
- Introduced Password Grant Flow for user authentication, allowing direct login with email and password. - Updated `useAuth` composable to manage login and logout processes, including Single Sign-Out from Cidaas. - Enhanced user interface with a new `UserMenu` component displaying user information and logout functionality. - Updated homepage to show personalized greetings for logged-in users and a login prompt for guests. - Added logout confirmation page with a countdown redirect to the homepage. - Documented the implementation details and future enhancements for OAuth2 flows in CLAUDE.md and other relevant documentation. - Added test credentials and guidelines for automated testing in the new TESTING.md file.
This commit is contained in:
302
tests/README.md
Normal file
302
tests/README.md
Normal file
@@ -0,0 +1,302 @@
|
||||
# Testing Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes how to run tests for my.experimenta.science and how to use test credentials.
|
||||
|
||||
---
|
||||
|
||||
## Test Credentials
|
||||
|
||||
Test credentials are stored in environment variables to keep them out of the codebase while making them accessible for automated tests.
|
||||
|
||||
**📖 For complete credentials and testing guide, see: [`docs/TESTING.md`](../docs/TESTING.md)**
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Copy .env.example to .env:**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. **Set test credentials in .env:**
|
||||
|
||||
See [`docs/TESTING.md`](../docs/TESTING.md) for current test credentials.
|
||||
|
||||
```env
|
||||
TEST_USER_EMAIL=<see-docs-TESTING-md>
|
||||
TEST_USER_PASSWORD=<see-docs-TESTING-md>
|
||||
```
|
||||
|
||||
3. **Verify configuration:**
|
||||
```bash
|
||||
# Start dev server
|
||||
pnpm dev
|
||||
|
||||
# In another terminal, test the credentials endpoint
|
||||
curl http://localhost:3000/api/test/credentials
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```json
|
||||
{
|
||||
"email": "bm@noxware.de",
|
||||
"password": "%654321qQ!"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Test Credentials in Tests
|
||||
|
||||
### Method 1: Via API Endpoint (Recommended for E2E tests)
|
||||
|
||||
```typescript
|
||||
// tests/e2e/example.spec.ts
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test('login flow', async ({ page }) => {
|
||||
// Fetch test credentials from API
|
||||
const response = await fetch('http://localhost:3000/api/test/credentials')
|
||||
|
||||
if (!response.ok) {
|
||||
test.skip() // Skip if credentials not configured
|
||||
return
|
||||
}
|
||||
|
||||
const { email, password } = await response.json()
|
||||
|
||||
// Use credentials in test
|
||||
await page.goto('/auth')
|
||||
await page.fill('input[type="email"]', email)
|
||||
await page.fill('input[type="password"]', password)
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
// Assert login success
|
||||
await expect(page).toHaveURL('/')
|
||||
})
|
||||
```
|
||||
|
||||
### Method 2: Via Server Utility (For server-side tests)
|
||||
|
||||
```typescript
|
||||
// tests/unit/auth.test.ts
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getTestCredentials } from '~/server/utils/test-helpers'
|
||||
|
||||
describe('Authentication', () => {
|
||||
it('should login with test credentials', async () => {
|
||||
const { email, password } = getTestCredentials()
|
||||
|
||||
// Use credentials in test
|
||||
const response = await $fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
body: { email, password },
|
||||
})
|
||||
|
||||
expect(response.success).toBe(true)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Method 3: Via Environment Variables (Direct access)
|
||||
|
||||
```typescript
|
||||
// For tests that need direct access
|
||||
const email = process.env.TEST_USER_EMAIL
|
||||
const password = process.env.TEST_USER_PASSWORD
|
||||
|
||||
if (!email || !password) {
|
||||
console.warn('Test credentials not configured, skipping test')
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
### ⚠️ Important Security Considerations
|
||||
|
||||
1. **Development Only:**
|
||||
- Test credentials should ONLY be used in development/staging
|
||||
- The `/api/test/credentials` endpoint returns 404 in production
|
||||
- Never commit real credentials to Git
|
||||
|
||||
2. **Environment Variables:**
|
||||
- `.env` is in `.gitignore` and never committed
|
||||
- `.env.example` contains example/documentation only (not real passwords)
|
||||
- Each developer should create their own `.env` file locally
|
||||
|
||||
3. **Production Safety:**
|
||||
- The test credentials endpoint is automatically disabled in production
|
||||
- Server utility warns if used in production environment
|
||||
- Test files are excluded from production builds
|
||||
|
||||
4. **Credential Rotation:**
|
||||
- Test credentials can be changed at any time in `.env`
|
||||
- All tests will automatically use the updated credentials
|
||||
- No code changes required when credentials change
|
||||
|
||||
---
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Unit Tests (Vitest)
|
||||
|
||||
```bash
|
||||
# Run all unit tests
|
||||
pnpm test
|
||||
|
||||
# Run with coverage
|
||||
pnpm test:coverage
|
||||
|
||||
# Watch mode
|
||||
pnpm test:watch
|
||||
```
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
|
||||
```bash
|
||||
# Run all E2E tests
|
||||
pnpm test:e2e
|
||||
|
||||
# Run specific test file
|
||||
pnpm test:e2e tests/e2e/auth-login.example.spec.ts
|
||||
|
||||
# Run in UI mode (visual debugger)
|
||||
pnpm test:e2e --ui
|
||||
|
||||
# Run in headed mode (see browser)
|
||||
pnpm test:e2e --headed
|
||||
```
|
||||
|
||||
### Before Running Tests
|
||||
|
||||
1. ✅ Ensure `.env` is configured with test credentials
|
||||
2. ✅ Start the dev server: `pnpm dev`
|
||||
3. ✅ Start database: `docker-compose up -d` (if using Docker)
|
||||
4. ✅ Run migrations: `pnpm db:migrate`
|
||||
|
||||
---
|
||||
|
||||
## Test Structure
|
||||
|
||||
```
|
||||
tests/
|
||||
├── README.md # This file
|
||||
├── e2e/ # End-to-end tests (Playwright)
|
||||
│ └── auth-login.example.spec.ts # Example E2E test with credentials
|
||||
└── unit/ # Unit tests (Vitest)
|
||||
└── (to be added)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example Tests
|
||||
|
||||
See `tests/e2e/auth-login.example.spec.ts` for a complete example of using test credentials in E2E tests.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Test credentials not configured" Error
|
||||
|
||||
**Cause:** `TEST_USER_EMAIL` or `TEST_USER_PASSWORD` not set in `.env`
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# 1. Check if .env exists
|
||||
ls -la .env
|
||||
|
||||
# 2. If not, copy from example
|
||||
cp .env.example .env
|
||||
|
||||
# 3. Edit .env and set credentials
|
||||
# TEST_USER_EMAIL=bm@noxware.de
|
||||
# TEST_USER_PASSWORD=%654321qQ!
|
||||
|
||||
# 4. Restart dev server
|
||||
```
|
||||
|
||||
### API Endpoint Returns 404
|
||||
|
||||
**Cause:** Server is running in production mode
|
||||
|
||||
**Solution:** Test credentials endpoint is only available in development. Set:
|
||||
```bash
|
||||
NODE_ENV=development
|
||||
```
|
||||
|
||||
### Tests Fail with "Invalid Credentials"
|
||||
|
||||
**Possible causes:**
|
||||
1. Wrong credentials in `.env`
|
||||
2. Test user doesn't exist in Cidaas
|
||||
3. Cidaas staging environment is down
|
||||
|
||||
**Solution:**
|
||||
- Verify credentials work manually by logging in at `/auth`
|
||||
- Check Cidaas staging status
|
||||
- Contact team lead for valid test account
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check if credentials are configured:**
|
||||
```typescript
|
||||
if (!response.ok) {
|
||||
test.skip() // Don't fail, just skip
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
2. **Use the API endpoint for E2E tests:**
|
||||
- Cleaner separation of concerns
|
||||
- Easier to mock/stub in CI/CD
|
||||
- Works across different test frameworks
|
||||
|
||||
3. **Clean up test data:**
|
||||
```typescript
|
||||
test.afterEach(async () => {
|
||||
// Logout after each test
|
||||
await $fetch('/api/auth/logout', { method: 'POST' })
|
||||
})
|
||||
```
|
||||
|
||||
4. **Isolate tests:**
|
||||
- Each test should be independent
|
||||
- Don't rely on state from previous tests
|
||||
- Use `test.beforeEach` to reset state
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
For GitLab CI/CD, add test credentials as environment variables:
|
||||
|
||||
```yaml
|
||||
# .gitlab-ci.yml
|
||||
test:
|
||||
stage: test
|
||||
variables:
|
||||
TEST_USER_EMAIL: $CI_TEST_USER_EMAIL # Set in GitLab CI/CD settings
|
||||
TEST_USER_PASSWORD: $CI_TEST_USER_PASSWORD
|
||||
script:
|
||||
- pnpm install
|
||||
- pnpm test
|
||||
- pnpm test:e2e
|
||||
```
|
||||
|
||||
Set `CI_TEST_USER_EMAIL` and `CI_TEST_USER_PASSWORD` as protected variables in:
|
||||
**GitLab → Settings → CI/CD → Variables**
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
- Check `CLAUDE.md` for authentication patterns
|
||||
- Check `docs/CIDAAS_INTEGRATION.md` for Cidaas setup
|
||||
- Ask in team chat or create an issue
|
||||
81
tests/e2e/auth-login.example.spec.ts
Normal file
81
tests/e2e/auth-login.example.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* E2E Test: Authentication - Login Flow
|
||||
*
|
||||
* This is an EXAMPLE test showing how to use test credentials.
|
||||
* To run this test:
|
||||
*
|
||||
* 1. Copy .env.example to .env
|
||||
* 2. Set TEST_USER_EMAIL and TEST_USER_PASSWORD
|
||||
* 3. Run: pnpm test:e2e
|
||||
*
|
||||
* @example
|
||||
* ```bash
|
||||
* # .env
|
||||
* TEST_USER_EMAIL=bm@noxware.de
|
||||
* TEST_USER_PASSWORD=%654321qQ!
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Authentication - Login Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to auth page before each test
|
||||
await page.goto('/auth')
|
||||
})
|
||||
|
||||
test('should login with test credentials from environment', async ({ page }) => {
|
||||
// Fetch test credentials from API endpoint
|
||||
const response = await fetch('http://localhost:3000/api/test/credentials')
|
||||
|
||||
// Skip test if credentials not configured
|
||||
if (!response.ok) {
|
||||
test.skip()
|
||||
return
|
||||
}
|
||||
|
||||
const { email, password } = await response.json()
|
||||
|
||||
// Fill in login form
|
||||
await page.fill('input[type="email"]', email)
|
||||
await page.fill('input[type="password"]', password)
|
||||
|
||||
// Submit form
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
// Wait for navigation to complete
|
||||
await page.waitForURL('/')
|
||||
|
||||
// Verify we're on homepage
|
||||
expect(page.url()).toBe('http://localhost:3000/')
|
||||
|
||||
// Optional: Verify user is logged in (check for user menu, etc.)
|
||||
// await expect(page.locator('[data-testid="user-menu"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show error with invalid credentials', async ({ page }) => {
|
||||
// Fill in login form with invalid credentials
|
||||
await page.fill('input[type="email"]', 'invalid@example.com')
|
||||
await page.fill('input[type="password"]', 'wrongpassword')
|
||||
|
||||
// Submit form
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
// Verify error message is shown
|
||||
await expect(page.locator('[role="alert"]')).toBeVisible()
|
||||
await expect(page.locator('[role="alert"]')).toContainText('Invalid credentials')
|
||||
})
|
||||
|
||||
test('should validate email format', async ({ page }) => {
|
||||
// Fill in invalid email
|
||||
await page.fill('input[type="email"]', 'not-an-email')
|
||||
await page.fill('input[type="password"]', 'somepassword')
|
||||
|
||||
// Submit form
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
// Verify validation error
|
||||
// Note: Exact selector depends on your validation error display
|
||||
await expect(page.locator('text=Invalid email')).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user