Digital Signature for Filament GitHub

#Testing a signable document

SignableDocumentContract is a trait with the checks every signable document should pass. Use it in a test class for each document. Give it a subject that routes, and optionally one that doesn't.

PHP
namespace Tests\Feature\Signatures;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Kukux\DigitalSignature\Testing\SignableDocumentContract;
use Tests\TestCase;

class DtrDocumentContractTest extends TestCase
{
    use RefreshDatabase, SignableDocumentContract;

    protected function documentKey(): string
    {
        return 'dtr';
    }

    /** Everyone named, each with a login and a registered signature. */
    protected function routableSubject(): array
    {
        $employee = Employee::factory()->withUser()->withSignature()->hasInCharge()->create();

        return [$employee, ['month' => '2026-09-01']];
    }

    /** Optional: something routing should refuse. */
    protected function unroutableSubject(): ?array
    {
        return [Employee::factory()->withUser()->withSignature()->create(), ['month' => '2026-09-01']];
    }
}

Your test class gets:

TestChecks
test_the_sample_renders_to_a_pdfthe template's sample renders
test_every_required_slot_has_somewhere_to_goif the view uses data-signature-slot markers, every required slot is marked; otherwise each has a default or designer placement
test_slot_signing_orders_are_uniqueno two slots share an order
test_it_routes_and_a_second_route_reports_already_routedit routes, and routing again finds the same session
test_a_refusal_leaves_no_record_behinda refused route rolls back the record it opened (skipped without unroutableSubject())
test_once_routed_the_stored_document_is_served_and_nothing_is_rendered_againafter routing, the template is swapped for one that fails on any render; the document of record must still be served, and must match its hash

Rendering is real, so your app's PDF stack (DomPDF, fonts) has to be installed. Fake the signature disk in setUp() if your suite doesn't already (Storage::fake('local')).


#Testing routing itself

Assert on reason(), never on the wording, which an app can change without touching code:

PHP
use Kukux\DigitalSignature\Routing\RoutingResult;
use Kukux\DigitalSignature\Services\DocumentRouter;

$result = app(DocumentRouter::class)->route('dtr', $employee, ['month' => '2026-09-01']);

$this->assertSame(RoutingResult::MISSING_SIGNATORIES, $result->reason());
$this->assertSame(0, Dtr::count());

Guards are built by the container, so swap a dependency with $this->app->instance(TimeLogRepository::class, $fake).


#Signing in tests without certificates

Signing a real PDF needs a CA, user certificates and TCPDF. To test what happens around signing (states, history, notifications), stub only the embedding step of SignatureManager. The package's own suite does this in stubSignatureEmbedding(), in tests/Pest.php.