Test/e2e login auth (#19)

* Add basic test for login and auth state

* Remove test file

* Change login setup

* Apply suggestions from code review

Co-authored-by: Martin Braquet <martin.braquet@gmail.com>

* Change signin structure to use UI

* Fix URL loading

* Spin up backend server as well for E2E

---------

Co-authored-by: Martin Braquet <martin.braquet@gmail.com>
This commit is contained in:
Nicholas Chamberlain
2025-11-18 14:17:00 -08:00
committed by GitHub
parent e5fc734b90
commit f7fb0c6c82
14 changed files with 97 additions and 12 deletions

View File

@@ -67,11 +67,15 @@ jobs:
NEXT_PUBLIC_SUPABASE_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_KEY }}
run: |
npx nyc --reporter=lcov yarn --cwd=web serve &
npx nyc --reporter=lcov yarn --cwd=backend/api dev &
npx wait-on http://localhost:3000
npx playwright test tests/e2e
SERVER_PID=$(fuser -k 3000/tcp)
echo $SERVER_PID
kill $SERVER_PID
SERVER_PID=$(fuser -k 8088/tcp)
echo $SERVER_PID
kill $SERVER_PID
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@
# Playwright
/tests/reports/playwright-report
/tests/e2e/web/.auth/
# next.js
/.next/

View File

@@ -24,6 +24,7 @@
"test:coverage": "yarn workspaces run test --coverage",
"test:watch": "yarn workspaces run test --watch",
"test:update": "yarn workspaces run test --updateSnapshot",
"test:e2e": "./scripts/e2e.sh",
"playwright": "playwright test",
"playwright:ui": "playwright test --ui",
"playwright:debug": "playwright test --debug",

View File

@@ -1,4 +1,5 @@
import { defineConfig, devices } from '@playwright/test';
import path from 'path';
export default defineConfig({
testDir: './tests/e2e',
@@ -13,8 +14,9 @@ export default defineConfig({
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
name: 'main',
use: {
...devices['Desktop Chrome'], },
},
// {
// name: 'firefox',
@@ -25,4 +27,5 @@ export default defineConfig({
// use: { ...devices['Desktop Safari'] },
// },
],
});
});

View File

@@ -10,9 +10,13 @@ export NEXT_PUBLIC_API_URL=localhost:8088
export NEXT_PUBLIC_FIREBASE_ENV=DEV
npx nyc --reporter=lcov yarn --cwd=web serve &
npx nyc --reporter=lcov yarn --cwd=backend/api dev &
npx wait-on http://localhost:3000
npx playwright test tests/e2e
SERVER_PID=$(fuser -k 3000/tcp)
echo $SERVER_PID
kill $SERVER_PID
SERVER_PID=$(fuser -k 8088/tcp)
echo $SERVER_PID
kill $SERVER_PID

View File

View File

@@ -0,0 +1,5 @@
export const config = {
BASE_URL: 'http://localhost:3000',
DEFAULT_LOGIN: 'defaultUser@dev.com',
DEFAULT_PASSWORD: 'defaultPassword',
};

View File

@@ -0,0 +1,24 @@
import { test as base, Page, expect } from '@playwright/test';
import { SignInPage } from '../pages/signInPage';
import { config } from '../TESTING_CONFIG';
export const test = base.extend<{
authenticatedPage: Page;
}>({
authenticatedPage: async ({ page }, use) => {
const signInPage = new SignInPage(page);
await page.goto('/signin');
await signInPage.fillEmailField(config.DEFAULT_LOGIN);
await signInPage.fillPasswprdField(config.DEFAULT_PASSWORD);
await signInPage.clickSignInWithEmailButton();
await page.waitForLoadState('networkidle');
await page.waitForURL('/');
expect(page.url()).not.toContain('/signin')
await use(page);
},
});

View File

View File

View File

@@ -0,0 +1,42 @@
import { expect, Locator, Page } from '@playwright/test';
//sets up of all the functions that signin tests will use.
export class SignInPage{
private readonly signInLink: Locator;
private readonly emailField: Locator;
private readonly passwordField: Locator;
private readonly signInWithEmailButton: Locator;
private readonly signInWithGoogleButton: Locator;
constructor(public readonly page: Page) {
this.signInLink=page.getByRole('link', { name: 'Sign in' });
this.emailField=page.getByLabel('Email');
this.passwordField=page.getByLabel('Password');
this.signInWithEmailButton=page.getByRole('button',{name: 'Sign in With Email'});
this.signInWithGoogleButton=page.getByRole('button',{name: 'Google'});
}
async clickSignInText() {
await this.signInLink.click();
}
async clickSignInWithEmailButton() {
await this.signInWithEmailButton.click();
}
async clickSignInWithEGoogleButton() {
await this.signInWithGoogleButton.click();
}
async fillEmailField(email: string) {
await expect(this.emailField).toBeVisible();
await this.emailField.fill(email);
}
async fillPasswprdField(password: string) {
await expect(this.passwordField).toBeVisible();
await this.passwordField.fill(password);
}
}

View File

View File

@@ -0,0 +1,10 @@
import { expect } from '@playwright/test';
import { test } from '../fixtures/signInFixture';
test('should be logged in and see settings page', async ({ authenticatedPage }) => {
await authenticatedPage.goto('/settings');
await expect(
authenticatedPage.getByRole('heading', { name: 'Theme' })
).toBeVisible();
});

View File

@@ -1,9 +0,0 @@
import {expect, test} from '@playwright/test';
test('shows', async ({page}) => {
await page.goto('/'); // Adjust this to your route
expect(await page.title()).toBe('Compass');
//
// const spinner = page.locator('[data-testid="spinner"]');
// await expect(spinner).toBeVisible();
});