Skip to content

Testing & Debugging

This guide covers running tests, debugging, and the pre-commit hook system.

Running Tests

Web Unit Tests (Vitest)

pnpm --filter @meisterbill/web test:unit     # Run web tests
pnpm --filter @meisterbill/web test:unit --run # Run without watch mode

Test Count: 143 passing tests (1 skipped) Framework: Vitest Location: apps/web/test/unit/

API Tests (Jest)

pnpm --filter @meisterbill/api test          # Run API tests

Test Count: 397 passing tests Framework: Jest Location: apps/api/src/**/*.test.ts

App Unit Tests (Vitest)

pnpm --filter @meisterbill/app test:unit     # Run app unit tests

Framework: Vitest Location: apps/app/

Schema Tests (Vitest)

pnpm --filter @meisterbill/schemas test      # Run schema tests

Test Count: 414 passing tests Framework: Vitest Location: schemas/src/**/*.test.ts

E2E Tests

pnpm --filter @meisterbill/app test:e2e      # App E2E tests (Cypress)
pnpm e2e                                      # Full E2E tests (Playwright)

App E2E: Cypress Full E2E: Playwright

Pre-commit Hook

A git pre-commit hook automatically runs all unit tests before allowing commits.

Location: .git/hooks/pre-commit

Tests Run: - Web unit tests (@meisterbill/web) - API tests (@meisterbill/api) - App unit tests (@meisterbill/app) - Schema tests (@meisterbill/schemas)

Behavior: - Commit is aborted if any tests fail - Ensures only tested code is committed to the repository

The hook is installed automatically and will run on every git commit.

Test File Organization

Web Tests

apps/web/
├── test/unit/
│   ├── CookieBanner.spec.ts
│   ├── CustomerHelper.spec.ts
│   ├── Formater.spec.ts
│   ├── image-processing.spec.ts
│   ├── useAuth.spec.ts
│   ├── useInvoiceStatusMachine.spec.ts
│   └── plugins/
│       └── AuthInit.spec.ts
└── pages/
    └── **/__tests__/
        └── *.test.ts

API Tests

apps/api/src/
├── routes/
│   ├── addresses.test.ts
│   ├── auth.test.ts
│   ├── brands.test.ts
│   ├── credit-notes.test.ts
│   ├── founder-deal.test.ts
│   ├── payments.test.ts
│   ├── projects.test.ts
│   └── ...
├── services/
│   ├── EmailService.test.ts
│   └── CommitSummarizationService.test.ts
└── utils/
    └── documentNumber.test.ts

Schema Tests

schemas/src/
├── AccountSchema.test.ts
├── AddressFormatters.test.ts
├── AddressSchema.test.ts
├── BreadcrumbSchema.test.ts
├── CurrencyCodeSchema.test.ts
├── CustomerSchema.test.ts
├── DocumentItemSchema.test.ts
├── DocumentSchema.test.ts
├── EventSchema.test.ts
├── ServiceProviderSchema.test.ts
├── TaxCalculationSchema.test.ts
├── TaxNumberSchema.test.ts
└── forms/
    ├── NewPasswordSchema.test.ts
    └── SignUpSchema.test.ts

Debugging

Console Logging

Use console.debug for JavaScript debugging (not console.log):

console.debug('User logged in:', userId);
console.debug('Invoice state:', invoice);

This follows the project convention defined in CLAUDE.md.

API Hot Reloading

The API server has hot reloading enabled. No need to restart after changes.

Web Hot Reloading

The web server also has hot reloading. Changes are reflected immediately.

Testing Specific Files

# Web - test specific file
pnpm --filter @meisterbill/web test:unit CustomerHelper.spec.ts

# API - test specific file pattern
pnpm --filter @meisterbill/api test addresses

# Schemas - test specific file
pnpm --filter @meisterbill/schemas test AddressFormatters

Common Test Scenarios

Testing Event Bus

Example from apps/web/test/unit/plugins/AuthInit.spec.ts:

import { eventBus } from '~/composables/useEventBus';

it('emits auth.login.success event', () => {
  const handler = vi.fn();
  eventBus.on('auth.login.success', handler);

  // Trigger login
  await login({ email, password });

  expect(handler).toHaveBeenCalledWith(expect.objectContaining({
    userId: 'test-user-id',
  }));
});

Testing State Machines

Example from apps/web/test/unit/useInvoiceStatusMachine.spec.ts:

import { useInvoiceStatusMachine } from '~/composables/useInvoiceStatusMachine';

it('transitions from draft to finalized', () => {
  const { canTransitionTo, getValidNextStatuses } = useInvoiceStatusMachine();

  expect(canTransitionTo('draft', 'finalized')).toBe(true);
  expect(getValidNextStatuses('draft')).toContain('finalized');
});

Testing API Routes

Example from apps/api/src/routes/auth.test.ts:

import { app } from '../index';

describe('POST /auth/signup', () => {
  it('creates a new user', async () => {
    const res = await app.request('/auth/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: 'test@example.com',
        password: 'SecurePass123!',
        terms_accepted: true,
      }),
    });

    expect(res.status).toBe(201);
  });
});

Test Coverage Goals

  • Critical paths: 100% coverage
  • Business logic: 90%+ coverage
  • UI components: 70%+ coverage
  • Overall: 80%+ coverage target

Continuous Integration

All tests run automatically in CI/CD before deployment. Tests are defined in the .gitea/workflows/deploy.yml file.

See Also