diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e10446..6d36126 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,6 @@ git remote add upstream https://github.com/original-owner/meds.git bun install cp .env.example .env # Edit .env with your development values -docker compose -f docker/docker-compose.yaml up -d ``` #### 3. Create Feature Branch @@ -313,22 +312,13 @@ bun test --coverage # Run integration tests bun run test:integration -# Run E2E tests with Playwright -bun run test:e2e - -# Run E2E tests in UI mode -bun run test:e2e:ui - -# Debug E2E tests -bun run test:e2e:debug - -# Run all tests (unit + integration + e2e) +# Run all tests (unit + integration) bun run test:all ``` ### E2E Testing -E2E tests use Playwright and are located in `tests/e2e/`. When adding new features: +Integration tests are located in `tests/integration/`. When adding new features: ```typescript // Use custom fixtures for authenticated testing @@ -442,7 +432,6 @@ We follow [Semantic Versioning](https://semver.org/): ### Required Tools - **Node.js 18+** or **Bun 1.0+** -- **Docker and Docker Compose** - **Git** - **Code Editor** (VS Code recommended) @@ -473,10 +462,6 @@ bun test # Run tests bun test:coverage # Run tests with coverage bun test:watch # Run tests in watch mode -# Docker -docker compose -f docker/docker-compose.yaml up -d # Start services -docker compose -f docker/docker-compose.yaml logs # View logs -docker compose -f docker/docker-compose.yaml down # Stop services ``` ## ๐Ÿ†˜ Getting Help diff --git a/README.md b/README.md index 5aae600..bb27e58 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ make dev ### **Available Commands** -```bash +````bash # Development bun run dev # Start development server bun run build # Build for production @@ -135,10 +135,10 @@ bun run lint:fix # Fix linting issues bun run type-check # TypeScript type checking # Testing +```bash bun run test # Run unit tests bun run test:watch # Run tests in watch mode -bun run test:e2e # Run end-to-end tests -``` +```` ### **Makefile Commands** @@ -256,7 +256,7 @@ bun run type-check # TypeScript validation - **Unit Tests**: Jest-based tests for services and utilities - **Component Tests**: React component testing - **Integration Tests**: Service integration validation -- **E2E Tests**: Playwright-based full user journey testing +- **Integration Tests**: Service validation and API testing ### **Running Tests** @@ -267,11 +267,8 @@ bun run test # Run with coverage bun run test:coverage -# Run E2E tests -bun run test:e2e - -# Run E2E tests in UI mode -bun run test:e2e:ui +# Run integration tests +bun run test:services ``` ## ๐Ÿ“ Project Structure diff --git a/bun.lock b/bun.lock index 19ca979..95daede 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,6 @@ "@babel/core": "^7.28.4", "@babel/preset-env": "^7.28.3", "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.55.0", "@secretlint/node": "^11.2.3", "@secretlint/secretlint-rule-preset-recommend": "^11.2.3", "@testing-library/jest-dom": "^6.8.0", @@ -432,8 +431,6 @@ "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], - "@playwright/test": ["@playwright/test@1.55.0", "", { "dependencies": { "playwright": "1.55.0" }, "bin": { "playwright": "cli.js" } }, "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.0", "", { "os": "android", "cpu": "arm" }, "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.0", "", { "os": "android", "cpu": "arm64" }, "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw=="], @@ -1570,10 +1567,6 @@ "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], - "playwright": ["playwright@1.55.0", "", { "dependencies": { "playwright-core": "1.55.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA=="], - - "playwright-core": ["playwright-core@1.55.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg=="], - "plugin-error": ["plugin-error@1.0.1", "", { "dependencies": { "ansi-colors": "^1.0.1", "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" } }, "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA=="], "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], @@ -2134,8 +2127,6 @@ "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], - "raw-body/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], "rc-config-loader/js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], diff --git a/docs/architecture/PROJECT_STRUCTURE.md b/docs/architecture/PROJECT_STRUCTURE.md index 33c654c..d938930 100644 --- a/docs/architecture/PROJECT_STRUCTURE.md +++ b/docs/architecture/PROJECT_STRUCTURE.md @@ -110,10 +110,6 @@ rxminder/ โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ”ง admin-login-debug.js # Admin debugging โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ”ง auth-db-debug.js # Auth debugging โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ”ง debug-email-validation.js # Email debugging -โ”‚ โ””โ”€โ”€ ๐Ÿ“ e2e/ # End-to-end tests with Playwright -โ”‚ โ”œโ”€โ”€ ๐Ÿ“ README.md # E2E testing documentation -โ”‚ โ”œโ”€โ”€ ๐Ÿงช fixtures.ts # Custom test fixtures -โ”‚ โ”œโ”€โ”€ ๐Ÿงช helpers.ts # Test utilities and data โ”‚ โ”œโ”€โ”€ ๐Ÿงช auth.spec.ts # Authentication flow tests โ”‚ โ”œโ”€โ”€ ๐Ÿงช medication.spec.ts # Medication management tests โ”‚ โ”œโ”€โ”€ ๐Ÿงช admin.spec.ts # Admin interface tests @@ -149,7 +145,6 @@ rxminder/ โ”œโ”€โ”€ ๐ŸŽจ .prettierrc # Code formatting rules โ”œโ”€โ”€ โšก eslint.config.cjs # Linting configuration โ”œโ”€โ”€ ๐Ÿงช jest.config.json # Test configuration - โ”œโ”€โ”€ ๐ŸŽญ playwright.config.ts # E2E test configuration โ”œโ”€โ”€ ๐Ÿ“ .markdownlint.json # Markdown linting โ”œโ”€โ”€ ๐Ÿ”’ .secretlintrc.json # Secret detection โ”œโ”€โ”€ โœ๏ธ .editorconfig # Editor consistency @@ -224,8 +219,7 @@ Comprehensive testing at multiple levels: ``` tests/ โ”œโ”€โ”€ integration/ # Service integration tests -โ”œโ”€โ”€ manual/ # Debug scripts and manual testing -โ””โ”€โ”€ e2e/ # Full application testing +โ””โ”€โ”€ manual/ # Debug scripts and manual testing # Unit tests alongside code services/auth/__tests__/ # Co-located with services diff --git a/docs/implementation/IMPLEMENTATION_SUMMARY.md b/docs/implementation/IMPLEMENTATION_SUMMARY.md index c28c590..24e22f0 100644 --- a/docs/implementation/IMPLEMENTATION_SUMMARY.md +++ b/docs/implementation/IMPLEMENTATION_SUMMARY.md @@ -112,7 +112,7 @@ bun run dev - Unit tests for services and utilities - Component tests for React components - Integration tests for API endpoints -- End-to-end tests with Playwright +- Integration tests for service validation ### Test Environment diff --git a/package.json b/package.json index 3475b30..beb0189 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@babel/core": "^7.28.4", "@babel/preset-env": "^7.28.3", "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.55.0", "@secretlint/node": "^11.2.3", "@secretlint/secretlint-rule-preset-recommend": "^11.2.3", "@testing-library/jest-dom": "^6.8.0", diff --git a/playwright.config.ts b/playwright.config.ts deleted file mode 100644 index d78b62c..0000000 --- a/playwright.config.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { defineConfig, devices } from '@playwright/test'; - -/** - * @see https://playwright.dev/docs/test-configuration - */ -export default defineConfig({ - testDir: './tests/e2e', - /* 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:8080', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - - /* Take screenshot on failure */ - screenshot: 'only-on-failure', - - /* Record video on failure */ - video: 'retain-on-failure', - }, - - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - - { - name: 'firefox', - use: { ...devices['Desktop 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: 'docker compose -f docker/docker-compose.yaml up -d', - url: 'http://localhost:8080', - reuseExistingServer: !process.env.CI, - timeout: 120 * 1000, // 2 minutes - }, -}); diff --git a/tests/e2e/README.md b/tests/e2e/README.md deleted file mode 100644 index 2a63f29..0000000 --- a/tests/e2e/README.md +++ /dev/null @@ -1,319 +0,0 @@ -# ๐ŸŽญ End-to-End Testing with Playwright - -## Overview - -This directory contains comprehensive end-to-end tests for the Medication Reminder App using Playwright. These tests simulate real user interactions across different browsers and devices. - -## Test Structure - -``` -tests/e2e/ -โ”œโ”€โ”€ README.md # This documentation -โ”œโ”€โ”€ fixtures.ts # Custom test fixtures and utilities -โ”œโ”€โ”€ helpers.ts # Test helper functions and data -โ”œโ”€โ”€ auth.spec.ts # Authentication flow tests -โ”œโ”€โ”€ medication.spec.ts # Medication management tests -โ”œโ”€โ”€ admin.spec.ts # Admin interface tests -โ”œโ”€โ”€ ui-navigation.spec.ts # UI and navigation tests -โ””โ”€โ”€ reminders.spec.ts # Reminder system tests -``` - -## Test Categories - -### ๐Ÿ” **Authentication Tests** (`auth.spec.ts`) - -- User registration and login -- Admin authentication -- OAuth button visibility -- Invalid credential handling -- Session management - -### ๐Ÿ’Š **Medication Management** (`medication.spec.ts`) - -- Adding new medications -- Editing existing medications -- Deleting medications -- Marking doses as taken -- Viewing medication history - -### ๐Ÿ‘‘ **Admin Interface** (`admin.spec.ts`) - -- User management operations -- Password changes -- User status toggles -- Admin permissions - -### ๐ŸŽจ **UI & Navigation** (`ui-navigation.spec.ts`) - -- Theme switching -- Modal interactions -- Responsive design -- Search functionality -- Account management - -### โฐ **Reminder System** (`reminders.spec.ts`) - -- Custom reminder creation -- Reminder editing and deletion -- Scheduled dose display -- Missed dose handling - -## Setup and Installation - -### 1. Install Playwright - -```bash -# Install Playwright and browsers -bun add -D @playwright/test -bunx playwright install -``` - -### 2. Update Package.json - -```json -{ - "scripts": { - "test:e2e": "playwright test", - "test:e2e:ui": "playwright test --ui", - "test:e2e:debug": "playwright test --debug", - "test:e2e:report": "playwright show-report" - } -} -``` - -## Running Tests - -### Basic Test Execution - -```bash -# Run all E2E tests -bun run test:e2e - -# Run tests in UI mode (interactive) -bun run test:e2e:ui - -# Run specific test file -bunx playwright test auth.spec.ts - -# Run tests in debug mode -bun run test:e2e:debug -``` - -### Browser-Specific Testing - -```bash -# Run on specific browser -bunx playwright test --project=chromium -bunx playwright test --project=firefox -bunx playwright test --project=webkit - -# Run on mobile browsers -bunx playwright test --project="Mobile Chrome" -bunx playwright test --project="Mobile Safari" -``` - -### Test Reporting - -```bash -# Generate and view HTML report -bun run test:e2e:report - -# Run with specific reporter -bunx playwright test --reporter=line -bunx playwright test --reporter=json -``` - -## Test Configuration - -The tests are configured via `playwright.config.ts`: - -- **Base URL**: `http://localhost:8080` -- **Auto-start**: Docker Compose before tests -- **Browsers**: Chrome, Firefox, Safari, Mobile Chrome, Mobile Safari -- **Retries**: 2 on CI, 0 locally -- **Screenshots**: On failure -- **Videos**: On failure -- **Traces**: On retry - -## Test Data and Fixtures - -### Custom Fixtures (`fixtures.ts`) - -- `adminPage`: Auto-login as admin user -- `userPage`: Auto-login as regular user - -### Helper Functions (`helpers.ts`) - -- `MedicationHelpers`: Medication CRUD operations -- `AuthHelpers`: Authentication actions -- `ModalHelpers`: Modal interactions -- `WaitHelpers`: Wait utilities -- `TestData`: Pre-defined test data - -### Example Usage - -```typescript -import { test } from './fixtures'; -import { MedicationHelpers, TestData } from './helpers'; - -test('should add medication', async ({ adminPage }) => { - const medicationHelper = new MedicationHelpers(adminPage); - const testMed = TestData.medications[0]; - - await medicationHelper.addMedication(testMed.name, testMed.dosage, testMed.frequency); -}); -``` - -## Best Practices - -### โœ… Test Organization - -- Group related tests in describe blocks -- Use descriptive test names -- Keep tests independent and isolated - -### โœ… Selectors - -- Use data-testid attributes for reliable targeting -- Prefer semantic selectors (role, text content) -- Avoid CSS selectors that may change - -### โœ… Waiting Strategies - -- Use `waitForSelector()` for dynamic content -- Leverage auto-waiting for most actions -- Add explicit waits for complex interactions - -### โœ… Test Data - -- Use helper functions for common operations -- Keep test data in centralized location -- Clean up test data after tests - -## Debugging Tests - -### Local Debugging - -```bash -# Debug specific test -bunx playwright test auth.spec.ts --debug - -# Run with headed browser -bunx playwright test --headed - -# Slow down execution -bunx playwright test --slow-mo=1000 -``` - -### CI/CD Integration - -```bash -# Run in CI mode -CI=true bunx playwright test - -# Generate artifacts for CI -bunx playwright test --reporter=github -``` - -## Adding New Tests - -### 1. Create Test File - -```typescript -import { test, expect } from './fixtures'; - -test.describe('New Feature', () => { - test('should do something', async ({ adminPage }) => { - // Test implementation - }); -}); -``` - -### 2. Add Helper Functions - -```typescript -// In helpers.ts -export class NewFeatureHelpers { - constructor(private page: any) {} - - async performAction() { - // Helper implementation - } -} -``` - -### 3. Update Documentation - -- Add test description to this README -- Update test count in project documentation - -## Troubleshooting - -### Common Issues - -**Tests timeout:** - -- Increase timeout in config -- Add explicit waits -- Check application startup time - -**Flaky tests:** - -- Add proper wait conditions -- Use retry logic -- Check for race conditions - -**Browser compatibility:** - -- Test across all configured browsers -- Check for browser-specific issues -- Use cross-browser compatible selectors - -### Debug Commands - -```bash -# Show browser developer tools -bunx playwright test --debug - -# Record test execution -bunx playwright codegen localhost:8080 - -# Trace viewer -bunx playwright show-trace trace.zip -``` - -## Continuous Integration - -Example GitHub Actions workflow: - -```yaml -name: E2E Tests -on: [push, pull_request] -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - run: bun install - - run: bunx playwright install --with-deps - - run: bun run test:e2e - - uses: actions/upload-artifact@v3 - if: always() - with: - name: playwright-report - path: playwright-report/ -``` - -## Coverage and Metrics - -The E2E tests provide coverage for: - -- โœ… User authentication flows -- โœ… Core medication management -- โœ… Admin functionality -- โœ… UI interactions and navigation -- โœ… Responsive design -- โœ… Cross-browser compatibility - -For optimal coverage, run tests regularly and add new tests for new features. diff --git a/tests/e2e/admin.spec.ts b/tests/e2e/admin.spec.ts deleted file mode 100644 index 51542a6..0000000 --- a/tests/e2e/admin.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test.describe('Admin Interface', () => { - test.beforeEach(async ({ page }) => { - // Login as admin - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - - // Wait for main app and open admin interface - await expect(page.locator('h1')).toContainText('Medication Reminder'); - await page.click('button:has-text("Admin")'); - }); - - test('should display admin interface', async ({ page }) => { - await expect(page.locator('text=Admin Interface')).toBeVisible(); - await expect(page.locator('text=User Management')).toBeVisible(); - }); - - test('should show list of users', async ({ page }) => { - // Should show admin user at minimum - await expect(page.locator('text=admin@localhost')).toBeVisible(); - await expect(page.locator('text=Admin')).toBeVisible(); // Role - }); - - test('should allow changing user password', async ({ page }) => { - // Click on a user's change password button - await page.click('[data-testid="change-password"]'); - - // Fill new password - await page.fill('input[type="password"]', 'NewPassword123!'); - - // Submit password change - await page.click('button:has-text("Change Password")'); - - // Should show success message - await expect(page.locator('text=Password changed')).toBeVisible(); - }); - - test('should allow suspending/activating users', async ({ page }) => { - // Look for user status controls - const statusButton = page - .locator('[data-testid="toggle-user-status"]') - .first(); - await expect(statusButton).toBeVisible(); - }); - - test('should refresh user list', async ({ page }) => { - await page.click('button:has-text("Refresh")'); - - // Should still show users after refresh - await expect(page.locator('text=admin@localhost')).toBeVisible(); - }); - - test('should close admin interface', async ({ page }) => { - await page.click('button[aria-label="Close"]'); - - // Should return to main app - await expect(page.locator('text=Admin Interface')).not.toBeVisible(); - await expect(page.locator('h1')).toContainText('Medication Reminder'); - }); -}); diff --git a/tests/e2e/auth.spec.ts b/tests/e2e/auth.spec.ts deleted file mode 100644 index 05a9383..0000000 --- a/tests/e2e/auth.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test.describe('Authentication Flow', () => { - test.beforeEach(async ({ page }) => { - await page.goto('/'); - }); - - test('should display login page for unauthenticated users', async ({ - page, - }) => { - await expect(page.locator('h2')).toContainText(['Sign In', 'Login']); - await expect(page.locator('input[type="email"]')).toBeVisible(); - await expect(page.locator('input[type="password"]')).toBeVisible(); - }); - - test('should allow user registration', async ({ page }) => { - // Click register tab/link - await page.click('text=Register'); - - // Fill registration form - await page.fill('input[type="email"]', 'test@example.com'); - await page.fill('input[name="username"]', 'testuser'); - await page.fill('input[type="password"]', 'TestPassword123!'); - - // Submit registration - await page.click('button[type="submit"]'); - - // Should show verification message or redirect - await expect(page.locator('text=verification')).toBeVisible(); - }); - - test('should login with admin credentials', async ({ page }) => { - // Fill login form with admin credentials - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - - // Submit login - await page.click('button[type="submit"]'); - - // Should redirect to main app - await expect(page.locator('h1')).toContainText('Medication Reminder'); - await expect(page.locator('text=Admin')).toBeVisible(); - }); - - test('should show error for invalid credentials', async ({ page }) => { - await page.fill('input[type="email"]', 'invalid@example.com'); - await page.fill('input[type="password"]', 'wrongpassword'); - - await page.click('button[type="submit"]'); - - await expect(page.locator('text=Invalid')).toBeVisible(); - }); - - test('should handle OAuth login buttons', async ({ page }) => { - await expect(page.locator('button:has-text("Google")')).toBeVisible(); - await expect(page.locator('button:has-text("GitHub")')).toBeVisible(); - }); -}); diff --git a/tests/e2e/fixtures.ts b/tests/e2e/fixtures.ts deleted file mode 100644 index 78925ee..0000000 --- a/tests/e2e/fixtures.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { test as base, Page } from '@playwright/test'; - -// Define fixture types -type TestFixtures = { - adminPage: Page; - userPage: Page; -}; - -// Extend basic test with custom fixtures -export const test = base.extend({ - // Auto-login fixture for admin user - adminPage: async ({ page }, use) => { - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - - // Wait for app to load - await page.waitForSelector('h1:has-text("Medication Reminder")'); - - await use(page); - }, - - // Regular user login fixture - userPage: async ({ page }, use) => { - await page.goto('/'); - - // Register a test user first if needed - await page.click('text=Register'); - await page.fill('input[type="email"]', 'testuser@example.com'); - await page.fill('input[name="username"]', 'testuser'); - await page.fill('input[type="password"]', 'TestPassword123!'); - await page.click('button[type="submit"]'); - - // For mock database, user might be auto-verified - // Wait for either verification message or app load - try { - await page.waitForSelector('h1:has-text("Medication Reminder")', { - timeout: 5000, - }); - } catch { - // If not auto-logged in, login manually - await page.goto('/'); - await page.fill('input[type="email"]', 'testuser@example.com'); - await page.fill('input[type="password"]', 'TestPassword123!'); - await page.click('button[type="submit"]'); - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - await use(page); - }, -}); - -export { expect } from '@playwright/test'; diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts deleted file mode 100644 index a42b514..0000000 --- a/tests/e2e/helpers.ts +++ /dev/null @@ -1,158 +0,0 @@ -// E2E Test Utilities and Helpers -import { Page } from '@playwright/test'; - -// Type definitions for better type safety -interface MedicationData { - name: string; - dosage: string; - frequency: string; - times: string; -} - -interface UserData { - email: string; - username: string; - password: string; -} - -interface ReminderData { - title: string; - icon: string; - frequency: number; -} - -export class MedicationHelpers { - constructor(private page: Page) {} - - async addMedication( - name: string, - dosage: string, - frequency: string = 'daily', - times: string = '1' - ): Promise { - await this.page.click('button:has-text("Add Medication")'); - await this.page.fill('input[name="name"]', name); - await this.page.fill('input[name="dosage"]', dosage); - await this.page.selectOption('select[name="frequency"]', frequency); - if (times !== '1') { - await this.page.fill('input[name="times"]', times); - } - await this.page.click('button[type="submit"]'); - - // Wait for medication to appear - await this.page.waitForSelector(`text=${name}`); - } - - async deleteMedication(name: string): Promise { - await this.page.click('button:has-text("Manage")'); - - // Find the medication row and click delete - await this.page - .locator(`tr:has-text("${name}") [data-testid="delete-medication"]`) - .click(); - await this.page.click('button:has-text("Delete")'); - - // Close manage modal - await this.page.click('button:has-text("Close")'); - } - - async takeDose(medicationName: string): Promise { - await this.page - .locator( - `.dose-card:has-text("${medicationName}") button:has-text("Take")` - ) - .click(); - } -} - -export class AuthHelpers { - constructor(private page: Page) {} - - async loginAsAdmin(): Promise { - await this.page.goto('/'); - await this.page.fill('input[type="email"]', 'admin@localhost'); - await this.page.fill('input[type="password"]', 'admin123!'); - await this.page.click('button[type="submit"]'); - await this.page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - async registerUser( - email: string, - username: string, - password: string - ): Promise { - await this.page.goto('/'); - await this.page.click('text=Register'); - await this.page.fill('input[type="email"]', email); - await this.page.fill('input[name="username"]', username); - await this.page.fill('input[type="password"]', password); - await this.page.click('button[type="submit"]'); - } - - async logout(): Promise { - await this.page.click('[data-testid="avatar-dropdown"]'); - await this.page.click('button:has-text("Logout")'); - await this.page.waitForSelector('h2:has-text("Sign In")'); - } -} - -export class ModalHelpers { - constructor(private page: Page) {} - - async openModal(buttonText: string): Promise { - await this.page.click(`button:has-text("${buttonText}")`); - } - - async closeModal(): Promise { - await this.page.click('button:has-text("Close")'); - } - - async confirmAction(): Promise { - await this.page.click('button:has-text("Confirm")'); - } -} - -export class WaitHelpers { - constructor(private page: Page) {} - - async waitForAppLoad(): Promise { - await this.page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - async waitForModal(title: string): Promise { - await this.page.waitForSelector(`text=${title}`); - } - - async waitForNotification(message: string): Promise { - await this.page.waitForSelector(`text=${message}`); - } -} - -// Data generators for testing -export const TestData = { - medications: [ - { name: 'Aspirin', dosage: '100mg', frequency: 'daily', times: '1' }, - { name: 'Vitamin D', dosage: '1000 IU', frequency: 'daily', times: '1' }, - { name: 'Omega-3', dosage: '500mg', frequency: 'daily', times: '2' }, - { name: 'Calcium', dosage: '600mg', frequency: 'twice_daily', times: '1' }, - ] as const satisfies readonly MedicationData[], - - users: [ - { - email: 'test1@example.com', - username: 'testuser1', - password: 'TestPass123!', - }, - { - email: 'test2@example.com', - username: 'testuser2', - password: 'TestPass456!', - }, - ] as const satisfies readonly UserData[], - - reminders: [ - { title: 'Drink Water', icon: 'bell', frequency: 60 }, - { title: 'Exercise', icon: 'heart', frequency: 1440 }, // Daily - { title: 'Check Blood Pressure', icon: 'chart', frequency: 10080 }, // Weekly - ] as const satisfies readonly ReminderData[], -} as const; diff --git a/tests/e2e/medication.spec.ts b/tests/e2e/medication.spec.ts deleted file mode 100644 index 33f8d2d..0000000 --- a/tests/e2e/medication.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test.describe('Medication Management', () => { - test.beforeEach(async ({ page }) => { - // Login as admin first - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - - // Wait for main app to load - await expect(page.locator('h1')).toContainText('Medication Reminder'); - }); - - test('should add a new medication', async ({ page }) => { - // Click add medication button - await page.click('button:has-text("Add Medication")'); - - // Fill medication form - await page.fill('input[name="name"]', 'Aspirin'); - await page.fill('input[name="dosage"]', '100mg'); - await page.selectOption('select[name="frequency"]', 'daily'); - await page.fill('input[name="times"]', '2'); - - // Submit form - await page.click('button[type="submit"]'); - - // Should see medication in list - await expect(page.locator('text=Aspirin')).toBeVisible(); - await expect(page.locator('text=100mg')).toBeVisible(); - }); - - test('should edit existing medication', async ({ page }) => { - // First add a medication - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Vitamin D'); - await page.fill('input[name="dosage"]', '1000 IU'); - await page.click('button[type="submit"]'); - - // Click edit button for the medication - await page.click('[data-testid="edit-medication"]'); - - // Update dosage - await page.fill('input[name="dosage"]', '2000 IU'); - await page.click('button[type="submit"]'); - - // Should see updated dosage - await expect(page.locator('text=2000 IU')).toBeVisible(); - }); - - test('should delete medication', async ({ page }) => { - // Add a medication first - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Test Medicine'); - await page.fill('input[name="dosage"]', '50mg'); - await page.click('button[type="submit"]'); - - // Open manage medications modal - await page.click('button:has-text("Manage")'); - - // Delete the medication - await page.click('[data-testid="delete-medication"]'); - await page.click('button:has-text("Delete")'); // Confirm deletion - - // Should not see medication anymore - await expect(page.locator('text=Test Medicine')).not.toBeVisible(); - }); - - test('should mark dose as taken', async ({ page }) => { - // Add a medication first - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Daily Vitamin'); - await page.fill('input[name="dosage"]', '1 tablet'); - await page.selectOption('select[name="frequency"]', 'daily'); - await page.click('button[type="submit"]'); - - // Find and click the "Take" button for upcoming dose - await page.click('button:has-text("Take")'); - - // Should show as taken - await expect(page.locator('text=Taken')).toBeVisible(); - await expect(page.locator('.bg-green-50')).toBeVisible(); - }); - - test('should show medication history', async ({ page }) => { - // Open history modal - await page.click('button:has-text("History")'); - - // Should show history modal - await expect(page.locator('text=Medication History')).toBeVisible(); - - // Close modal - await page.click('button:has-text("Close")'); - }); -}); diff --git a/tests/e2e/reminders.spec.ts b/tests/e2e/reminders.spec.ts deleted file mode 100644 index 50973b1..0000000 --- a/tests/e2e/reminders.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test.describe('Reminder System', () => { - test.beforeEach(async ({ page }) => { - // Login as admin - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - await expect(page.locator('h1')).toContainText('Medication Reminder'); - }); - - test('should create custom reminder', async ({ page }) => { - // Click manage reminders - await page.click('button:has-text("Reminders")'); - - // Add new reminder - await page.click('button:has-text("Add Reminder")'); - - // Fill reminder form - await page.fill('input[name="title"]', 'Drink Water'); - await page.selectOption('select[name="icon"]', 'bell'); - await page.fill('input[name="frequency"]', '60'); // Every hour - - await page.click('button[type="submit"]'); - - // Should see reminder in list - await expect(page.locator('text=Drink Water')).toBeVisible(); - }); - - test('should edit custom reminder', async ({ page }) => { - // First create a reminder - await page.click('button:has-text("Reminders")'); - await page.click('button:has-text("Add Reminder")'); - await page.fill('input[name="title"]', 'Exercise'); - await page.click('button[type="submit"]'); - - // Edit the reminder - await page.click('[data-testid="edit-reminder"]'); - await page.fill('input[name="title"]', 'Morning Exercise'); - await page.click('button[type="submit"]'); - - await expect(page.locator('text=Morning Exercise')).toBeVisible(); - }); - - test('should delete custom reminder', async ({ page }) => { - // Create and then delete reminder - await page.click('button:has-text("Reminders")'); - await page.click('button:has-text("Add Reminder")'); - await page.fill('input[name="title"]', 'Temporary Reminder'); - await page.click('button[type="submit"]'); - - // Delete it - await page.click('[data-testid="delete-reminder"]'); - await page.click('button:has-text("Delete")'); // Confirm - - await expect(page.locator('text=Temporary Reminder')).not.toBeVisible(); - }); - - test('should show scheduled medication doses', async ({ page }) => { - // Add a medication first - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Scheduled Med'); - await page.fill('input[name="dosage"]', '5mg'); - await page.selectOption('select[name="frequency"]', 'daily'); - await page.fill('input[name="times"]', '3'); // 3 times daily - await page.click('button[type="submit"]'); - - // Should see scheduled doses for today - await expect(page.locator('text=Scheduled Med')).toBeVisible(); - await expect(page.locator('button:has-text("Take")')).toHaveCount(3); - }); - - test('should handle missed doses', async ({ page }) => { - // This would test the logic for marking doses as missed - // when they pass their scheduled time - - // Add medication with past schedule - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Past Due Med'); - await page.fill('input[name="dosage"]', '10mg'); - await page.click('button[type="submit"]'); - - // Simulate time passing or manually mark as missed - // This would depend on your app's specific implementation - }); -}); diff --git a/tests/e2e/test-utils.ts b/tests/e2e/test-utils.ts deleted file mode 100644 index 42559ae..0000000 --- a/tests/e2e/test-utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Optimized test utilities to reduce duplication -import { Page } from '@playwright/test'; - -export class TestUtils { - static async loginAsAdmin(page: Page): Promise { - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async loginAsUser( - page: Page, - email: string = 'testuser@example.com', - password: string = 'TestPassword123!' - ): Promise { - await page.goto('/'); - await page.fill('input[type="email"]', email); - await page.fill('input[type="password"]', password); - await page.click('button[type="submit"]'); - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async waitForApp(page: Page): Promise { - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async openModal(page: Page, buttonText: string): Promise { - await page.click(`button:has-text("${buttonText}")`); - } - - static async closeModal(page: Page): Promise { - await page.click('button:has-text("Close")'); - } -} diff --git a/tests/e2e/ui-navigation.spec.ts b/tests/e2e/ui-navigation.spec.ts deleted file mode 100644 index 284efd2..0000000 --- a/tests/e2e/ui-navigation.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test.describe('User Interface and Navigation', () => { - test.beforeEach(async ({ page }) => { - // Login as admin - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - await expect(page.locator('h1')).toContainText('Medication Reminder'); - }); - - test('should display main navigation elements', async ({ page }) => { - await expect( - page.locator('button:has-text("Add Medication")') - ).toBeVisible(); - await expect(page.locator('button:has-text("Manage")')).toBeVisible(); - await expect(page.locator('button:has-text("History")')).toBeVisible(); - await expect(page.locator('button:has-text("Stats")')).toBeVisible(); - }); - - test('should toggle theme', async ({ page }) => { - // Click theme switcher - await page.click('[data-testid="theme-switcher"]'); - - // Check if dark mode is applied - await expect(page.locator('html')).toHaveClass(/dark/); - - // Toggle back to light mode - await page.click('[data-testid="theme-switcher"]'); - await expect(page.locator('html')).not.toHaveClass(/dark/); - }); - - test('should open and close account modal', async ({ page }) => { - // Click account button - await page.click('button:has-text("Account")'); - - // Should show account modal - await expect(page.locator('text=Account Settings')).toBeVisible(); - - // Close modal - await page.click('button:has-text("Close")'); - await expect(page.locator('text=Account Settings')).not.toBeVisible(); - }); - - test('should open stats modal', async ({ page }) => { - await page.click('button:has-text("Stats")'); - - await expect(page.locator('text=Medication Statistics')).toBeVisible(); - await expect(page.locator('text=Weekly Adherence')).toBeVisible(); - - await page.click('button:has-text("Close")'); - }); - - test('should show current time and date', async ({ page }) => { - // Should display current time somewhere on the page - const timeElement = page.locator('[data-testid="current-time"]'); - await expect(timeElement).toBeVisible(); - }); - - test('should handle responsive design', async ({ page }) => { - // Test mobile viewport - await page.setViewportSize({ width: 375, height: 667 }); - - // Should still show main elements - await expect(page.locator('h1')).toBeVisible(); - await expect( - page.locator('button:has-text("Add Medication")') - ).toBeVisible(); - - // Reset to desktop - await page.setViewportSize({ width: 1280, height: 720 }); - }); - - test('should search medications', async ({ page }) => { - // Add a test medication first - await page.click('button:has-text("Add Medication")'); - await page.fill('input[name="name"]', 'Searchable Medicine'); - await page.fill('input[name="dosage"]', '10mg'); - await page.click('button[type="submit"]'); - - // Use search functionality - await page.fill('input[placeholder*="search"]', 'Searchable'); - - // Should show filtered results - await expect(page.locator('text=Searchable Medicine')).toBeVisible(); - - // Clear search - await page.fill('input[placeholder*="search"]', ''); - }); - - test('should logout user', async ({ page }) => { - // Click logout (usually in avatar dropdown) - await page.click('[data-testid="avatar-dropdown"]'); - await page.click('button:has-text("Logout")'); - - // Should return to login page - await expect(page.locator('h2')).toContainText(['Sign In', 'Login']); - }); -});