shouldReceive('serialize')->andReturnUsing(fn($data) => serialize($data)); $serializer->shouldReceive('unserialize')->andReturnUsing(fn($data) => unserialize($data)); $cache = new GeneralCache($nullCacheDriver, $serializer); $templateLoader = new TemplateLoader( pathProvider: $pathProvider, cache: $cache, discoveryRegistry: null, templates: [], templatePath: '/src/Framework/View/templates', cacheEnabled: false ); // Create TemplateProcessor $templateProcessor = new TemplateProcessor( astTransformers: [], stringProcessors: [], container: $container, chainOptimizer: null, compiledTemplateCache: null, performanceTracker: null ); // Create real Session instance $session = Session::fromArray( SessionId::fromString(str_repeat('a', 32)), new SystemClock(), new CsrfTokenGenerator(new SecureRandomGenerator()), [] ); // Create LiveComponentRenderer $liveComponentRenderer = new LiveComponentRenderer( templateLoader: $templateLoader, templateProcessor: $templateProcessor, session: $session ); // Create real instances for final classes with minimal dependencies $cacheKeyBuilder = new \App\Framework\LiveComponents\Caching\CacheKeyBuilder(); $cacheManager = new ComponentCacheManager( cache: $cache, cacheKeyBuilder: $cacheKeyBuilder ); $eventDispatcher = new \App\Framework\LiveComponents\ComponentEventDispatcher(); $handler = new LiveComponentHandler( eventDispatcher: $eventDispatcher, session: $session ); $metadataCompiler = new \App\Framework\LiveComponents\Performance\ComponentMetadataCompiler(); $metadataCache = new ComponentMetadataCache( cache: $cache, compiler: $metadataCompiler ); $highResClock = new \App\Framework\DateTime\HighResolutionClock(); $memoryMonitor = new \App\Framework\Performance\MemoryMonitor(); $performanceTracker = new NestedPerformanceTracker( clock: new SystemClock(), highResClock: $highResClock, memoryMonitor: $memoryMonitor ); // Create real ComponentRegistry $componentRegistry = new ComponentRegistry( container: $container, discoveryRegistry: $discoveryRegistry, renderer: $liveComponentRenderer, cacheManager: $cacheManager, handler: $handler, metadataCache: $metadataCache, performanceTracker: $performanceTracker ); $this->pageRenderer = new AdminPageRenderer($componentRegistry); $this->formFactory = Mockery::mock(AdminFormFactory::class); $this->tableFactory = Mockery::mock(AdminTableFactory::class); $apiHandler = new AdminApiHandler(); $adapterFactory = new RepositoryAdapterFactory(); $this->service = new CrudService( $this->pageRenderer, $this->formFactory, $this->tableFactory, $apiHandler, $adapterFactory ); $this->config = CrudConfig::forResource( resource: 'campaigns', resourceName: 'Campaign', title: 'Campaigns' )->withColumns([ ['field' => 'name', 'label' => 'Name'], ['field' => 'status', 'label' => 'Status'], ]); }); afterEach(function () { Mockery::close(); }); describe('CrudService', function () { describe('renderIndex', function () { it('renders index view with items using AdminPageRenderer', function () { $items = [ ['id' => '1', 'name' => 'Campaign 1', 'status' => 'active'], ['id' => '2', 'name' => 'Campaign 2', 'status' => 'draft'], ]; $request = Mockery::mock(Request::class); $request->shouldReceive('uri')->andReturn('/admin/campaigns'); $mockTable = Mockery::mock(Table::class); $mockTable->shouldReceive('render')->andReturn('...
'); $this->tableFactory->shouldReceive('create') ->once() ->andReturn($mockTable); $result = $this->service->renderIndex( $this->config, $items, $request ); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->template)->toBe('admin-index'); }); it('includes create action when canCreate is true', function () { $items = []; $request = Mockery::mock(Request::class); $request->shouldReceive('uri')->andReturn('/admin/campaigns'); $mockTable = Mockery::mock(Table::class); $mockTable->shouldReceive('render')->andReturn(''); $this->tableFactory->shouldReceive('create')->andReturn($mockTable); $result = $this->service->renderIndex($this->config, $items, $request); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->data['actions'])->toBeArray(); expect($result->data['actions'][0]['label'])->toBe('Create Campaign'); }); }); describe('renderCreate', function () { it('renders create form using AdminPageRenderer', function () { $formFields = [ 'name' => ['type' => 'text', 'label' => 'Name'], ]; $mockForm = Mockery::mock(); $mockForm->shouldReceive('build')->andReturn('
...
'); $mockForm->shouldReceive('getId')->andReturn('campaign-form'); $this->formFactory->shouldReceive('create') ->once() ->andReturn($mockForm); $result = $this->service->renderCreate( $this->config, $formFields ); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->template)->toBe('admin-form'); expect($result->data['title'])->toBe('Create Campaign'); }); it('renders create form with help text as subtitle', function () { $formFields = []; $helpText = 'Fill in the campaign details carefully.'; $mockForm = Mockery::mock(); $mockForm->shouldReceive('build')->andReturn(''); $mockForm->shouldReceive('getId')->andReturn('form-id'); $this->formFactory->shouldReceive('create')->andReturn($mockForm); $result = $this->service->renderCreate( $this->config, $formFields, null, $helpText ); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->data['subtitle'])->toBe($helpText); }); }); describe('renderEdit', function () { it('renders edit form using AdminPageRenderer', function () { $formFields = [ 'name' => ['type' => 'text', 'label' => 'Name'], ]; $itemData = [ 'id' => '123', 'name' => 'Test Campaign', 'status' => 'active', ]; $metadata = [ 'id' => '123', 'createdAt' => '2024-01-01 10:00:00', 'updatedAt' => '2024-01-02 15:30:00', ]; $mockForm = Mockery::mock(); $mockForm->shouldReceive('build')->andReturn('
...
'); $mockForm->shouldReceive('getId')->andReturn('edit-form'); $this->formFactory->shouldReceive('create') ->once() ->andReturn($mockForm); $result = $this->service->renderEdit( $this->config, '123', $formFields, $itemData, $metadata ); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->template)->toBe('admin-form'); expect($result->data['title'])->toBe('Edit Campaign'); }); }); describe('renderShow', function () { it('renders show view using AdminPageRenderer', function () { $fields = [ ['label' => 'Name', 'value' => 'Test Campaign', 'type' => 'text'], ['label' => 'Status', 'value' => 'Active', 'type' => 'badge', 'color' => 'success'], ]; $metadata = [ 'id' => '123', 'createdAt' => '2024-01-01', ]; $result = $this->service->renderShow( $this->config, '123', $fields, $metadata ); expect($result)->toBeInstanceOf(ViewResult::class); expect($result->template)->toBe('admin-show'); expect($result->data['fields'])->toBe($fields); expect($result->data['metadata'])->toBe($metadata); expect($result->data['editUrl'])->toBe('/admin/campaigns/edit/123'); }); }); describe('redirectAfterCreate', function () { it('redirects to index after successful create', function () { $request = Mockery::mock(Request::class); $request->parsedBody = Mockery::mock(); $request->parsedBody->shouldReceive('get') ->with('action') ->andReturn(null); $result = $this->service->redirectAfterCreate( $this->config, $request, '123' ); expect($result)->toBeInstanceOf(Redirect::class); expect($result->url)->toBe('/admin/campaigns'); }); it('redirects to create form when save-and-continue is requested', function () { $request = Mockery::mock(Request::class); $request->parsedBody = Mockery::mock(); $request->parsedBody->shouldReceive('get') ->with('action') ->andReturn('save-and-continue'); $result = $this->service->redirectAfterCreate( $this->config, $request, '123' ); expect($result->url)->toBe('/admin/campaigns/create'); }); }); describe('redirectAfterUpdate', function () { it('redirects to index after successful update', function () { $request = Mockery::mock(Request::class); $request->parsedBody = Mockery::mock(); $request->parsedBody->shouldReceive('get') ->with('action') ->andReturn(null); $result = $this->service->redirectAfterUpdate( $this->config, $request, '123' ); expect($result->url)->toBe('/admin/campaigns'); }); it('redirects to show view when save-and-view is requested', function () { $request = Mockery::mock(Request::class); $request->parsedBody = Mockery::mock(); $request->parsedBody->shouldReceive('get') ->with('action') ->andReturn('save-and-view'); $result = $this->service->redirectAfterUpdate( $this->config, $request, '123' ); expect($result->url)->toBe('/admin/campaigns/view/123'); }); }); describe('redirectAfterDelete', function () { it('redirects to index after successful delete', function () { $result = $this->service->redirectAfterDelete($this->config); expect($result)->toBeInstanceOf(Redirect::class); expect($result->url)->toBe('/admin/campaigns'); }); }); describe('redirectWithError', function () { it('redirects back with error message', function () { $result = $this->service->redirectWithError('Something went wrong'); expect($result)->toBeInstanceOf(Redirect::class); }); it('redirects with error message and input data', function () { $inputData = ['name' => 'Test', 'email' => 'test@example.com']; $result = $this->service->redirectWithError( 'Validation failed', $inputData ); expect($result)->toBeInstanceOf(Redirect::class); }); }); });