Skip to main content

Testing

Sessiq has four kinds of automated tests. Unit tests need no database. Integration and API end-to-end tests run against a dedicated test database. The Playwright suite drives the web app in a real browser.

SuiteRunnerLocationFile patternCommand
API unitVitestapps/api/tests/unit/*.spec.tspnpm test:api:unit
API integrationJestapps/api/tests/integration/*.int.spec.tspnpm test:api:int
API end-to-endJestapps/api/tests/e2e/*.e2e.spec.tspnpm test:api:e2e
Browser (QA)Playwrightapps/web/e2e/specs/*.spec.tspnpm test:qa

Other suites:

  • Web unit tests use Jest: pnpm -F web test.
  • The scheduling engine has its own tests: pnpm -F @aetech/scheduling-engine run test.

The repository's TESTING_GUIDE.md has the detailed conventions.

Unit tests​

pnpm test:api:unit

Unit tests run with no database (SKIP_DB=1). They mock the database layer.

To mock tenant-scoped database access, use createMockRlsContext() from apps/api/tests/helpers/mock-rls-context.ts. It replaces @booking/db/rls-context so that withTenant() passes a mocked transaction client to your callback:

import { vi } from 'vitest';
import { createMockRlsContext } from '../../helpers/mock-rls-context';

vi.mock('@booking/db/rls-context', () => createMockRlsContext());

When a mock does not resolve, look for the cause first: __mocks__ directories, the shared helpers in apps/api/tests/helpers/ and apps/api/tests/setup/, and the import chain.

Integration tests​

Integration tests use a separate PostgreSQL container on port 5433. Set it up once:

docker compose --profile test up -d postgres_test
pnpm exec dotenv -e .env.test -- pnpm db:test:reset
  • The container bootstraps its roles, extensions and tenant functions from packages/db/scripts/bootstrap-test-db.sql on first start.
  • pnpm db:test:reset syncs the Prisma schema and applies the custom SQL. It needs DATABASE_URL pointing at the test database, which .env.test provides. It is idempotent; re-run it whenever the schema changes.
  • If bootstrap-test-db.sql changes, recreate the volume: docker compose --profile test down -v, then start the container again.

Then run the suite:

pnpm test:api:int

The runner reads .env.test, runs files in batches and retries batches that fail on connection errors. If the schema is out of date, the tests stop with a hint to run pnpm db:test:reset.

Naming​

Every integration test file must end in .int.spec.ts. The runner only picks up that suffix; files named .integration.spec.ts are silently skipped.

Building the module graph​

Compose the NestJS app with createTestApp() from apps/api/tests/setup/test-app.ts. It returns a TestingModuleBuilder built from TestAppModule, which mirrors the production AppModule minus the global guard, pipe and filter providers. It also sets permissive default mocks for JwtAuthGuard, TenantGuard, SiteAccessGuard and RbacGuard.

import { createTestApp } from '../../setup/test-app';
import { createIntegrationTestApp } from '../../setup/integration-helpers';

const moduleRef = await createTestApp()
.overrideGuard(JwtAuthGuard)
.useValue(myJwtMock)
.compile();
const app = createIntegrationTestApp(moduleRef);
await app.init();

Chain .overrideGuard(...) and .overrideProvider(...) for per-test behaviour. Do not hand-list modules or build your own preset; importing the real module graph keeps tests in step with production.

Because the global guards are stripped, a test cannot prove that a route is protected by them. Assert the route's metadata (for example that it is not marked public) instead.

API end-to-end tests​

pnpm test:api:e2e

These use the same test database and createTestApp(), and exercise complete flows such as customer sign-in and payments through the HTTP layer.

Browser tests (Playwright)​

The Playwright suite in apps/web/e2e/specs/ runs against a running web app and API (by default http://localhost:3000, override with BASE_URL).

CommandRuns
pnpm test:qaThe whole suite
pnpm test:qa:smokeTests tagged @smoke
pnpm test:qa:paymentTests tagged @payment
pnpm test:qa:mobileTests tagged @mobile
pnpm test:qa:a11yTests tagged @accessibility

Locally only the chromium-light project runs. CI runs every project (Chromium light and dark, Firefox, WebKit, mobile Chrome, mobile Safari, high contrast). Pick one locally with pnpm test:qa -- --project=mobile-chrome.

Before you write a browser test:

  • Take selector text from apps/web/messages/en.json and fi.json, not from memory.
  • Check the exact API paths in the controller decorators or apps/api/openapi.json.
  • Check rate limits on the endpoints you call, and the permissions the test user's role has.
  • Keep screenshots and scripts you want to keep outside apps/web/e2e/test-results/; it is cleared on every run.

Types in tests​

All test files are typechecked by pnpm typecheck. Fix type errors in tests like any other code. Do not add // @ts-nocheck.