From 7c84eefe785240ee752994312804bbb28a42a472 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sun, 21 Jun 2026 21:44:06 +0300 Subject: [PATCH] test child creation --- .../tests/Feature/ElementsEndpointTest.php | 87 +++++++++++++++ .../UseCases/CreateChildElementTest.php | 102 ++++++++++++++++++ .../cypress/e2e/admin-element.cy.ts | 24 +++++ 3 files changed, 213 insertions(+) create mode 100644 backend/tests/Unit/Element/UseCases/CreateChildElementTest.php diff --git a/backend/tests/Feature/ElementsEndpointTest.php b/backend/tests/Feature/ElementsEndpointTest.php index cd17cd2..01bd6a7 100644 --- a/backend/tests/Feature/ElementsEndpointTest.php +++ b/backend/tests/Feature/ElementsEndpointTest.php @@ -142,6 +142,93 @@ class ElementsEndpointTest extends TestCase ]); } + public function testCreateChildRequiresAuthentication(): void + { + $setRepository = app(SetRepository::class); + $elementRepository = app(ElementRepository::class); + $set = $this->createSet($setRepository); + $parentElement = $this->createElement( + $elementRepository, + $set, + 'Baderech HaAvodah', + 'A structured path for growth', + null, + '

A structured path for growth

', + null, + null, + null, + null, + ); + + $response = $this->postJson( + "/api/elements/{$parentElement->getId()}/children", + [ + 'title' => 'New child', + ], + ); + + $response->assertUnauthorized(); + $response->assertExactJson([ + 'error' => 'unauthenticated', + ]); + } + + public function testAuthenticatedCreateChildReturnsElementPayload(): void + { + $setRepository = app(SetRepository::class); + $elementRepository = app(ElementRepository::class); + $set = $this->createSet($setRepository); + $parentElement = $this->createElement( + $elementRepository, + $set, + 'Baderech HaAvodah', + 'A structured path for growth', + null, + '

A structured path for growth

', + null, + null, + null, + null, + ); + $this->createSession('valid-token'); + + $response = $this->withCredentials() + ->withUnencryptedCookie('auth_token', 'valid-token') + ->postJson( + "/api/elements/{$parentElement->getId()}/children", + [ + 'title' => 'New admin child', + ], + ); + + $response->assertCreated(); + $body = $response->json(); + $childElementId = $body['element']['id']; + $response->assertExactJson([ + 'element' => [ + 'id' => $childElementId, + 'title' => 'New admin child', + 'description' => '', + 'iconImageUrl' => null, + 'richText' => '', + 'shortPdfPath' => null, + 'longPdfPath' => null, + 'youtubeUrl' => null, + ], + ]); + + $createdElement = $elementRepository->find($childElementId); + $this->assertNotNull($createdElement); + $this->assertSame( + $parentElement->getId(), + $createdElement->getParentElement()->getId(), + ); + $this->assertSame( + $parentElement->getSet()->getId(), + $createdElement->getSet()->getId(), + ); + } + public function testAuthenticatedUpdateReturnsElementPayload(): void { $sampleYoutubeUrl = 'https://www.youtube.com/watch?v=' diff --git a/backend/tests/Unit/Element/UseCases/CreateChildElementTest.php b/backend/tests/Unit/Element/UseCases/CreateChildElementTest.php new file mode 100644 index 0000000..85819c5 --- /dev/null +++ b/backend/tests/Unit/Element/UseCases/CreateChildElementTest.php @@ -0,0 +1,102 @@ +elementRepository = new FakeElementRepository(); + $this->createChildElement = new CreateChildElement( + $this->elementRepository, + ); + } + + public function testCreatesChildElementFromParentSet(): void + { + $parentElement = $this->createParentElement(); + + $childElement = $this->createChildElement->execute( + new CreateChildElementRequest( + parentElementId: $parentElement->getId(), + title: 'Admin child', + ) + ); + + $this->assertSame('Admin child', $childElement->getTitle()); + $this->assertSame('', $childElement->getDescription()); + $this->assertNull($childElement->getIconImageUrl()); + $this->assertSame('', $childElement->getRichText()); + $this->assertNull($childElement->getShortPdfPath()); + $this->assertNull($childElement->getLongPdfPath()); + $this->assertNull($childElement->getYoutubeUrl()); + $this->assertSame( + $parentElement->getSet()->getId(), + $childElement->getSet()->getId(), + ); + $this->assertSame( + $parentElement->getId(), + $childElement->getParentElement()->getId(), + ); + } + + public function testThrowsWhenParentElementIsMissing(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage('Parent element not found'); + + $this->createChildElement->execute(new CreateChildElementRequest( + parentElementId: 999, + title: 'Missing parent child', + )); + } + + public function testThrowsWhenTitleIsMissing(): void + { + $parentElement = $this->createParentElement(); + + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('title is required'); + + $this->createChildElement->execute(new CreateChildElementRequest( + parentElementId: $parentElement->getId(), + title: '', + )); + } + + private function createParentElement(): Element + { + $set = new DomainSet( + id: 1, + name: 'Baderech', + description: 'Baderech description', + iconImageUrl: '/assets/baderech-icon.png', + ); + + return $this->elementRepository->create(new CreateElementDto( + set: $set, + title: 'Parent', + description: 'Parent description', + iconImageUrl: null, + richText: '', + shortPdfPath: null, + longPdfPath: null, + youtubeUrl: null, + parentElement: null, + )); + } +} diff --git a/frontend/rabbi_gerzi/cypress/e2e/admin-element.cy.ts b/frontend/rabbi_gerzi/cypress/e2e/admin-element.cy.ts index f172d92..abf45e1 100644 --- a/frontend/rabbi_gerzi/cypress/e2e/admin-element.cy.ts +++ b/frontend/rabbi_gerzi/cypress/e2e/admin-element.cy.ts @@ -139,6 +139,30 @@ describe('admin element editing', () => { cy.get('[data-cy="element-rich-text"]').should('not.exist') }) + it('adds a child element and opens it for editing', () => { + cy.resetDb() + loginAsAdmin() + cy.visit('/admin/element/1') + cy.intercept('POST', /\/api\/elements\/1\/children$/) + .as('createChildElement') + + cy.get('[data-cy="admin-child-title"]').type('Admin added child') + cy.get('[data-cy="admin-add-child"]').click() + cy.wait('@createChildElement') + + cy.location('pathname').should('match', /^\/admin\/element\/\d+$/) + cy.get('[data-cy="admin-element-title"]') + .should('have.value', 'Admin added child') + cy.contains('header.site-header a', 'View Element').click() + cy.contains('h1', 'Admin added child').should('be.visible') + + cy.visit('/element/1') + cy.get('[data-cy="child-element-list"]') + .should('contain.text', 'Admin added child') + + cy.resetDb() + }) + it('uploads icon and pdf files immediately through the UI', () => { cy.resetDb() loginAsAdmin()