From 91d63ebddc6df01ff7d0ac687ef3a54df9c1de98 Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Sun, 6 Feb 2022 11:04:28 +0100
Subject: [PATCH] Write test for login and fix initial setup bug

---
 .../Pages/LoginPage/InitialSetupPage.test.tsx | 77 ++++++++++++++++++
 ui/src/Pages/LoginPage/InitialSetupPage.tsx   | 29 +++----
 ui/src/Pages/LoginPage/LoginPage.test.tsx     | 78 +++++++++++++++++++
 ui/src/Pages/LoginPage/LoginPage.tsx          | 23 +++---
 ui/src/Pages/LoginPage/loginTestHelpers.ts    | 15 ++++
 ui/src/Pages/LoginPage/loginUtilities.tsx     |  2 +-
 6 files changed, 199 insertions(+), 25 deletions(-)
 create mode 100644 ui/src/Pages/LoginPage/InitialSetupPage.test.tsx
 create mode 100644 ui/src/Pages/LoginPage/LoginPage.test.tsx
 create mode 100644 ui/src/Pages/LoginPage/loginTestHelpers.ts

diff --git a/ui/src/Pages/LoginPage/InitialSetupPage.test.tsx b/ui/src/Pages/LoginPage/InitialSetupPage.test.tsx
new file mode 100644
index 0000000..e1a6f22
--- /dev/null
+++ b/ui/src/Pages/LoginPage/InitialSetupPage.test.tsx
@@ -0,0 +1,77 @@
+import React from 'react'
+import { MockedProvider } from '@apollo/client/testing'
+import { render, screen, waitFor } from '@testing-library/react'
+import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom'
+import { createMemoryHistory } from 'history'
+import * as authentication from '../../helpers/authentication'
+import InitialSetupPage from './InitialSetupPage'
+import { mockInitialSetupGraphql } from './loginTestHelpers'
+
+jest.mock('../../helpers/authentication.ts')
+
+const authToken = authentication.authToken as jest.Mock<
+  ReturnType<typeof authentication.authToken>
+>
+
+describe('Initial setup page', () => {
+  test('Render initial setup form', async () => {
+    authToken.mockImplementation(() => null)
+
+    const history = createMemoryHistory({
+      initialEntries: ['/initialSetup'],
+    })
+
+    render(
+      <MockedProvider mocks={[mockInitialSetupGraphql(true)]}>
+        <HistoryRouter history={history}>
+          <InitialSetupPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    expect(screen.getByLabelText('Username')).toBeInTheDocument()
+    expect(screen.getByLabelText('Password')).toBeInTheDocument()
+    expect(screen.getByLabelText('Photo path')).toBeInTheDocument()
+    expect(screen.getByDisplayValue('Setup Photoview')).toBeInTheDocument()
+  })
+
+  test('Redirect if auth token is present', async () => {
+    authToken.mockImplementation(() => 'some-token')
+
+    const history = createMemoryHistory({
+      initialEntries: ['/initialSetup'],
+    })
+
+    render(
+      <MockedProvider mocks={[mockInitialSetupGraphql(true)]}>
+        <HistoryRouter history={history}>
+          <InitialSetupPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    await waitFor(() => {
+      expect(history.location.pathname).toBe('/')
+    })
+  })
+
+  test('Redirect if not initial setup', async () => {
+    authToken.mockImplementation(() => null)
+
+    const history = createMemoryHistory({
+      initialEntries: ['/initialSetup'],
+    })
+
+    render(
+      <MockedProvider mocks={[mockInitialSetupGraphql(false)]}>
+        <HistoryRouter history={history}>
+          <InitialSetupPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    await waitFor(() => {
+      expect(history.location.pathname).toBe('/')
+    })
+  })
+})
diff --git a/ui/src/Pages/LoginPage/InitialSetupPage.tsx b/ui/src/Pages/LoginPage/InitialSetupPage.tsx
index 80b10c3..b0b1722 100644
--- a/ui/src/Pages/LoginPage/InitialSetupPage.tsx
+++ b/ui/src/Pages/LoginPage/InitialSetupPage.tsx
@@ -1,9 +1,9 @@
-import React from 'react'
+import React, { useEffect } from 'react'
 import { gql, useQuery, useMutation } from '@apollo/client'
 import { useNavigate } from 'react-router-dom'
 import { Container } from './loginUtilities'
 
-import { checkInitialSetupQuery, login } from './loginUtilities'
+import { INITIAL_SETUP_QUERY, login } from './loginUtilities'
 import { authToken } from '../../helpers/authentication'
 import { useTranslation } from 'react-i18next'
 import { CheckInitialSetup } from './__generated__/CheckInitialSetup'
@@ -45,19 +45,18 @@ const InitialSetupPage = () => {
     formState: { errors: formErrors },
   } = useForm<InitialSetupFormData>()
 
-  if (authToken()) {
-    navigate('/')
-    return null
-  }
+  useEffect(() => {
+    if (authToken()) navigate('/')
+  }, [])
 
-  const { data: initialSetupData } = useQuery<CheckInitialSetup>(
-    checkInitialSetupQuery
-  )
+  const { data: initialSetupData } =
+    useQuery<CheckInitialSetup>(INITIAL_SETUP_QUERY)
 
-  if (initialSetupData?.siteInfo?.initialSetup) {
-    navigate('/')
-    return null
-  }
+  const notInitialSetup = initialSetupData?.siteInfo?.initialSetup === false
+
+  useEffect(() => {
+    if (notInitialSetup) navigate('/')
+  }, [notInitialSetup])
 
   const [authorize, { loading: authorizeLoading, data: authorizationData }] =
     useMutation(initialSetupMutation, {
@@ -80,6 +79,10 @@ const InitialSetupPage = () => {
     })
   })
 
+  if (authToken() || notInitialSetup) {
+    return null
+  }
+
   let errorMessage = null
   if (authorizationData && !authorizationData.initialSetupWizard.success) {
     errorMessage = authorizationData.initialSetupWizard.status
diff --git a/ui/src/Pages/LoginPage/LoginPage.test.tsx b/ui/src/Pages/LoginPage/LoginPage.test.tsx
new file mode 100644
index 0000000..1b4bfde
--- /dev/null
+++ b/ui/src/Pages/LoginPage/LoginPage.test.tsx
@@ -0,0 +1,78 @@
+import { render, screen, waitFor } from '@testing-library/react'
+import React from 'react'
+import LoginPage from './LoginPage'
+import * as authentication from '../../helpers/authentication'
+import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom'
+import { createMemoryHistory } from 'history'
+import { MockedProvider } from '@apollo/client/testing'
+import { mockInitialSetupGraphql } from './loginTestHelpers'
+
+jest.mock('../../helpers/authentication.ts')
+
+const authToken = authentication.authToken as jest.Mock<
+  ReturnType<typeof authentication.authToken>
+>
+
+describe('Login page redirects', () => {
+  test('Auth token redirect', async () => {
+    authToken.mockImplementation(() => 'some-token')
+
+    const history = createMemoryHistory({
+      initialEntries: ['/login'],
+    })
+
+    render(
+      <MockedProvider mocks={[]}>
+        <HistoryRouter history={history}>
+          <LoginPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    await waitFor(() => {
+      expect(history.location.pathname).toBe('/')
+    })
+  })
+
+  test('Initial setup redirect', async () => {
+    authToken.mockImplementation(() => null)
+
+    const history = createMemoryHistory({
+      initialEntries: ['/login'],
+    })
+
+    render(
+      <MockedProvider mocks={[mockInitialSetupGraphql(true)]}>
+        <HistoryRouter history={history}>
+          <LoginPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    await waitFor(() => {
+      expect(history.location.pathname).toBe('/initialSetup')
+    })
+  })
+})
+
+describe('Login page', () => {
+  test('Render login form', async () => {
+    authToken.mockImplementation(() => null)
+
+    const history = createMemoryHistory({
+      initialEntries: ['/login'],
+    })
+
+    render(
+      <MockedProvider mocks={[mockInitialSetupGraphql(false)]}>
+        <HistoryRouter history={history}>
+          <LoginPage />
+        </HistoryRouter>
+      </MockedProvider>
+    )
+
+    expect(screen.getByLabelText('Username')).toBeInTheDocument()
+    expect(screen.getByLabelText('Password')).toBeInTheDocument()
+    expect(screen.getByDisplayValue('Sign in')).toBeInTheDocument()
+  })
+})
diff --git a/ui/src/Pages/LoginPage/LoginPage.tsx b/ui/src/Pages/LoginPage/LoginPage.tsx
index 2af22ab..485a510 100644
--- a/ui/src/Pages/LoginPage/LoginPage.tsx
+++ b/ui/src/Pages/LoginPage/LoginPage.tsx
@@ -1,7 +1,7 @@
-import React from 'react'
+import React, { useEffect } from 'react'
 import { useQuery, gql, useMutation } from '@apollo/client'
 import { useForm } from 'react-hook-form'
-import { checkInitialSetupQuery, login } from './loginUtilities'
+import { INITIAL_SETUP_QUERY, login } from './loginUtilities'
 import { authToken } from '../../helpers/authentication'
 
 import { useTranslation } from 'react-i18next'
@@ -69,8 +69,6 @@ const LoginForm = () => {
     })
   }
 
-  console.log('errors', formErrors)
-
   const errorMessage =
     data && !data.authorizeUser.success ? data.authorizeUser.status : null
 
@@ -130,16 +128,19 @@ const LoginPage = () => {
   const navigate = useNavigate()
 
   const { data: initialSetupData } = useQuery<CheckInitialSetup>(
-    checkInitialSetupQuery
+    INITIAL_SETUP_QUERY,
+    { variables: {} }
   )
 
-  if (authToken()) {
-    navigate('/')
-    return null
-  }
+  useEffect(() => {
+    if (authToken()) navigate('/')
+  }, [])
 
-  if (initialSetupData?.siteInfo?.initialSetup) {
-    navigate('initialSetup')
+  useEffect(() => {
+    if (initialSetupData?.siteInfo?.initialSetup) navigate('/initialSetup')
+  }, [initialSetupData?.siteInfo?.initialSetup])
+
+  if (authToken() || initialSetupData?.siteInfo?.initialSetup) {
     return null
   }
 
diff --git a/ui/src/Pages/LoginPage/loginTestHelpers.ts b/ui/src/Pages/LoginPage/loginTestHelpers.ts
new file mode 100644
index 0000000..cde4ecf
--- /dev/null
+++ b/ui/src/Pages/LoginPage/loginTestHelpers.ts
@@ -0,0 +1,15 @@
+import { INITIAL_SETUP_QUERY } from './loginUtilities'
+
+export const mockInitialSetupGraphql = (initial: boolean) => ({
+  request: {
+    query: INITIAL_SETUP_QUERY,
+    variables: {},
+  },
+  result: {
+    data: {
+      siteInfo: {
+        initialSetup: initial,
+      },
+    },
+  },
+})
diff --git a/ui/src/Pages/LoginPage/loginUtilities.tsx b/ui/src/Pages/LoginPage/loginUtilities.tsx
index f27397f..4f75403 100644
--- a/ui/src/Pages/LoginPage/loginUtilities.tsx
+++ b/ui/src/Pages/LoginPage/loginUtilities.tsx
@@ -2,7 +2,7 @@ import { gql } from '@apollo/client'
 import { saveTokenCookie } from '../../helpers/authentication'
 import styled from 'styled-components'
 
-export const checkInitialSetupQuery = gql`
+export const INITIAL_SETUP_QUERY = gql`
   query CheckInitialSetup {
     siteInfo {
       initialSetup
