diff --git a/src/Mealz/MealBundle/Controller/MealGuestController.php b/src/Mealz/MealBundle/Controller/MealGuestController.php index 886145f10..b7ea12cb4 100644 --- a/src/Mealz/MealBundle/Controller/MealGuestController.php +++ b/src/Mealz/MealBundle/Controller/MealGuestController.php @@ -104,7 +104,6 @@ public function getEventInvitationData( return new JsonResponse($guestData, 200); } - // TODO: check for existing guest public function joinEventAsGuest( string $invitationId, Request $request, diff --git a/src/Mealz/MealBundle/Tests/Controller/AbstractControllerTestCase.php b/src/Mealz/MealBundle/Tests/Controller/AbstractControllerTestCase.php index fe58887c8..fbf972e82 100644 --- a/src/Mealz/MealBundle/Tests/Controller/AbstractControllerTestCase.php +++ b/src/Mealz/MealBundle/Tests/Controller/AbstractControllerTestCase.php @@ -10,6 +10,7 @@ use App\Mealz\MealBundle\Entity\EventParticipation; use App\Mealz\MealBundle\Entity\Meal; use App\Mealz\MealBundle\Entity\Participant; +use App\Mealz\MealBundle\Repository\DayRepository; use App\Mealz\MealBundle\Repository\MealRepositoryInterface; use App\Mealz\MealBundle\Tests\AbstractDatabaseTestCase; use App\Mealz\UserBundle\Entity\Profile; @@ -167,6 +168,23 @@ protected function createEventParticipation(Day $day, Event $event): EventPartic return $eventParticipation; } + protected function createFutureEvent(): EventParticipation + { + $newEvent = $this->createEvent(); + $this->persistAndFlushAll([$newEvent]); + + $dayRepo = self::$container->get(DayRepository::class); + + $criteria = new \Doctrine\Common\Collections\Criteria(); + $criteria->where(\Doctrine\Common\Collections\Criteria::expr()->gt('lockParticipationDateTime', new DateTime())); + + /** @var Day $day */ + $day = $dayRepo->matching($criteria)->get(0); + $this->assertNotNull($day); + + return $this->createEventParticipation($day, $newEvent); + } + /** * Helper method to get the recent meal. */ diff --git a/src/Mealz/MealBundle/Tests/Controller/MealGuestControllerTest.php b/src/Mealz/MealBundle/Tests/Controller/MealGuestControllerTest.php new file mode 100644 index 000000000..7f75a69c0 --- /dev/null +++ b/src/Mealz/MealBundle/Tests/Controller/MealGuestControllerTest.php @@ -0,0 +1,140 @@ +clearAllTables(); + $this->loadFixtures([ + new LoadWeeks(), + new LoadDays(), + new LoadCategories(), + new LoadDishes(), + new LoadDishVariations(), + new LoadEvents(), + new LoadMeals(), + new LoadRoles(), + new LoadUsers(self::$container->get('security.user_password_encoder.generic')), + ]); + + $this->loginAs(self::USER_KITCHEN_STAFF); + } + + public function testnewGuestEventInvitation(): void + { + $guestInvitationRepo = self::$container->get(GuestInvitationRepository::class); + $eventParticipation = $this->createFutureEvent(); + $url = '/event/invitation/' . $eventParticipation->getDay()->getId(); + + $this->client->request('GET', $url); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $guestLink = json_decode($this->client->getResponse()->getContent())->url; + $prefix = 'http://localhost/guest/event/'; + $invitationId = str_ireplace($prefix, '', $guestLink); + + $invitation = $guestInvitationRepo->find($invitationId); + $this->assertNotNull($invitation); + $this->assertEquals( + $eventParticipation->getEvent()->getTitle(), + $invitation->getDay()->getEvent()->getEvent()->getTitle() + ); + } + + public function testGetEventInvitationData(): void + { + $guestInvitationRepo = self::$container->get(GuestInvitationRepository::class); + $eventParticipation = $this->createFutureEvent(); + $profile = $this->createProfile('Max', 'Mustermann' . time()); + $this->persistAndFlushAll([$profile]); + $eventInvitation = $guestInvitationRepo->findOrCreateInvitation($profile, $eventParticipation->getDay()); + + $this->client->request('GET', '/api/event/invitation/' . $eventInvitation->getId()); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent()); + $this->assertEquals( + json_encode($eventParticipation->getDay()->getDateTime()), + json_encode($content->date) + ); + $this->assertEquals( + json_encode($eventParticipation->getDay()->getLockParticipationDateTime()), + json_encode($content->lockDate) + ); + $this->assertEquals( + $eventParticipation->getDay()->getEvent()->getEvent()->getTitle(), + $content->event + ); + } + + public function testJoinEventAsGuest(): void + { + $guestInvitationRepo = self::$container->get(GuestInvitationRepository::class); + $eventParticipation = $this->createFutureEvent(); + $profile = $this->createProfile('Max', 'Mustermann' . time()); + $this->persistAndFlushAll([$profile]); + $eventInvitation = $guestInvitationRepo->findOrCreateInvitation($profile, $eventParticipation->getDay()); + + // with company + $this->client->request( + 'POST', + '/api/event/invitation/' . $eventInvitation->getId(), + [], + [], + [], + json_encode([ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'company' => 'District 9', + ]) + ); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + // without company + $this->client->request( + 'POST', + '/api/event/invitation/' . $eventInvitation->getId(), + [], + [], + [], + json_encode([ + 'firstName' => 'Jane', + 'lastName' => 'Doe', + 'company' => null, + ]) + ); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + // without firstName + $this->client->request( + 'POST', + '/api/event/invitation/' . $eventInvitation->getId(), + [], + [], + [], + json_encode([ + 'firstName' => null, + 'lastName' => 'Doe', + 'company' => 'District 9', + ]) + ); + $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); + } +} diff --git a/src/Resources/src/locales/de.json b/src/Resources/src/locales/de.json index 2fc8a5f8d..ee1d739c8 100644 --- a/src/Resources/src/locales/de.json +++ b/src/Resources/src/locales/de.json @@ -164,7 +164,8 @@ "701": "Es wurden noch nicht alle Details zu dem Event eingefügt", "801": "Keine Berechtigung zum beitreten oder verlassen", "802": "Dem Event konnte nicht beigetreten werden", - "803": "Das Event konnte nicht verlassen werden" + "803": "Das Event konnte nicht verlassen werden", + "903": "Sie haben sich schon angemeldet. Bei Fragen wenden Sie sich bitte an Ihren Ansprechpartner." }, "success": { "menu": { @@ -205,7 +206,7 @@ "updated": "Der Zeitslot wurde erfolgreich bearbeitet." }, "guest": { - "joined": "Sie nehmen an der Veranstaltung teil. Wir freuen uns Sie dabei zu haben." + "joined": "Wir freuen uns Sie dabei zu haben." } } }, @@ -237,7 +238,7 @@ "submit": "An Event teilnehmen", "joined": "Sie nehmen am %eventDate% an \"%EventTitle%\" teil. Wir freuen uns Sie dabei zu haben.", "title": "Veranstaltung bei AOE", - "description": "Hiermit laden wir Sie zu der Veranstaltung \"%EventTitle%\" bei uns ein. Damit wir rechtzeitig mit Ihnen planen können tragen Sie sich bitte bis spätestens %lockDate% Uhr für das Event ein.\n\nWir freuen uns auf Sie!" + "description": "Hiermit laden wir Sie zu der Veranstaltung \"%EventTitle%\" ein. Damit wir mit Ihnen planen können tragen Sie sich bitte bis spätestens %lockDate% Uhr für das Event ein.\n\nWir freuen uns auf Sie!" } }, "header": { diff --git a/src/Resources/src/locales/en.json b/src/Resources/src/locales/en.json index 667315066..eecd7701b 100644 --- a/src/Resources/src/locales/en.json +++ b/src/Resources/src/locales/en.json @@ -164,7 +164,8 @@ "701": "Event creation parameters are missing", "801": "No permission to enter or leave the event", "802": "Couldn't join the event", - "803": "Couldn't leave the event" + "803": "Couldn't leave the event", + "903": "You are already registered. If you have any questions please message your contact person." }, "success": { "menu": { @@ -205,7 +206,7 @@ "updated": "The timeslot was successfully updated." }, "guest": { - "joined": "You are participating in the Event. We are looking forward to meeting you." + "joined": "We are looking forward to meeting you." } } }, diff --git a/src/Resources/src/views/GuestEvent.vue b/src/Resources/src/views/GuestEvent.vue index c5cd1af60..270a09f41 100644 --- a/src/Resources/src/views/GuestEvent.vue +++ b/src/Resources/src/views/GuestEvent.vue @@ -107,7 +107,7 @@ async function handleSubmit() { console.log('Trying to join'); const { error, response } = await postJoinEventGuest(props.hash, formData.value); - if (error.value === true && isMessage(response.value)) { + if (error.value === true && isMessage(response.value) && response.value.message.includes('already joined')) { sendFlashMessage({ type: FlashMessageType.ERROR, message: response.value.message diff --git a/tests/e2e/cypress/e2e/GuestEventInvitation.cy.ts b/tests/e2e/cypress/e2e/GuestEventInvitation.cy.ts new file mode 100644 index 000000000..95d7ae7e7 --- /dev/null +++ b/tests/e2e/cypress/e2e/GuestEventInvitation.cy.ts @@ -0,0 +1,124 @@ +describe('Test GuestEventInvitation', () => { + beforeEach(() => { + cy.resetDB(); + cy.loginAs('kochomi'); + }); + + it('should copy a invitation link to the clipboard then visit the link and register for an event', () => { + cy.intercept('GET', '**/api/weeks').as('getWeeks'); + cy.intercept('GET', '**/api/meals/count').as('getDishesCount'); + cy.intercept('GET', '**/api/categories').as('getCategories'); + cy.intercept('GET', '**/api/dishes').as('getDishes'); + cy.intercept('GET', '**/api/events').as('getEvents'); + cy.intercept('GET', '**/api/dashboard').as('getDashboard'); + cy.intercept('GET', '**/api/participations/event/**').as('getParticipants'); + cy.intercept('GET', '**/event/invitation/**').as('getEventInvitation'); + cy.intercept('POST', '**/api/event/invitation/**').as('postEventInvitation'); + + cy.visitMeals(); + cy.get('span > a').contains('Mahlzeiten').click(); + cy.wait(['@getWeeks']); + + // Go to the next week + cy.get('h4').eq(1).contains('Woche').click(); + cy.wait(['@getDishesCount', '@getCategories', '@getDishes']); + + // add an event on monday + cy.get('input') + .eq(2) + .click() + .parent().parent() + .find('li').contains('Afterwork') + .click(); + + cy.get('h2').should('contain', 'Woche').click(); + + // Save + cy.contains('input', 'Speichern').click(); + + cy.get('[data-cy="msgClose"]').click(); + + // find the saved event + cy.get('input') + .eq(2) + .should('have.value', 'Afterwork'); + + // go to dashboard + cy.get('header > nav > div > a > svg').click(); + cy.wait(['@getDashboard', '@getEvents']); + + // confirm event is not joined yet + cy.get('h2') + .contains('Nächste Woche') + .parent() + .parent() + .find('span') + .contains('Montag') + .parent() + .parent() + .find('span') + .contains('Afterwork') + .parent() + .find('svg') + .eq(0) + .click(); + + cy.wait('@getEventInvitation'); + + cy.contains('span', 'In die Zwischenablage kopiert!') + .parent() + .parent() + .children() + .eq(0) + .then($el => { + cy.log('Link: ' + $el[0].innerText); + const link = $el[0].innerText; + expect(link).to.match(/^(http|https):\/\/(meals.test|localhost)\/guest\/event\/\S*$/); + + cy.clearAllSessionStorage(); + cy.visit(link); + + cy.get('input[placeholder="Vorname"]') + .click() + .type('John'); + + cy.get('input[placeholder="Nachname"]') + .click() + .type('Doe'); + + cy.get('input[placeholder="Betrieb"]') + .click() + .type('District 17') + + cy.contains('input', 'An Event teilnehmen').click(); + + cy.wait('@postEventInvitation'); + cy.get('[data-cy=msgClose]').click(); + + cy.visitMeals(); + cy.loginAs('kochomi'); + + cy.get('h2') + .contains('Nächste Woche') + .parent() + .parent() + .find('span') + .contains('Montag') + .parent() + .parent() + .find('span') + .contains('Afterwork') + .parent() + .find('svg') + .eq(1) + .click(); + + cy.get('span') + .contains('Teilnahmen "Afterwork"') + .parent() + .parent() + .find('ul') + .contains('Doe, John (District 17)'); + }); + }); +}); \ No newline at end of file