diff --git a/src/app/(both)/layout.tsx b/src/app/(both)/layout.tsx index 74144bd..8f96035 100644 --- a/src/app/(both)/layout.tsx +++ b/src/app/(both)/layout.tsx @@ -1,3 +1,4 @@ +import { ClientSafeProvider, getProviders } from 'next-auth/react' import { PropsWithChildren, ReactElement } from 'react' import { getProfilAtih } from '../../authentification' @@ -9,7 +10,10 @@ export default async function Layout({ children }: PropsWithChildren): Promise - + } + />
{ const profil = await getProfilAtih() + let providers = undefined + if (!profil.isConnected) { + providers = await getProviders() as Record<'pasrel', ClientSafeProvider> + } + return ( ) } diff --git a/src/app/(deconnecte)/connexion/page.tsx b/src/app/(deconnecte)/connexion/page.tsx deleted file mode 100644 index 7baad51..0000000 --- a/src/app/(deconnecte)/connexion/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Metadata } from 'next' -import { BuiltInProviderType } from 'next-auth/providers' -import { ClientSafeProvider, LiteralUnion, getProviders } from 'next-auth/react' -import { ReactElement } from 'react' - -import { checkIfConnected } from '../../../authentification' -import Connexion from '../../../components/Connexion/Connexion' - -export const metadata: Metadata = { - title: 'Se connecter à EvalCarbone SIH', -} - -export default async function PageConnexion(): Promise { - await checkIfConnected() - - const providers = await getProviders() as Record, ClientSafeProvider> - - return ( - - ) -} diff --git a/src/app/(deconnecte)/layout.tsx b/src/app/(deconnecte)/layout.tsx deleted file mode 100644 index 74144bd..0000000 --- a/src/app/(deconnecte)/layout.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { PropsWithChildren, ReactElement } from 'react' - -import { getProfilAtih } from '../../authentification' -import AccesRapide from '../../components/sharedComponents/AccesRapide' -import EnTete from '../../components/sharedComponents/EnTete' -import PiedDePage from '../../components/sharedComponents/PiedDePage' - -export default async function Layout({ children }: PropsWithChildren): Promise { - return ( - <> - - -
-
- {children} -
-
- - - ) -} diff --git a/src/authentification.ts b/src/authentification.ts index f6993ee..e15e8a2 100644 --- a/src/authentification.ts +++ b/src/authentification.ts @@ -97,18 +97,10 @@ export async function getProfilAtih(): Promise { } } -export async function checkIfConnected(): Promise { - const session = await getServerSession(authOptions) - - if (session) { - redirect('/') - } -} - export async function checkIfNotConnected(): Promise { const session = await getServerSession(authOptions) if (!session) { - redirect('/connexion') + redirect('/') } } diff --git a/src/components/Accueil/Accueil.test.tsx b/src/components/Accueil/Accueil.test.tsx index e2ad499..97e66cb 100644 --- a/src/components/Accueil/Accueil.test.tsx +++ b/src/components/Accueil/Accueil.test.tsx @@ -1,29 +1,35 @@ -import { screen } from '@testing-library/react' +import { fireEvent, screen } from '@testing-library/react' +import * as nextAuth from 'next-auth/react' import PageAccueil from '../../app/(both)/page' import * as authentification from '../../authentification' -import { renderComponent } from '../../testShared' +import { renderComponent, spyPasrel } from '../../testShared' describe('page d’accueil', () => { describe('en étant déconnecté', () => { - it('quand j’affiche la page alors je peux me connecter', async () => { + it('quand j’affiche la page alors je peux m’authentifier', async () => { // GIVEN vi.spyOn(authentification, 'getProfilAtih').mockResolvedValueOnce({ isAdmin: false, isConnected: false, nomEtablissement: '', }) + // @ts-expect-error + vi.spyOn(nextAuth, 'getProviders').mockResolvedValueOnce(spyPasrel) + vi.spyOn(nextAuth, 'signIn').mockImplementationOnce(vi.fn()) - // WHEN renderComponent(await PageAccueil()) + const boutonSeConnecter = screen.getByRole('button', { name: 'Se connecter' }) + + // WHEN + fireEvent.click(boutonSeConnecter) // THEN - const lienSeConnecter = screen.getByRole('link', { name: 'Se connecter' }) - expect(lienSeConnecter).toHaveAttribute('href', 'connexion') + expect(nextAuth.signIn).toHaveBeenCalledWith('pasrel') }) }) - describe('en tant qu’utilisateur et connecté', () => { + describe('en tant qu’utilisateur et étant connecté', () => { it('quand j’affiche la page alors je peux créer un inventaire', async () => { vi.spyOn(authentification, 'getProfilAtih').mockResolvedValueOnce({ isAdmin: false, @@ -35,12 +41,14 @@ describe('page d’accueil', () => { renderComponent(await PageAccueil()) // THEN + const lienAccederAuxInventaires = screen.queryByRole('link', { name: 'Accéder aux inventaires' }) + expect(lienAccederAuxInventaires).not.toBeInTheDocument() const lienCreerUnInventaire = screen.getByRole('link', { name: 'Créer un inventaire' }) expect(lienCreerUnInventaire).toHaveAttribute('href', 'creer-un-inventaire') }) }) - describe('en tant qu’admin', () => { + describe('en tant qu’admin et étant connecté', () => { it('quand j’affiche la page alors je peux accéder aux inventaires', async () => { vi.spyOn(authentification, 'getProfilAtih').mockResolvedValueOnce({ isAdmin: true, @@ -52,8 +60,10 @@ describe('page d’accueil', () => { renderComponent(await PageAccueil()) // THEN - const lienAccederAuxInventaires = screen.queryByRole('link', { name: 'Accéder aux inventaires' }) + const lienAccederAuxInventaires = screen.getByRole('link', { name: 'Accéder aux inventaires' }) expect(lienAccederAuxInventaires).toHaveAttribute('href', 'inventaires') + const lienCreerUnInventaire = screen.queryByRole('link', { name: 'Créer un inventaire' }) + expect(lienCreerUnInventaire).not.toBeInTheDocument() }) }) }) diff --git a/src/components/Accueil/Accueil.tsx b/src/components/Accueil/Accueil.tsx index 6d037be..7092418 100644 --- a/src/components/Accueil/Accueil.tsx +++ b/src/components/Accueil/Accueil.tsx @@ -1,12 +1,16 @@ import Link from 'next/link' +import { ClientSafeProvider } from 'next-auth/react' import { ReactElement } from 'react' +import Connexion from '../Connexion/Connexion' + type AccueilProps = Readonly<{ isAdmin: boolean isConnected: boolean + providers?: Readonly> }> -export default function Accueil({ isAdmin, isConnected }: AccueilProps): ReactElement { +export default function Accueil({ isAdmin, isConnected, providers }: AccueilProps): ReactElement { return (
@@ -57,16 +61,12 @@ export default function Accueil({ isAdmin, isConnected }: AccueilProps): ReactEl ) : null } { - !isConnected && ( -
- - Se connecter - -
- ) + !isConnected && providers ? ( + + ) : null }
diff --git a/src/components/Connexion/Connexion.test.tsx b/src/components/Connexion/Connexion.test.tsx deleted file mode 100644 index 56ca4b2..0000000 --- a/src/components/Connexion/Connexion.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { fireEvent, screen } from '@testing-library/react' -// import * as navigation from 'next/navigation' -// import * as nextAuth from 'next-auth' -import * as nextAuthReact from 'next-auth/react' - -import PageConnexion from '../../app/(deconnecte)/connexion/page' -import * as authentification from '../../authentification' -import { renderComponent } from '../../testShared' - -describe('page de connexion', () => { - // describe('en étant connecté', () => { - // it('quand j’affiche une page quelconque alors je suis redirigé vers l’accueil', async () => { - // // GIVEN - // vi.spyOn(nextAuth, 'getServerSession').mockResolvedValueOnce({} as nextAuth.Session) - // vi.spyOn(navigation, 'redirect').mockImplementationOnce(vi.fn()) - - // // WHEN - // renderComponent(await PageConnexion()) - - // // THEN - // expect(navigation.redirect).toHaveBeenCalledWith('/') - // }) - // }) - - describe('en étant déconnecté', () => { - it('quand je clique sur me connecter alors je me connecte', async () => { - // GIVEN - vi.spyOn(authentification, 'checkIfConnected').mockImplementationOnce(vi.fn()) - // @ts-expect-error - vi.spyOn(nextAuthReact, 'getProviders').mockResolvedValueOnce({ - pasrel: { - callbackUrl: 'http://localhost:3000/api/auth/callback/pasrel', - id: 'pasrel', - name: 'Pasrel', - signinUrl: 'http://localhost:3000/api/auth/signin/pasrel', - type: 'oauth', - }, - }) - vi.spyOn(nextAuthReact, 'signIn').mockImplementationOnce(vi.fn()) - - renderComponent(await PageConnexion()) - - // WHEN - const boutonSeConnecter = screen.getByRole('button', { name: 'Se connecter avec Plage' }) - fireEvent.click(boutonSeConnecter) - - // THEN - expect(nextAuthReact.signIn).toHaveBeenCalledWith('pasrel') - }) - }) -}) diff --git a/src/components/Connexion/Connexion.tsx b/src/components/Connexion/Connexion.tsx index f06ca9e..0780541 100644 --- a/src/components/Connexion/Connexion.tsx +++ b/src/components/Connexion/Connexion.tsx @@ -1,29 +1,23 @@ 'use client' -import { BuiltInProviderType } from 'next-auth/providers' -import { ClientSafeProvider, LiteralUnion, signIn } from 'next-auth/react' +import { ClientSafeProvider, signIn } from 'next-auth/react' import { ReactElement } from 'react' type ConnexionProps = Readonly<{ - providers: Readonly, ClientSafeProvider>> + providers: Readonly> + styleDuBouton: 'bouton' | 'lien' }> -export default function Connexion({ providers }: ConnexionProps): ReactElement { +export default function Connexion({ providers, styleDuBouton }: ConnexionProps): ReactElement { + const classe = styleDuBouton === 'bouton' ? 'btn btn--plain btn--primary' : 'nav-link' + return ( -
- { - Object.values(providers).map((provider): ReactElement => ( -
- -
- )) - } -
+ ) } diff --git a/src/components/sharedComponents/EnTete.test.tsx b/src/components/sharedComponents/EnTete.test.tsx index 4154a45..e5dbd8a 100644 --- a/src/components/sharedComponents/EnTete.test.tsx +++ b/src/components/sharedComponents/EnTete.test.tsx @@ -3,7 +3,7 @@ import * as nextAuth from 'next-auth/react' import EnTete from './EnTete' import { ProfilAtih } from '../../authentification' -import { renderComponent } from '../../testShared' +import { renderComponent, spyPasrel } from '../../testShared' describe('en-tête', () => { describe('en étant connecté', () => { @@ -64,21 +64,35 @@ describe('en-tête', () => { }) describe('en étant déconnecté', () => { - it('quand j’affiche une page quelconque alors j’ai accès au lien de connexion', () => { + it('quand j’affiche une page quelconque alors je peux m’authentifier', () => { + // GIVEN + vi.spyOn(nextAuth, 'signIn').mockImplementationOnce(vi.fn()) + + renderComponent( + + ) + const menuItems = screen.getAllByRole('listitem') + const boutonSeConnecter = within(menuItems[2]).getByRole('button', { name: 'Se connecter' }) + // WHEN - renderComponent() + fireEvent.click(boutonSeConnecter) // THEN - const menuItems = screen.getAllByRole('listitem') expect(menuItems).toHaveLength(3) - - const lienSeConnecter = within(menuItems[2]).getByRole('link', { name: 'Se connecter' }) - expect(lienSeConnecter).toHaveAttribute('href', '/connexion') + expect(nextAuth.signIn).toHaveBeenCalledWith('pasrel') }) it('quand j’affiche une page quelconque alors je n’ai pas accès à la déconnexion', () => { // WHEN - renderComponent() + renderComponent( + + ) // THEN const boutonSeDeconnecter = screen.queryByRole('button', { name: 'Se déconnecter' }) @@ -87,7 +101,12 @@ describe('en-tête', () => { it('quand j’affiche une page quelconque alors je n’ai pas accès au lien de mes inventaires', () => { // WHEN - renderComponent() + renderComponent( + + ) // THEN const lienInventaires = screen.queryByRole('link', { name: 'Inventaires' }) diff --git a/src/components/sharedComponents/EnTete.tsx b/src/components/sharedComponents/EnTete.tsx index 296c5b7..72156e1 100644 --- a/src/components/sharedComponents/EnTete.tsx +++ b/src/components/sharedComponents/EnTete.tsx @@ -2,13 +2,16 @@ import Image from 'next/image' import { usePathname } from 'next/navigation' +import { ClientSafeProvider } from 'next-auth/react' import { ReactElement, ReactNode } from 'react' import Deconnexion from './Deconnexion' import { ProfilAtih } from '../../authentification' +import Connexion from '../Connexion/Connexion' type EnTeteProps = Readonly<{ profil: ProfilAtih + providers?: Readonly> }> type Menu = Readonly<{ @@ -17,7 +20,7 @@ type Menu = Readonly<{ path: string }> -export default function EnTete({ profil }: EnTeteProps): ReactElement { +export default function EnTete({ profil, providers }: EnTeteProps): ReactElement { const pathname = usePathname() const menu: ReadonlyArray = [ { @@ -40,11 +43,6 @@ export default function EnTete({ profil }: EnTeteProps): ReactElement { label: 'Référentiels', path: '/modifier-un-referentiel', }, - { - isDisplayed: !profil.isConnected, - label: 'Se connecter', - path: '/connexion', - }, ] return ( @@ -120,9 +118,19 @@ export default function EnTete({ profil }: EnTeteProps): ReactElement { return null }) } + { + !profil.isConnected && providers ? ( +
  • + +
  • + ) : null + } { profil.isConnected ? ( -
  • +
  • ) : null diff --git a/src/testShared.ts b/src/testShared.ts index c947239..9ba3d94 100644 --- a/src/testShared.ts +++ b/src/testShared.ts @@ -1,6 +1,7 @@ import { indicateurImpactEquipementModel, inventaireModel, modeleModel } from '@prisma/client' import { RenderResult, render } from '@testing-library/react' import { UserEvent, userEvent } from '@testing-library/user-event' +import { ClientSafeProvider } from 'next-auth/react' import { ReactElement } from 'react' import * as authentification from './authentification' @@ -63,6 +64,16 @@ export const spyNextNavigation = { }, } +export const spyPasrel = { + pasrel: { + callbackUrl: 'http://localhost:3000/api/auth/callback/pasrel', + id: 'pasrel', + name: 'Pasrel', + signinUrl: 'http://localhost:3000/api/auth/signin/pasrel', + type: 'oauth', + }, +} as Record<'pasrel', ClientSafeProvider> + export function inventaireModelFactory(override?: Partial): inventaireModel { const date = new Date()