setUpComponentTest(); }); describe('Test Harness - ComponentFactory', function () { it('creates simple counter component', function () { $component = ComponentFactory::counter(initialCount: 5); expect($component->getData()->toArray())->toBe(['count' => 5]); }); it('creates list component', function () { $component = ComponentFactory::list(['item1', 'item2']); $data = $component->getData()->toArray(); expect($data['items'])->toHaveCount(2); }); it('creates custom component with builder', function () { $component = ComponentFactory::make() ->withId('posts:manager') ->withState(['posts' => [], 'count' => 0]) ->withAction('addPost', function (string $title) { $this->state['posts'][] = $title; $this->state['count']++; return ComponentData::fromArray($this->state); }) ->create(); expect($component->getId()->toString())->toBe('posts:manager'); expect($component->getData()->toArray())->toBe(['posts' => [], 'count' => 0]); }); }); describe('Test Harness - Action Execution', function () { it('executes action successfully', function () { $component = ComponentFactory::counter(); $result = $this->callAction($component, 'increment'); expect($result->state->data['count'])->toBe(1); }); it('executes action with parameters', function () { $component = ComponentFactory::list(); $result = $this->callAction($component, 'addItem', ['item' => 'New Task']); expect($result->state->data['items'])->toContain('New Task'); }); it('handles multiple actions in sequence', function () { $component = ComponentFactory::counter(); $result1 = $this->callAction($component, 'increment'); $result2 = $this->callAction($component, 'increment'); $result3 = $this->callAction($component, 'decrement'); // Note: Component state is immutable, so we need to track manually // In real app, component would be re-hydrated with new state expect($result1->state->data['count'])->toBe(1); expect($result2->state->data['count'])->toBe(2); expect($result3->state->data['count'])->toBe(1); }); }); describe('Test Harness - Action Assertions', function () { it('asserts action executes', function () { $component = ComponentFactory::counter(); $result = $this->assertActionExecutes($component, 'increment'); expect($result->state->data['count'])->toBe(1); }); it('asserts action throws exception', function () { $component = ComponentFactory::make() ->withId('error:component') ->withState(['data' => 'test']) ->withAction('fail', function () { throw new \RuntimeException('Expected error'); }) ->create(); $this->assertActionThrows($component, 'fail', \RuntimeException::class); }); }); describe('Test Harness - State Assertions', function () { it('asserts state equals expected values', function () { $component = ComponentFactory::counter(10); $result = $this->callAction($component, 'increment'); $this->assertStateEquals($result, ['count' => 11]); }); it('asserts state has key', function () { $component = ComponentFactory::list(['item1']); $result = $this->callAction($component, 'addItem', ['item' => 'item2']); $this->assertStateHas($result, 'items'); }); it('gets state value', function () { $component = ComponentFactory::counter(5); $result = $this->callAction($component, 'increment'); $count = $this->getStateValue($result, 'count'); expect($count)->toBe(6); }); }); describe('Test Harness - Authorization', function () { it('authenticates user with permissions', function () { $this->actingAs(['posts.edit', 'posts.delete']); expect($this->session->get('user'))->toHaveKey('permissions'); expect($this->session->get('user')['permissions'])->toBe(['posts.edit', 'posts.delete']); }); it('asserts action requires authentication', function () { // Note: Attributes on closures are not supported for magic methods via __call() // For authorization testing, use real component classes instead of ComponentFactory $this->markTestSkipped('Authorization attributes on closures require real component classes'); $component = ComponentFactory::make() ->withId('protected:component') ->withState(['data' => 'secret']) ->withAction( 'protectedAction', #[RequiresPermission('admin.access')] function () { return ComponentData::fromArray($this->state); } ) ->create(); // Without authentication, should throw $this->assertActionRequiresAuth($component, 'protectedAction'); })->skip('Authorization attributes not supported on closures'); it('executes protected action with correct permission', function () { // Note: Same limitation as above - closures don't support attributes for authorization $this->markTestSkipped('Authorization attributes on closures require real component classes'); $component = ComponentFactory::make() ->withId('protected:component') ->withState(['data' => 'secret']) ->withAction( 'protectedAction', #[RequiresPermission('admin.access')] function () { return ComponentData::fromArray($this->state); } ) ->create(); $this->actingAs(['admin.access']); $result = $this->assertActionExecutes($component, 'protectedAction'); expect($result->state->data['data'])->toBe('secret'); })->skip('Authorization attributes not supported on closures'); }); describe('Test Harness - State Validation', function () { it('validates state after action', function () { $component = ComponentFactory::counter(); $result = $this->callAction($component, 'increment'); $this->assertStateValidates($result); }); it('ensures state consistency', function () { $component = ComponentFactory::list(['a', 'b', 'c']); $result = $this->callAction($component, 'removeItem', ['index' => 1]); // State should still be valid array structure $this->assertStateValidates($result); expect($result->state->data['items'])->toHaveCount(2); }); }); describe('Test Harness - Event Assertions', function () { it('asserts no events dispatched by default', function () { $component = ComponentFactory::counter(); $result = $this->callAction($component, 'increment'); $this->assertNoEventsDispatched($result); }); it('asserts event count', function () { $component = ComponentFactory::counter(); $result = $this->callAction($component, 'increment'); $this->assertEventCount($result, 0); }); });