diff --git a/package-lock.json b/package-lock.json index af0005c84..7d0525c60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5863,9 +5863,9 @@ } }, "node_modules/apexcharts": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.47.0.tgz", - "integrity": "sha512-s/fgNCA69b8lJdhI3R7Z+/Df47RPplLyHwuvttecR+aaZ3/Pm6wHYPiAGjqDNbVsMGXhuA9mcOpIYU5ZWeSdeg==", + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.52.0.tgz", + "integrity": "sha512-7dg0ADKs8AA89iYMZMe2sFDG0XK5PfqllKV9N+i3hKHm3vEtdhwz8AlXGm+/b0nJ6jKiaXsqci5LfVxNhtB+dA==", "peer": true, "dependencies": { "@yr/monotone-cubic-spline": "^1.0.3", @@ -5899,13 +5899,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "devOptional": true + "optional": true }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "devOptional": true, + "optional": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -5918,7 +5918,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7047,7 +7047,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "devOptional": true, + "optional": true, "engines": { "node": ">=10" } @@ -7362,7 +7362,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "devOptional": true, + "optional": true, "bin": { "color-support": "bin.js" } @@ -7483,7 +7483,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "devOptional": true + "optional": true }, "node_modules/content-disposition": { "version": "0.5.4", @@ -8202,7 +8202,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "devOptional": true + "optional": true }, "node_modules/depd": { "version": "2.0.0", @@ -10308,7 +10308,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -10320,7 +10320,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, + "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -10332,7 +10332,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "optional": true }, "node_modules/fs-monkey": { "version": "1.0.5", @@ -10397,7 +10397,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "devOptional": true, + "optional": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -10417,13 +10417,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true + "optional": true }, "node_modules/gauge/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, + "optional": true, "engines": { "node": ">=8" } @@ -10432,7 +10432,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, + "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10769,7 +10769,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "devOptional": true + "optional": true }, "node_modules/hash-base": { "version": "3.0.4", @@ -15606,7 +15606,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -15619,7 +15619,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, + "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -15631,7 +15631,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "optional": true }, "node_modules/mkdirp": { "version": "0.5.6", @@ -18685,7 +18685,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "devOptional": true, + "optional": true, "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -21422,7 +21422,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "devOptional": true + "optional": true }, "node_modules/set-function-length": { "version": "1.2.2", @@ -22478,7 +22478,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "devOptional": true, + "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -22495,7 +22495,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, + "optional": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -22507,7 +22507,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "optional": true }, "node_modules/terminal-link": { "version": "2.1.1", @@ -24001,7 +24001,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "devOptional": true, + "optional": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -24010,13 +24010,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true + "optional": true }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, + "optional": true, "engines": { "node": ">=8" } @@ -24025,7 +24025,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, + "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", diff --git a/src/Mutations/invitationMutation.tsx b/src/Mutations/invitationMutation.tsx new file mode 100644 index 000000000..7ba183571 --- /dev/null +++ b/src/Mutations/invitationMutation.tsx @@ -0,0 +1,15 @@ +import { gql } from '@apollo/client'; + +export const SEND_INVITATION = gql` + mutation SendInvitation($invitees: [InviteeInput!]!, $orgToken: String!) { + sendInvitation(invitees: $invitees, orgToken: $orgToken) { + status + invitees { + email + role + } + orgToken + createdAt + } + } +`; diff --git a/src/components/DataPagination.tsx b/src/components/DataPagination.tsx index c70ce33a3..f69e6c7dd 100644 --- a/src/components/DataPagination.tsx +++ b/src/components/DataPagination.tsx @@ -24,26 +24,28 @@ function DataPagination({ -
- {' '} - {' '} -
+
+
+ {' '} + {' '} +
+
Page{' '} @@ -72,7 +74,7 @@ function DataPagination({ /> {' '} + {emailError && ( +

{emailError}

+ )} +
+ +
+ +
+
+

Learn more

+ +
+
+ ); +}; + +export default InviteForm; diff --git a/src/components/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap b/src/components/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap index 82f5a6de5..a51f5eeea 100644 --- a/src/components/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap +++ b/src/components/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap @@ -28,6 +28,7 @@ Array [ >
- add + @@ -1111,60 +1112,64 @@ Array [ colSpan={6} >
- - - + + - + + + +
- - - + + - + + + +
({ + useTranslation: () => ({ t: (key: string) => key }), +})); +jest.mock('../../../components/DataTable', () => { + return function MockDataTable({ data, columns }: any) { + return ( +
+ {data.map((item: any, index: number) => ( +
+ {columns.map((column: any) => ( + + {column.Cell ? column.Cell({ row: { original: item } }) : item[column.accessor]} + + ))} +
+ ))} +
+ ); + }; +}); + +const mocks = [ + { + request: { + query: GET_SESSIONS, + }, + result: { + data: { + getAllSessions: [ + { + id: '1', + Sessionname: 'Test Session', + description: 'Test Description', + platform: 'Test Platform', + duration: '1:00', + organizer: 'Test Organizer', + }, + ], + }, + }, + }, + { + request: { + query: CREATE_SESSION, + variables: { + sessionInput: { + Sessionname: 'New Session', + description: 'New Description', + platform: 'New Platform', + duration: '2:00', + organizer: 'New Organizer', + }, + }, + }, + result: { + data: { + createSession: { + id: '2', + Sessionname: 'New Session', + description: 'New Description', + platform: 'New Platform', + duration: '2:00', + organizer: 'New Organizer', + }, + }, + }, + }, + { + request: { + query: DELETE_SESSION, + variables: { + ID: '1', + }, + }, + result: { + data: { + deleteSession: { + id: '1', + }, + }, + }, + }, + { + request: { + query: EDIT_SESSION, + variables: { + ID: '1', + editSessionInput: { + Sessionname: 'Updated Session', + description: 'Updated Description', + platform: 'Updated Platform', + duration: '3:00', + organizer: 'Updated Organizer', + }, + }, + }, + result: { + data: { + editSession: { + id: '1', + Sessionname: 'Updated Session', + description: 'Updated Description', + platform: 'Updated Platform', + duration: '3:00', + organizer: 'Updated Organizer', + }, + }, + }, + }, +]; + +describe('AdminSission Component', () => { + + it('displays session data in the table', async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByText('Test Session')).toBeInTheDocument(); + expect(screen.getByText('Test Description')).toBeInTheDocument(); + expect(screen.getByText('Test Platform')).toBeInTheDocument(); + expect(screen.getByText('1:00')).toBeInTheDocument(); + expect(screen.getByText('Test Organizer')).toBeInTheDocument(); + }); + }); + + it('opens add session modal when register button is clicked', async () => { + render( + + + + ); + + await waitFor(() => { + fireEvent.click(screen.getByText('register +')); + }); + + expect(screen.getByText('AddSession')).toBeInTheDocument(); + }); + + it('opens delete session modal when delete icon is clicked', async () => { + render( + + + + ); + + await waitFor(() => { + const deleteIcon = screen.getByTestId('deleteIcon'); + fireEvent.click(deleteIcon); + }); + + expect(screen.getByText('DeleteSession')).toBeInTheDocument(); + }); + + it('opens update session modal when update icon is clicked', async () => { + render( + + + + ); + + await waitFor(() => { + const updateIcon = screen.getByTestId('updateIcon'); + fireEvent.click(updateIcon); + }); + + expect(screen.getByText('UpdateSession')).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/src/containers/tests/__snapshots__/MainRoutes.test.tsx.snap b/src/containers/tests/__snapshots__/MainRoutes.test.tsx.snap index 915ea633b..20c057e32 100644 --- a/src/containers/tests/__snapshots__/MainRoutes.test.tsx.snap +++ b/src/containers/tests/__snapshots__/MainRoutes.test.tsx.snap @@ -94,7 +94,7 @@ exports[`Main Routes Should render 1`] = ` > Docs @@ -237,7 +237,7 @@ exports[`Main Routes Should render 1`] = ` className="p-2 w-full mt-2 dark:text-dark-text-fill text-primary" > Docs @@ -368,7 +368,7 @@ exports[`Main Routes Should render 1`] = ` > Docs diff --git a/src/pages/invitationModelComponet.tsx b/src/pages/invitationModelComponet.tsx new file mode 100644 index 000000000..fe9caccbf --- /dev/null +++ b/src/pages/invitationModelComponet.tsx @@ -0,0 +1,22 @@ +// src/components/InvitationModal.tsx +import React, { useState } from 'react'; +import { IoIosCloseCircleOutline } from "react-icons/io"; +import InviteForm from '../components/invitationModel' + +interface InvitationModalProps { + isOpen: boolean; + onClose: () => void; +} + +function InvitationModal({ isOpen, onClose }: InvitationModalProps) { + if (!isOpen) return null; + + return ( +
+ + +
+ ); +}; + +export default InvitationModal; diff --git a/src/pages/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap b/src/pages/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap index 605cb0add..23bca3981 100644 --- a/src/pages/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap +++ b/src/pages/tests/__snapshots__/AdminTraineeDashboard.test.tsx.snap @@ -28,6 +28,7 @@ Array [ >
- add + @@ -1111,60 +1112,64 @@ Array [ colSpan={6} >
- - - + + - + + + +
- - - + + - + + + +
- We are delighted to have you with us. Soon you shall be receiving an email that contains an organization you will be part of. + We are delighted to have you with us. Soon you shall be receiving an email that contains details of a cohort that you will be part of.

{ + return function MockInviteForm({ onClose }: { onClose: () => void }) { + return

Mock Invite Form
; + }; +}); + +describe('InvitationModal', () => { + it('renders nothing when isOpen is false', () => { + render( {}} />); + expect(screen.queryByTestId('invite-form')).not.toBeInTheDocument(); + }); + + it('renders the InviteForm when isOpen is true', () => { + render( {}} />); + expect(screen.getByTestId('invite-form')).toBeInTheDocument(); + }); + + it('applies the correct styles to the modal wrapper', () => { + render( {}} />); + const modalWrapper = screen.getByTestId('invite-form').parentElement; + expect(modalWrapper).toHaveClass('fixed inset-0 flex items-center justify-center bg-black bg-opacity-30 z-50'); + }); +}); \ No newline at end of file diff --git a/src/tests/__snapshots__/App.test.tsx.snap b/src/tests/__snapshots__/App.test.tsx.snap index c3acbad99..f35f3f243 100644 --- a/src/tests/__snapshots__/App.test.tsx.snap +++ b/src/tests/__snapshots__/App.test.tsx.snap @@ -98,7 +98,7 @@ exports[`App test Should render app 1`] = ` >
Docs diff --git a/src/tests/__snapshots__/Siderbar.test.tsx.snap b/src/tests/__snapshots__/Siderbar.test.tsx.snap index 2d6e4f6ba..b02107203 100644 --- a/src/tests/__snapshots__/Siderbar.test.tsx.snap +++ b/src/tests/__snapshots__/Siderbar.test.tsx.snap @@ -95,7 +95,7 @@ exports[` Renders Home 1`] = ` > Docs diff --git a/src/tests/invitation.test.tsx b/src/tests/invitation.test.tsx new file mode 100644 index 000000000..6d89970a3 --- /dev/null +++ b/src/tests/invitation.test.tsx @@ -0,0 +1,175 @@ +import React from 'react'; +import "@testing-library/jest-dom"; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { MockedProvider } from '@apollo/client/testing'; +import { toast } from 'react-toastify'; +import InviteForm from '../components/invitationModel'; +import { SEND_INVITATION } from '../Mutations/invitationMutation'; + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + }, +})); + +const localStorageMock = (() => { + let store: { [key: string]: string } = {}; + return { + getItem: jest.fn((key: string) => store[key] || null), + setItem: jest.fn((key: string, value: string) => { + store[key] = value; + }), + clear: jest.fn(() => { + store = {}; + }), + }; +})(); + + +Object.defineProperty(global, 'localStorage', { value: localStorageMock }); + +const mocks = [ + { + request: { + query: SEND_INVITATION, + variables: { + invitees: [{ email: 'test@example.com', role: 'admin' }], + orgToken: 'mockToken', + }, + }, + result: { + data: { + sendInvitation: { + success: true, + }, + }, + }, + }, +]; + +describe('InviteForm', () => { + beforeEach(() => { + jest.clearAllMocks(); + (localStorage.getItem as jest.Mock).mockReturnValue('mockToken'); + }); + + it('renders correctly', () => { + render( + + {}} /> + + ); + + expect(screen.getByText('Invite users')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Email address')).toBeInTheDocument(); + expect(screen.getByText('Role')).toBeInTheDocument(); + expect(screen.getByText('Invite')).toBeInTheDocument(); + }); + + it('validates email input', async () => { + render( + + {}} /> + + ); + + const emailInput = screen.getByPlaceholderText('Email address'); + const submitButton = screen.getByText('Invite'); + + fireEvent.change(emailInput, { target: { value: 'invalid-email' } }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText('Please enter a valid email address.')).toBeInTheDocument(); + }); + }); + + it('handles role selection', async () => { + render( + + {}} /> + + ); + + const roleButton = screen.getByText('Role'); + fireEvent.click(roleButton); + + const adminOption = screen.getByText('Admin'); + fireEvent.click(adminOption); + + expect(screen.queryByText('Admin')).not.toBeInTheDocument(); + expect(roleButton).toHaveTextContent('admin'); + }); + + it('submits the form successfully', async () => { + render( + + {}} /> + + ); + + const emailInput = screen.getByPlaceholderText('Email address'); + const roleButton = screen.getByText('Role'); + const submitButton = screen.getByText('Invite'); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.click(roleButton); + fireEvent.click(screen.getByText('Admin')); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(toast.success).toHaveBeenCalledWith('Invitation sent successfully!'); + }); + }); + + it('handles invitation error', async () => { + const errorMock = { + ...mocks[0], + error: new Error('Invitation failed'), + }; + + render( + + {}} /> + + ); + + const emailInput = screen.getByPlaceholderText('Email address'); + const roleButton = screen.getByText('Role'); + const submitButton = screen.getByText('Invite'); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.click(roleButton); + fireEvent.click(screen.getByText('Admin')); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalledWith('Error sending invitation: Invitation failed'); + }); + }); + + + it('displays loading state when submitting', async () => { + render( + + {}} /> + + ); + + const emailInput = screen.getByPlaceholderText('Email address'); + const roleButton = screen.getByText('Role'); + const submitButton = screen.getByText('Invite'); + + fireEvent.change(emailInput, { target: { value: 'test@example.com' } }); + fireEvent.click(roleButton); + fireEvent.click(screen.getByText('Admin')); + fireEvent.click(submitButton); + + expect(submitButton).toBeDisabled(); + + await waitFor(() => { + expect(submitButton).not.toBeDisabled(); + }); + }); +});