diff --git a/.env.example b/.env.example index 52cf5a5..2fc90cb 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,14 @@ -WEBSITE_URL= \ No newline at end of file +WEBSITE_URL= + +TEST_USER_NAME= +TEST_USER_EMAIL= +TEST_USER_PASSWORD= + +TEST_ADMIN_EMAIL= +TEST_ADMIN_PASSWORD= + +TEST_LAWYER_EMAIL= +TEST_LAWYER_PASSWORD= + +TEST_CLIENT_EMAIL= +TEST_CLIENT_PASSWORD= \ No newline at end of file diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index b5b5749..fd8fe82 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,6 +10,15 @@ jobs: runs-on: ubuntu-latest env: WEBSITE_URL: ${{ secrets.WEBSITE_URL }} + TEST_USER_NAME: ${{ secrets.TEST_USER_NAME }} + TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }} + TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }} + TEST_ADMIN_EMAIL: ${{ secrets.TEST_ADMIN_EMAIL }} + TEST_ADMIN_PASSWORD: ${{ secrets.TEST_ADMIN_PASSWORD }} + TEST_LAWYER_EMAIL: ${{ secrets.TEST_LAWYER_EMAIL }} + TEST_LAWYER_PASSWORD: ${{ secrets.TEST_LAWYER_PASSWORD }} + TEST_CLIENT_EMAIL: ${{ secrets.TEST_CLIENT_EMAIL }} + TEST_CLIENT_PASSWORD: ${{ secrets.TEST_CLIENT_PASSWORD }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -19,6 +28,8 @@ jobs: run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps + - name: Run auth setup + run: npx playwright test tests/auth.setup.ts --project=setup - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v4 diff --git a/package-lock.json b/package-lock.json index 5895cf8..2fb363c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "dotenv": "^17.2.1" + "dotenv": "^17.2.3" }, "devDependencies": { "@playwright/test": "^1.55.0", @@ -43,9 +43,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "license": "BSD-2-Clause", "engines": { "node": ">=12" diff --git a/package.json b/package.json index 3acebe3..223d70c 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,6 @@ "@types/node": "^24.3.0" }, "dependencies": { - "dotenv": "^17.2.1" + "dotenv": "^17.2.3" } } diff --git a/playwright.config.ts b/playwright.config.ts index 870311b..0b66e0e 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -34,6 +34,11 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ + { + name: 'setup', + testMatch: /.*\.setup\.ts/, + }, + { name: 'chromium', use: { ...devices['Desktop Chrome'] }, diff --git a/tests/auth.setup.ts b/tests/auth.setup.ts new file mode 100644 index 0000000..0132d11 --- /dev/null +++ b/tests/auth.setup.ts @@ -0,0 +1,44 @@ +import { test as setup, expect } from '@playwright/test'; + +const TEST_ADMIN_EMAIL = process.env.TEST_ADMIN_EMAIL; +const TEST_ADMIN_PASSWORD = process.env.TEST_ADMIN_PASSWORD; +const TEST_LAWYER_EMAIL = process.env.TEST_LAWYER_EMAIL; +const TEST_LAWYER_PASSWORD = process.env.TEST_LAWYER_PASSWORD; +const TEST_CLIENT_EMAIL = process.env.TEST_CLIENT_EMAIL; +const TEST_CLIENT_PASSWORD = process.env.TEST_CLIENT_PASSWORD; + +const adminFile = 'playwright/.auth/admin.json'; + +setup('authenticate as admin', async ({ page }) => { + await page.goto('/login.php'); + + await page.fill('input[name="email"]', TEST_ADMIN_EMAIL!); + await page.fill('input[name="password"]', TEST_ADMIN_PASSWORD!); + await page.getByRole('button', { name: 'Login Now' }).click(); + + await page.context().storageState({ path: adminFile }); +}); + +const clientFile = 'playwright/.auth/client.json'; + +setup('authenticate as client', async ({ page }) => { + await page.goto('/login.php'); + + await page.fill('input[name="email"]', TEST_CLIENT_EMAIL!); + await page.fill('input[name="password"]', TEST_CLIENT_PASSWORD!); + await page.getByRole('button', { name: 'Login Now' }).click(); + + await page.context().storageState({ path: clientFile }); +}); + +const lawyerFile = 'playwright/.auth/lawyer.json'; + +setup('authenticate as lawyer', async ({ page }) => { + await page.goto('/login.php'); + + await page.fill('input[name="email"]', TEST_LAWYER_EMAIL!); + await page.fill('input[name="password"]', TEST_LAWYER_PASSWORD!); + await page.getByRole('button', { name: 'Login Now' }).click(); + + await page.context().storageState({ path: lawyerFile }); +}); \ No newline at end of file diff --git a/tests/auth.spec.ts b/tests/auth.spec.ts new file mode 100644 index 0000000..96cdf5c --- /dev/null +++ b/tests/auth.spec.ts @@ -0,0 +1,159 @@ +import { test, expect } from '@playwright/test'; +import crypto from 'crypto'; + +// Localize environment variables +const TEST_USER_NAME = process.env.TEST_USER_NAME; +const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL; +const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD; +const TEST_ADMIN_EMAIL = process.env.TEST_ADMIN_EMAIL; +const TEST_ADMIN_PASSWORD = process.env.TEST_ADMIN_PASSWORD; +const TEST_LAWYER_EMAIL = process.env.TEST_LAWYER_EMAIL; +const TEST_LAWYER_PASSWORD = process.env.TEST_LAWYER_PASSWORD; +const TEST_CLIENT_EMAIL = process.env.TEST_CLIENT_EMAIL; +const TEST_CLIENT_PASSWORD = process.env.TEST_CLIENT_PASSWORD; + +// Check if required environment variables are set +if (!TEST_USER_NAME || !TEST_USER_EMAIL || !TEST_USER_PASSWORD) { + throw new Error('Missing required environment variables: TEST_USER_NAME, TEST_USER_EMAIL, TEST_USER_PASSWORD'); +} + +if (!TEST_ADMIN_EMAIL || !TEST_ADMIN_PASSWORD) { + throw new Error('Missing required environment variables: TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD'); +} + +if (!TEST_LAWYER_EMAIL || !TEST_LAWYER_PASSWORD) { + throw new Error('Missing required environment variables: TEST_LAWYER_EMAIL, TEST_LAWYER_PASSWORD'); +} + +if (!TEST_CLIENT_EMAIL || !TEST_CLIENT_PASSWORD) { + throw new Error('Missing required environment variables: TEST_CLIENT_EMAIL, TEST_CLIENT_PASSWORD'); +} + +// Setup test - ensure test user exists for duplicate tests +test.beforeAll('setup: create test user if not exists', async ({ browser }) => { + const context = await browser.newContext(); + const page = await context.newPage(); + + try { + await page.goto('/register.php'); + + // Try to create the test user + await page.fill('input[name="first_name"]', TEST_USER_NAME); + await page.fill('input[name="last_name"]', TEST_USER_NAME); + await page.fill('input[name="email"]', TEST_USER_EMAIL); + await page.fill('input[name="password"]', TEST_USER_PASSWORD); + + await page.getByRole('button', { name: 'Create Account' }).click(); + + // Don't fail if user already exists - that's what we want + console.log('Test user setup completed (may already exist)'); + } catch (error) { + console.log('Test user setup: user might already exist'); + } finally { + await context.close(); + } +}); + +function generateUniqueUser() { + const uniqueId = `${Date.now()}-${crypto.randomBytes(4).toString('hex')}`; + return { + first_name: `${uniqueId}`, + last_name: `TestUser`, + email: `test+${uniqueId}@example.com`, + password: 'Password123!' + }; +} + +test.describe("User Registration", () => { + + test('users can register with unique email', async ({ page }) => { + await page.goto('/register.php'); + + const user = generateUniqueUser(); + + // Fill in registration form with unique email + await page.fill('input[name="first_name"]', user.first_name); + await page.fill('input[name="last_name"]', user.last_name); + await page.fill('input[name="email"]', user.email); + await page.fill('input[name="password"]', user.password); + await page.fill('input[name="confirm_password"]', user.password); + await page.getByRole('button', { name: 'Create Account' }).click(); + + await expect(page).toHaveURL(/login/); + }); + + test('users cannot register with duplicate email', async ({ page }) => { + await page.goto('/register.php'); + + const user = generateUniqueUser(); + + await page.fill('input[name="first_name"]', user.first_name); + await page.fill('input[name="last_name"]', user.last_name); + await page.fill('input[name="email"]', TEST_USER_EMAIL); + await page.fill('input[name="password"]', user.password); + await page.fill('input[name="confirm_password"]', user.password); + await page.getByRole('button', { name: 'Create Account' }).click(); + + await expect(page.locator('.alert-danger')).toBeVisible(); + }); +}) + +test.describe("User Login", () => { + + test('users can login with correct email and password', async ({ page }) => { + await page.goto('/login.php'); + + await page.fill('input[name="email"]', TEST_USER_EMAIL!); + await page.fill('input[name="password"]', TEST_USER_PASSWORD!); + await page.getByRole('button', { name: 'Login' }).click(); + + await expect(page).toHaveURL(/dashboard/); + }); + + test('users cannot login with incorrect email or password', async ({ page }) => { + await page.goto('/login.php'); + + await page.fill('input[name="email"]', TEST_USER_EMAIL!); + await page.fill('input[name="password"]', 'wrongpassword123'); + await page.getByRole('button', { name: 'Login' }).click(); + + await expect(page.locator('.alert-danger')).toBeVisible(); + await expect(page).toHaveURL(/login/); + }); +}) + +test.describe("Role Based Access Control", () => { + + test.describe("Admin Role", () => { + test.use({ storageState: 'playwright/.auth/admin.json' }); + + test('admin users can access admin features', async ({ page }) => { + await page.goto('/login.php'); + + await expect(page).toHaveURL(/dashboard/); + await expect(page.locator('span.badge-role')).toHaveText(/Admin/i); + }); + }); + + test.describe("Lawyer Role", () => { + test.use({ storageState: 'playwright/.auth/lawyer.json' }); + + test('lawyers can access lawyer features', async ({ page }) => { + await page.goto('/login.php'); + + await expect(page).toHaveURL(/dashboard/); + await expect(page.locator('span.badge-role')).toHaveText(/Lawyer/i); + }); + }); + + test.describe("Client Role", () => { + test.use({ storageState: 'playwright/.auth/client.json' }); + + test('clients can access client features', async ({ page }) => { + await page.goto('/login.php'); + + await expect(page).toHaveURL(/dashboard/); + await expect(page.locator('span.badge-role')).toHaveText(/Client/i); + }); + }); +}); diff --git a/tests/example.spec.ts b/tests/example.spec.ts index 6f6cc6f..df01c6f 100644 --- a/tests/example.spec.ts +++ b/tests/example.spec.ts @@ -11,8 +11,8 @@ test('register page', async ({ page }) => { await page.goto('/login.php'); // Click the get started link. - await page.getByRole('link', { name: 'Create an account' }).click(); + await page.getByRole('link', { name: 'Create Account' }).click(); // Expects page to have a button with the name of Create Account. - await expect(page.getByRole('button', { name: 'Register' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Create Account' })).toBeVisible(); }); \ No newline at end of file