container = new DefaultContainer(); $this->dispatcher = new EventDispatcher($this->container); }); test('dispatch with no handlers returns empty array', function () { $event = new TestEvent('test'); $results = $this->dispatcher->dispatch($event); expect($results)->toBeEmpty(); }); test('null event handlers do not cause errors', function () { $dispatcher = new EventDispatcher($this->container, null); $event = new TestEvent('test'); $results = $dispatcher->dispatch($event); expect($results)->toBeEmpty(); }); test('manual handler registration works', function () { $event = new TestEvent('test message'); // Register handler manually using the public method $this->dispatcher->addHandler(TestEvent::class, function ($event) { return 'Manual handler: ' . $event->message; }); $results = $this->dispatcher->dispatch($event); expect($results)->toHaveCount(1) ->and($results[0])->toBe('Manual handler: test message'); }); test('multiple manual handlers are called', function () { $event = new TestEvent('test'); $this->dispatcher->addHandler(TestEvent::class, function ($event) { return 'Handler 1: ' . $event->message; }); $this->dispatcher->addHandler(TestEvent::class, function ($event) { return 'Handler 2: ' . $event->message; }); $results = $this->dispatcher->dispatch($event); expect($results)->toHaveCount(2) ->and($results[0])->toBe('Handler 1: test') ->and($results[1])->toBe('Handler 2: test'); }); test('event inheritance works with manual handlers', function () { $childEvent = new ChildTestEvent('child message'); // Register handler for base event $this->dispatcher->addHandler(BaseTestEvent::class, function ($event) { return 'Base handler: ' . $event->message; }); $results = $this->dispatcher->dispatch($childEvent); expect($results)->toHaveCount(1) ->and($results[0])->toBe('Base handler: child message'); }); test('class-based handlers work with container', function () { $handler = new TestEventHandler(); $this->container->instance(TestEventHandler::class, $handler); $eventHandlers = [ [ 'event_class' => TestEvent::class, 'class' => TestEventHandler::class, 'method' => 'handle', 'attribute_data' => ['stopPropagation' => false], ], ]; $dispatcher = new EventDispatcher($this->container, $eventHandlers); $event = new TestEvent('test message'); $results = $dispatcher->dispatch($event); expect($results)->toHaveCount(1) ->and($results[0])->toBe('Handled: test message') ->and($handler->wasHandled())->toBeTrue(); }); // Test fixtures class TestEvent { public function __construct( public readonly string $message ) { } } class BaseTestEvent { public function __construct( public readonly string $message ) { } } class ChildTestEvent extends BaseTestEvent { } class TestEventHandler { private bool $handled = false; private int $handleCount = 0; #[OnEvent] public function handle(TestEvent $event): string { $this->handled = true; $this->handleCount++; return 'Handled: ' . $event->message; } #[OnEvent] public function handleBase(BaseTestEvent $event): string { return 'Base handled: ' . $event->message; } public function wasHandled(): bool { return $this->handled; } public function getHandleCount(): int { return $this->handleCount; } } class AnotherTestEventHandler { private bool $processed = false; #[OnEvent] public function process(TestEvent $event): string { $this->processed = true; return 'Processed: ' . $event->message; } public function wasProcessed(): bool { return $this->processed; } }