diff --git a/ui/package.json b/ui/package.json
index c3f3e581..e4ff9a9b 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -61,8 +61,8 @@
"lint": "npm run lint:types & npm run lint:eslint",
"lint:eslint": "eslint ./src --max-warnings 0 --cache --config .eslintrc.js",
"lint:types": "tsc --noemit",
- "jest": "craco test",
- "jest:ci": "CI=true craco test --verbose --ci --coverage",
+ "jest": "craco test --setupFilesAfterEnv ./testing/setupTests.ts",
+ "jest:ci": "CI=true craco test --setupFilesAfterEnv ./testing/setupTests.ts --verbose --ci --coverage",
"genSchemaTypes": "npx apollo client:codegen --target=typescript --globalTypesFile=src/__generated__/globalTypes.ts",
"extractTranslations": "i18next -c i18next-parser.config.js",
"prepare": "(cd .. && npx husky install)"
diff --git a/ui/src/Pages/PeoplePage/FaceCircleImage.test.tsx b/ui/src/Pages/PeoplePage/FaceCircleImage.test.tsx
index d82ffc17..f0a23a15 100644
--- a/ui/src/Pages/PeoplePage/FaceCircleImage.test.tsx
+++ b/ui/src/Pages/PeoplePage/FaceCircleImage.test.tsx
@@ -1,12 +1,8 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { render, screen } from '@testing-library/react'
import FaceCircleImage from './FaceCircleImage'
import { myFaces_myFaceGroups_imageFaces } from './__generated__/myFaces'
-require('../../localization').setupLocalization()
-
test('face circle image', () => {
const imageFace: myFaces_myFaceGroups_imageFaces = {
__typename: 'ImageFace',
diff --git a/ui/src/Pages/PeoplePage/PeoplePage.test.tsx b/ui/src/Pages/PeoplePage/PeoplePage.test.tsx
index 4480f4f3..3a6e9b36 100644
--- a/ui/src/Pages/PeoplePage/PeoplePage.test.tsx
+++ b/ui/src/Pages/PeoplePage/PeoplePage.test.tsx
@@ -1,5 +1,3 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import PeoplePage, {
@@ -11,8 +9,6 @@ import { MockedProvider } from '@apollo/client/testing'
import { MemoryRouter } from 'react-router'
import { myFaces_myFaceGroups } from './__generated__/myFaces'
-require('../../localization').setupLocalization()
-
jest.mock('../../hooks/useScrollPagination')
describe('PeoplePage component', () => {
diff --git a/ui/src/Pages/PeoplePage/SingleFaceGroup/SingleFaceGroup.test.tsx b/ui/src/Pages/PeoplePage/SingleFaceGroup/SingleFaceGroup.test.tsx
index a0bc0df8..acd0e202 100644
--- a/ui/src/Pages/PeoplePage/SingleFaceGroup/SingleFaceGroup.test.tsx
+++ b/ui/src/Pages/PeoplePage/SingleFaceGroup/SingleFaceGroup.test.tsx
@@ -1,12 +1,8 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import { MockedProvider } from '@apollo/client/testing'
import SingleFaceGroup, { SINGLE_FACE_GROUP } from './SingleFaceGroup'
-require('../../../localization').setupLocalization()
-
jest.mock('../../../hooks/useScrollPagination')
test('single face group', async () => {
diff --git a/ui/src/Pages/SettingsPage/Users/AddUserRow.test.tsx b/ui/src/Pages/SettingsPage/Users/AddUserRow.test.tsx
new file mode 100644
index 00000000..eadd6437
--- /dev/null
+++ b/ui/src/Pages/SettingsPage/Users/AddUserRow.test.tsx
@@ -0,0 +1,93 @@
+import React from 'react'
+import { fireEvent, render, screen, waitFor } from '@testing-library/react'
+import AddUserRow, {
+ CREATE_USER_MUTATION,
+ USER_ADD_ROOT_PATH_MUTATION,
+} from './AddUserRow'
+import { MockedProvider } from '@apollo/client/testing'
+
+const gqlMock = [
+ {
+ request: {
+ query: CREATE_USER_MUTATION,
+ variables: { username: 'testuser', admin: false },
+ },
+ result: {
+ data: {
+ createUser: {
+ id: '123',
+ username: 'testuser',
+ admin: false,
+ __typename: 'User',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_ADD_ROOT_PATH_MUTATION,
+ variables: { id: '123', rootPath: '/tmp' },
+ },
+ result: { data: { userAddRootPath: { id: '567', __typename: 'Album' } } },
+ },
+]
+
+test('Add user with username and path', async () => {
+ const userAdded = jest.fn()
+ const setShow = jest.fn()
+
+ render(
+
+
+
+ )
+
+ const usernameInput = screen.getByPlaceholderText('Username')
+ const pathInput = screen.getByPlaceholderText('/path/to/photos')
+ const addUserBtn = screen.getByText('Add user')
+
+ fireEvent.change(usernameInput, { target: { value: 'testuser' } })
+ fireEvent.change(pathInput, { target: { value: '/tmp' } })
+ fireEvent.click(addUserBtn)
+
+ await waitFor(() => {
+ expect(userAdded).toHaveBeenCalledTimes(1)
+ })
+
+ expect(setShow).not.toHaveBeenCalled()
+})
+
+test('Add user with only username', async () => {
+ const userAdded = jest.fn()
+ const setShow = jest.fn()
+
+ render(
+
+
+
+ )
+
+ const usernameInput = screen.getByPlaceholderText('Username')
+ const addUserBtn = screen.getByText('Add user')
+
+ // don't set path
+ // const pathInput = screen.getByPlaceholderText('/path/to/photos')
+ // fireEvent.change(pathInput, { target: { value: '/tmp' } })
+
+ fireEvent.change(usernameInput, { target: { value: 'testuser' } })
+ fireEvent.click(addUserBtn)
+
+ await waitFor(() => {
+ expect(userAdded).toHaveBeenCalledTimes(1)
+ })
+
+ expect(setShow).not.toHaveBeenCalled()
+})
diff --git a/ui/src/Pages/SettingsPage/Users/AddUserRow.tsx b/ui/src/Pages/SettingsPage/Users/AddUserRow.tsx
index b8c28b33..4ff90361 100644
--- a/ui/src/Pages/SettingsPage/Users/AddUserRow.tsx
+++ b/ui/src/Pages/SettingsPage/Users/AddUserRow.tsx
@@ -5,7 +5,7 @@ import Checkbox from '../../../primitives/form/Checkbox'
import { TextField, Button, ButtonGroup } from '../../../primitives/form/Input'
import { TableRow, TableCell } from '../../../primitives/Table'
-const CREATE_USER_MUTATION = gql`
+export const CREATE_USER_MUTATION = gql`
mutation createUser($username: String!, $admin: Boolean!) {
createUser(username: $username, admin: $admin) {
id
@@ -41,16 +41,19 @@ const AddUserRow = ({ setShow, show, onUserAdded }: AddUserRowProps) => {
const { t } = useTranslation()
const [state, setState] = useState(initialState)
+ const finished = () => {
+ setState(initialState)
+ onUserAdded()
+ }
+
const [addRootPath, { loading: addRootPathLoading }] = useMutation(
USER_ADD_ROOT_PATH_MUTATION,
{
onCompleted: () => {
- setState(initialState)
- onUserAdded()
+ finished()
},
onError: () => {
- setState(initialState)
- onUserAdded()
+ finished()
},
}
)
@@ -67,7 +70,7 @@ const AddUserRow = ({ setShow, show, onUserAdded }: AddUserRowProps) => {
},
})
} else {
- setState(initialState)
+ finished()
}
},
}
diff --git a/ui/src/Pages/SharePage/SharePage.test.js b/ui/src/Pages/SharePage/SharePage.test.js
index 1dbf8d9e..cae0ec7c 100644
--- a/ui/src/Pages/SharePage/SharePage.test.js
+++ b/ui/src/Pages/SharePage/SharePage.test.js
@@ -1,5 +1,3 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { MemoryRouter } from 'react-router-dom'
import { MockedProvider } from '@apollo/client/testing'
@@ -18,8 +16,6 @@ import SharePage, {
import { SIDEBAR_DOWNLOAD_QUERY } from '../../components/sidebar/SidebarDownload'
import { SHARE_ALBUM_QUERY } from './AlbumSharePage'
-require('../../localization').setupLocalization()
-
describe('load correct share page, based on graphql query', () => {
const token = 'TOKEN123'
diff --git a/ui/src/components/layout/Layout.test.js b/ui/src/components/layout/Layout.test.js
index 1140b865..b053203f 100644
--- a/ui/src/components/layout/Layout.test.js
+++ b/ui/src/components/layout/Layout.test.js
@@ -1,10 +1,7 @@
-import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react'
import React from 'react'
import Layout from './Layout'
-require('../../localization').setupLocalization()
-
test('Layout component', async () => {
render(
diff --git a/ui/src/components/layout/MainMenu.test.tsx b/ui/src/components/layout/MainMenu.test.tsx
index dbb5fb1b..89623ea2 100644
--- a/ui/src/components/layout/MainMenu.test.tsx
+++ b/ui/src/components/layout/MainMenu.test.tsx
@@ -1,5 +1,3 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { MockedProvider } from '@apollo/client/testing'
import { render, screen } from '@testing-library/react'
@@ -9,8 +7,6 @@ import { ADMIN_QUERY } from './Layout'
import { MemoryRouter } from 'react-router-dom'
import MainMenu, { MAPBOX_QUERY } from './MainMenu'
-require('../../localization').setupLocalization()
-
jest.mock('../../helpers/authentication.ts')
const authTokenMock = authentication.authToken as jest.MockedFunction<
diff --git a/ui/src/components/photoGallery/PhotoGallery.test.tsx b/ui/src/components/photoGallery/PhotoGallery.test.tsx
index 18fcd55e..8fe4aa0c 100644
--- a/ui/src/components/photoGallery/PhotoGallery.test.tsx
+++ b/ui/src/components/photoGallery/PhotoGallery.test.tsx
@@ -1,4 +1,3 @@
-import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react'
import React from 'react'
@@ -6,8 +5,6 @@ import { MediaType } from '../../__generated__/globalTypes'
import PhotoGallery from './PhotoGallery'
import { PhotoGalleryState } from './photoGalleryReducer'
-require('../../localization').setupLocalization()
-
jest.mock('./photoGalleryMutations', () => ({
useMarkFavoriteMutation: () => [jest.fn()],
}))
diff --git a/ui/src/components/photoGallery/presentView/PresentMedia.test.tsx b/ui/src/components/photoGallery/presentView/PresentMedia.test.tsx
index 027b5fc6..06c3423d 100644
--- a/ui/src/components/photoGallery/presentView/PresentMedia.test.tsx
+++ b/ui/src/components/photoGallery/presentView/PresentMedia.test.tsx
@@ -1,4 +1,3 @@
-import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react'
import React from 'react'
diff --git a/ui/src/components/photoGallery/presentView/PresentNavigationOverlay.test.tsx b/ui/src/components/photoGallery/presentView/PresentNavigationOverlay.test.tsx
index 89dd1fba..c9561f09 100644
--- a/ui/src/components/photoGallery/presentView/PresentNavigationOverlay.test.tsx
+++ b/ui/src/components/photoGallery/presentView/PresentNavigationOverlay.test.tsx
@@ -1,6 +1,3 @@
-import '@testing-library/jest-dom'
-import '@testing-library/user-event'
-
import React from 'react'
import PresentNavigationOverlay from './PresentNavigationOverlay'
import { fireEvent, render, screen, act } from '@testing-library/react'
diff --git a/ui/src/components/routes/AuthorizedRoute.test.js b/ui/src/components/routes/AuthorizedRoute.test.js
index 50fbf7c4..7bd83715 100644
--- a/ui/src/components/routes/AuthorizedRoute.test.js
+++ b/ui/src/components/routes/AuthorizedRoute.test.js
@@ -1,5 +1,3 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import AuthorizedRoute from './AuthorizedRoute'
import { render, screen } from '@testing-library/react'
diff --git a/ui/src/components/routes/Routes.test.js b/ui/src/components/routes/Routes.test.js
index c29bbc1c..1c7c44ee 100644
--- a/ui/src/components/routes/Routes.test.js
+++ b/ui/src/components/routes/Routes.test.js
@@ -1,5 +1,3 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import Routes from './Routes'
@@ -14,8 +12,6 @@ jest.mock('../../Pages/LoginPage/LoginPage.tsx', () => () => (
mocked login page
))
-require('../../localization').setupLocalization()
-
describe('routes', () => {
test('unauthorized root path should navigate to login page', async () => {
render(
diff --git a/ui/src/components/sidebar/MediaSidebar.test.js b/ui/src/components/sidebar/MediaSidebar.test.js
index d5ca1a7b..7ec5f15f 100644
--- a/ui/src/components/sidebar/MediaSidebar.test.js
+++ b/ui/src/components/sidebar/MediaSidebar.test.js
@@ -1,11 +1,7 @@
-import '@testing-library/jest-dom'
-
import React from 'react'
import { render, screen } from '@testing-library/react'
import { MetadataInfo } from './MediaSidebar'
-require('../../localization').setupLocalization()
-
describe('MetadataInfo', () => {
test('without EXIF information', async () => {
const media = {
diff --git a/ui/testing/setupTests.ts b/ui/testing/setupTests.ts
new file mode 100644
index 00000000..99c2dbf0
--- /dev/null
+++ b/ui/testing/setupTests.ts
@@ -0,0 +1,9 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom'
+import '@testing-library/user-event'
+
+// setup localization to make it easier to select elements by text
+require('../src/localization').setupLocalization()