Skip to main content

End-to-End Testing

This guide covers how to run and write end-to-end (E2E) tests for TestPlanIt using Playwright.

Prerequisites​

  • Node.js 24+ and pnpm installed
  • Docker, to run the E2E services
  • TestPlanIt development environment set up

Setup​

1. Start the E2E Services​

E2E tests run against separate services from your development stack: PostgreSQL, Valkey, Elasticsearch and S3-compatible file storage (RustFS). From the testplanit/ directory, start them with Docker:

cd testplanit
docker compose -f docker-compose.e2e.yml up -d

The ports (Postgres 5433, Valkey 6377, Elasticsearch 9221, storage 9100) don't clash with docker-compose.dev.yml, so both stacks can run at once.

2. Configure Environment​

Copy the example environment file. Its defaults match the services above:

cp .env.e2e.example .env.e2e

To use your own services instead, point DATABASE_URL, VALKEY_URL, ELASTICSEARCH_NODE and the AWS_* storage settings at them. The database is wiped on every run, so never point it at a database you want to keep.

Setup stops with an error if the storage bucket is not writable with the configured credentials. With the local storage service it also creates the bucket and sets its public-read and CORS rules.

Optional: Email Server Configuration​

Some E2E tests (particularly signup tests) send verification emails. To prevent undeliverable email alerts, you can configure a test email server:

# Add to .env.e2e or create .env.test.local (gitignored)
EMAIL_SERVER_HOST=your-smtp-server.com
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=your-username
EMAIL_SERVER_PASSWORD=your-password
TEST_EMAIL_DOMAIN=your-test-domain.com

Alternatives for email testing:

  • Ethereal Email: Free fake SMTP service at ethereal.email - catches emails without actually sending them
  • MailHog: Local SMTP test server (docker run -p 1025:1025 -p 8025:8025 mailhog/mailhog)
  • Your own test server: Configure TEST_EMAIL_DOMAIN to match your test email domain

If not configured, tests will use @example.com addresses (which will fail delivery).

3. Install Playwright Browsers​

cd testplanit
pnpm exec playwright install

4. Setup the E2E Database​

Run migrations and seed the E2E database with test data:

pnpm test:e2e:setup-db

Running Tests​

All E2E test commands are run from the testplanit/ directory.

Run All Tests​

pnpm test:e2e

This will:

  1. Load the .env.e2e environment
  2. Start a development server on port 3002 (if not already running)
  3. Run all Playwright tests

For faster and more stable tests, use a production build:

pnpm test:e2e:prod

This builds the application first, then runs tests against the production build with more parallel workers.

Complete Setup + Run​

To set up the database and run tests in one command:

pnpm test:e2e:run

Interactive UI Mode​

Debug and explore tests visually:

pnpm test:e2e:ui

Headed Mode​

Watch the browser as tests execute:

pnpm test:e2e:headed

Debug Mode​

Step through tests with Playwright Inspector:

pnpm test:e2e:debug

View Test Report​

After running tests, view the HTML report:

pnpm test:e2e:report

Test Configuration​

The Playwright configuration is in testplanit/e2e/playwright.config.ts.

Key Settings​

SettingValueDescription
Port3002E2E tests run on port 3002 to avoid conflicts with dev server
Timeout60sGlobal test timeout
Retries2 (CI only)Tests retry on CI, not locally
Workers1 (dev) / 6 (prod)More workers with production build
BrowserChromiumCurrently testing on Chrome only

Environment Variables​

VariableDescription
E2E_PORTOverride the default port (3002)
E2E_BASE_URLOverride the base URL for tests
E2E_VIDEOSet to on to always record video
E2E_PRODSet to on to run against production build
TEST_EMAIL_DOMAINEmail domain for test users (default: example.com). Configure with your test email server domain to receive verification emails during signup tests.

Project Structure​

testplanit/e2e/
├── playwright.config.ts # Playwright configuration
├── global-setup.ts # Authentication setup (runs once)
├── setup-db.ts # Database seeding script
├── fixtures/
│ ├── index.ts # Test fixtures
│ └── api.fixture.ts # API helper for test data
├── page-objects/
│ ├── base.page.ts # Base page object
│ ├── signin.page.ts # Sign-in page
│ └── repository/
│ └── repository.page.ts
├── tests/
│ ├── auth/ # Authentication tests
│ └── repository/ # Repository feature tests
├── .auth/ # Stored auth state (gitignored)
├── playwright-report/ # HTML reports (gitignored)
└── test-results/ # Test artifacts (gitignored)

Writing Tests​

Adding New Test Files​

Place new test files in the tests/ directory, organized by feature:

tests/
├── auth/ # Authentication-related tests
│ └── auth.spec.ts
├── repository/ # Repository feature tests
│ └── Test Repository Management/
│ ├── folder-creation.spec.ts
│ ├── test-case-management.spec.ts
│ └── ...
└── <new-feature>/ # Add new feature directories as needed
└── <test-name>.spec.ts

Test files must use the .spec.ts extension to be picked up by Playwright.

Using Page Objects​

import { test, expect } from "../../fixtures";
import { RepositoryPage } from "../../page-objects/repository/repository.page";

test("should create a folder", async ({ page }) => {
const repositoryPage = new RepositoryPage(page);
await repositoryPage.goto(1); // Project ID

await repositoryPage.createFolder("My New Folder");
await repositoryPage.verifyFolderExists("My New Folder");
});

Using the API Fixture​

Create test data via API for faster, more reliable tests:

test("should display test cases", async ({ api, page }) => {
// Create test data via API
const folderId = await api.createFolder(1, "Test Folder");
await api.createTestCase(1, folderId, "Test Case 1");

// Navigate and verify
const repositoryPage = new RepositoryPage(page);
await repositoryPage.goto(1);
await repositoryPage.selectFolder(folderId);
await repositoryPage.verifyTestCaseExists("Test Case 1");

// Cleanup is automatic via the api fixture
});

Best Practices​

  1. Use API for test data: Create data via API instead of UI for speed and reliability
  2. Page Object Pattern: Encapsulate page interactions in page objects
  3. Automatic cleanup: The API fixture automatically cleans up created data after each test
  4. Isolated database: Tests run against a separate database to avoid affecting development data

Troubleshooting​

Authentication Issues​

If tests fail with authentication errors:

rm -rf testplanit/e2e/.auth/
pnpm test:e2e

Database Connection Issues​

  1. Ensure PostgreSQL is running
  2. Verify .env.e2e has correct credentials
  3. Re-run database setup: pnpm test:e2e:setup-db

Port Conflicts​

If port 3002 is in use:

E2E_PORT=3003 pnpm test:e2e

Slow Tests​

  • Use E2E_PROD=on to run against production build
  • Use API fixture to create test data instead of UI
  • Use test.only() to run a single test during development

Flaky Tests​

  • Check for timing issues and add proper waits
  • Use await expect(locator).toBeVisible() instead of fixed delays
  • Increase timeouts for slow operations