Skip to content

Testing

Overview

Sutomo uses Pest v4 for PHP testing and Vitest for frontend JavaScript testing. Both are configured with coverage reporting.


Stack

LayerFrameworkConfigCoverage
PHP (Unit + Feature)Pest v4phpunit.xmlMinimum 75% overall
PHP (E2E)Playwrightplaywright.config.ts
JavaScript (Unit)Vitestvitest.config.tsMinimum 60%
JavaScript (E2E)Playwrightplaywright.config.ts

Coverage Standard

MetricMinimum TargetMeasured By
Lines75%Pest coverage report
Methods80%Pest coverage report
Branches60%Pest coverage report

Coverage reports are generated to tests/coverage-reports/html/ and can be viewed in a browser.

bash
# View coverage report
open tests/coverage-reports/html/index.html

Running Tests

PHP Tests (Pest)

bash
# Run all tests
composer run test

# Run specific file
php artisan test --compact --filter=CandidateTest

# Run with coverage
php artisan test --coverage

# Run specific suite
php artisan test --compact tests/Unit
php artisan test --compact tests/Feature

Frontend Tests (Vitest)

bash
# Run all frontend tests
npm run test:unit

# Run specific file
npx vitest run path/to/test.spec.ts

# Watch mode
npx vitest

E2E Tests (Playwright)

bash
# Run all E2E tests
npm run test:e2e

# Run with UI mode
npm run test:e2e:ui

# Run specific file
npx playwright test tests/e2e/setup/auto-navigation.spec.ts

CI Check

The full CI pipeline runs all checks in order:

bash
composer run ci:check
# Runs: lint → format → types → test

Writing PHP Tests

Unit Test Structure

tests/Unit/
├── Traits/          # Tests for shared traits (HasLocalization, HasContact, HasLog)
├── Enums/           # Tests for PHP enums
├── Models/          # Tests for model relationships and scopes
├── Services/        # Tests for service classes
│   ├── Algorithm/
│   ├── Asset/
│   ├── Assistant/
│   └── ...
├── Filament/        # Tests for Filament components
├── Common/          # Tests for common utilities
├── Platform/        # Tests for platform features
├── Mailtrap/        # Tests for email services
└── ExampleTest.php

Feature Test Structure

tests/Feature/
├── Assistant/
├── ContractManagement/
├── Filament/
├── Recruitment/
├── ExampleTest.php
├── RatingFieldTest.php
├── ShieldPermissionTest.php
└── TextareaTest.php

Creating Tests

bash
# Create a feature test
php artisan make:test --pest RecruitmentCandidateTest

# Create a unit test
php artisan make:test --pest --unit Services/Asset/ImageKitServiceTest

Test Example (Pest)

php
use function Pest\Laravel\assertDatabaseHas;

it('creates a candidate with valid data', function () {
    // Arrange
    $data = Candidate::factory()->make()->toArray();

    // Act
    $response = $this->post(route('api.candidates.store'), $data);

    // Assert
    $response->assertCreated();
    assertDatabaseHas('candidates', ['email' => $data['email']]);
});

Test Example (Unit - Service)

php
use App\Services\Otp\Generate;

it('generates a 6-digit code', function () {
    $otp = Generate::handle('test@example.com');

    expect($otp->code)->toMatch('/^\d{6}$/');
    expect($otp->expired_at)->toBeGreaterThan(now());
});

Writing Frontend Tests

Store Tests

typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { myStore } from '../stores/my.stores';

describe('myStore', () => {
    beforeEach(() => { myStore.reset(); });

    it('starts with default state', () => {
        expect(myStore.state.data.items).toEqual([]);
    });

    it('updates on setField', () => {
        myStore.setField('name', 'test');
        expect(myStore.state.data.name).toBe('test');
    });
});

Mocking API Calls

typescript
import { vi } from 'vitest';

vi.mock('../api/my.api', () => ({
    fetchData: vi.fn(),
}));

Key Files

FilePurpose
phpunit.xmlPHPUnit/Pest config — databases, coverage, env vars
vitest.config.tsVitest config for frontend unit tests
playwright.config.tsPlaywright config for E2E tests
tests/Unit/Unit tests (models, services, traits, enums)
tests/Feature/Feature tests (controllers, API endpoints)
tests/e2e/Playwright E2E tests
tests/coverage-reports/html/Coverage report output