From a83afeab724336859d1bfc84a609afd65e4d6d31 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 14:07:27 +0100 Subject: [PATCH 01/18] add editable name to profile frontend --- frontend/src/components/Profile.tsx | 6 +++- frontend/src/components/ProfileUserInfo.tsx | 34 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/ProfileUserInfo.tsx diff --git a/frontend/src/components/Profile.tsx b/frontend/src/components/Profile.tsx index 159eabf..d26bbca 100644 --- a/frontend/src/components/Profile.tsx +++ b/frontend/src/components/Profile.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { User } from "../types/User.ts"; import axios from "axios"; import { Statistics } from "../types/Statistics.ts"; +import {ProfileUserInfo} from "./ProfileUserInfo.tsx"; type PlayProps = { user: User; @@ -17,7 +18,7 @@ export const Profile: React.FC = ({ user }) => { axios.get(`api/user/${user.id}/statistics`).then((response) => setStatistics(response.data)); }, [user]); - if (user == null || !statistics) { + if (!user || !statistics) { return
loading
; } @@ -25,6 +26,9 @@ export const Profile: React.FC = ({ user }) => {
+

User Information

+ +

Statistics

diff --git a/frontend/src/components/ProfileUserInfo.tsx b/frontend/src/components/ProfileUserInfo.tsx new file mode 100644 index 0000000..b15777b --- /dev/null +++ b/frontend/src/components/ProfileUserInfo.tsx @@ -0,0 +1,34 @@ +import React, { ChangeEvent, useState } from "react"; +import { User } from "../types/User.ts"; + +type ProfileUserInfoProps = { + className: string; + user: User; +}; + +export const ProfileUserInfo: React.FC = ({ className, user }) => { + + // TODO add default random name, like "Erratic Bunny #1" to a newly created user + const [name, setName] = useState(user?.name ?? "No username found"); + const [isEditingName, setIsEditingName] = useState(false); + + const onNameChange = (event: ChangeEvent): void => { + setName(event.target.value); + }; + + return ( +
+ {isEditingName ? ( + setIsEditingName(false)} + /> + ) : ( + + )} +
+ ); +}; From 23d3af1671c68da5c7291e4e743a8be3b0024050 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:17:41 +0100 Subject: [PATCH 02/18] adjust decimals of shown statistics back --- frontend/src/components/Profile.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Profile.tsx b/frontend/src/components/Profile.tsx index 5d7a067..cda6ee3 100644 --- a/frontend/src/components/Profile.tsx +++ b/frontend/src/components/Profile.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { User } from "../types/User.ts"; import axios from "axios"; import { Statistics } from "../types/Statistics.ts"; -import {ProfileUserInfo} from "./ProfileUserInfo.tsx"; +import { ProfileUserInfo } from "./ProfileUserInfo.tsx"; import { Statistic } from "./Statistic.tsx"; type PlayProps = { @@ -45,7 +45,7 @@ export const Profile: React.FC = ({ user }) => {

User Information

- +

Statistics

@@ -53,9 +53,9 @@ export const Profile: React.FC = ({ user }) => { ))}
From f9229da5d50e5f1db018eedff7a0e1582a5d8708 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:37:25 +0100 Subject: [PATCH 03/18] make more information available in frontend user object --- frontend/src/types/User.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/types/User.ts b/frontend/src/types/User.ts index 3eadd04..0091ca4 100644 --- a/frontend/src/types/User.ts +++ b/frontend/src/types/User.ts @@ -1,4 +1,7 @@ export type User = { id: string; name: string; + email: string; + createdAt: string; + lastActive: string; } | null; From 1b883b7e4de7cc8ddfa1fbc26a48444f4046dd52 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:37:41 +0100 Subject: [PATCH 04/18] slight style changes --- frontend/src/components/Profile.tsx | 6 +++--- frontend/src/components/Statistic.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/Profile.tsx b/frontend/src/components/Profile.tsx index cda6ee3..4e11973 100644 --- a/frontend/src/components/Profile.tsx +++ b/frontend/src/components/Profile.tsx @@ -44,10 +44,10 @@ export const Profile: React.FC = ({ user }) => {
-

User Information

- +

User Information

+ -

Statistics

+

Statistics

{statistics.map((statistic) => ( = ({ name, easy, medium, hard }) => { return (
-
{name}:
+
{name}
{easy || "N/A"} {medium || "N/A"} From a61d0e36d815d3921a67c9807a49d8ef5250cd02 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:38:15 +0100 Subject: [PATCH 05/18] make username editable --- frontend/src/components/ProfileUserInfo.tsx | 49 +++++++++++++++------ 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/ProfileUserInfo.tsx b/frontend/src/components/ProfileUserInfo.tsx index b15777b..822af60 100644 --- a/frontend/src/components/ProfileUserInfo.tsx +++ b/frontend/src/components/ProfileUserInfo.tsx @@ -1,5 +1,6 @@ import React, { ChangeEvent, useState } from "react"; import { User } from "../types/User.ts"; +import axios from "axios"; type ProfileUserInfoProps = { className: string; @@ -7,28 +8,50 @@ type ProfileUserInfoProps = { }; export const ProfileUserInfo: React.FC = ({ className, user }) => { - - // TODO add default random name, like "Erratic Bunny #1" to a newly created user const [name, setName] = useState(user?.name ?? "No username found"); const [isEditingName, setIsEditingName] = useState(false); + if (!user) { + return <>; + } + const onNameChange = (event: ChangeEvent): void => { setName(event.target.value); }; + const saveName = (): void => { + axios.put("api/user", { ...user, name }).then(() => setIsEditingName(false)); + }; + return (
- {isEditingName ? ( - setIsEditingName(false)} - /> - ) : ( - - )} +
+
Username
+ {isEditingName ? ( +
+ + +
+ ) : ( + + )} +
+
+
Email
+
{user.email}
+
); }; From ff8fa184245720f74de43639899e46c6e94f820f Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:45:34 +0100 Subject: [PATCH 06/18] minor style change --- frontend/src/components/Profile.tsx | 33 +++++++++++++---------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/Profile.tsx b/frontend/src/components/Profile.tsx index 4e11973..c2fe4fe 100644 --- a/frontend/src/components/Profile.tsx +++ b/frontend/src/components/Profile.tsx @@ -41,24 +41,21 @@ export const Profile: React.FC = ({ user }) => { } return ( -
-
-
-

User Information

- - -

Statistics

- - {statistics.map((statistic) => ( - - ))} -
+
+
+

User Information

+ + +

Statistics

+ {statistics.map((statistic) => ( + + ))}
); From 7e10a235a8740dde67d4cc2db862d0f31b06fc48 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Mon, 12 Feb 2024 17:47:28 +0100 Subject: [PATCH 07/18] minor style change --- frontend/src/components/Profile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Profile.tsx b/frontend/src/components/Profile.tsx index c2fe4fe..539305b 100644 --- a/frontend/src/components/Profile.tsx +++ b/frontend/src/components/Profile.tsx @@ -42,7 +42,7 @@ export const Profile: React.FC = ({ user }) => { return (
-
+

User Information

From f54cb93fbd3219b5746327982bb7e545651004da Mon Sep 17 00:00:00 2001 From: paulkreft Date: Tue, 27 Feb 2024 10:30:28 +0100 Subject: [PATCH 08/18] fix wrong stopwatch display --- frontend/src/components/StopWatch.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/StopWatch.tsx b/frontend/src/components/StopWatch.tsx index c16c68a..4951148 100644 --- a/frontend/src/components/StopWatch.tsx +++ b/frontend/src/components/StopWatch.tsx @@ -15,11 +15,11 @@ export const StopWatch: React.FC = ({ className, value }) => { return; } const minutes: number = Math.floor(value / 60000); - const seconds: number = (value % 60000) / 1000; + const seconds: number = Math.floor((value % 60000) / 1000); const centiseconds: number = Math.floor((value % 1000) / 10); const minuteString = minutes >= 10 ? minutes.toString() : "0" + minutes.toString(); - const secondString = seconds >= 10 ? seconds.toFixed(0) : "0" + seconds.toFixed(0); + const secondString = seconds >= 10 ? seconds.toString() : "0" + seconds.toString(); const millisecondString = centiseconds >= 10 ? centiseconds.toString() : "0" + centiseconds.toString(); setDisplayTime(`${minuteString}:${secondString}:${millisecondString}`); From a079187ac386956b53bc602408acabc80a7fa445 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Tue, 27 Feb 2024 12:29:15 +0100 Subject: [PATCH 09/18] remove border on mobile screen containers --- frontend/src/components/EmailLogin.tsx | 19 +++++++------------ frontend/src/components/EmailSignUp.tsx | 2 +- frontend/src/components/MultiplayerLobby.tsx | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/EmailLogin.tsx b/frontend/src/components/EmailLogin.tsx index f364f50..aa0e8e9 100644 --- a/frontend/src/components/EmailLogin.tsx +++ b/frontend/src/components/EmailLogin.tsx @@ -1,6 +1,6 @@ import React, { FormEvent, useEffect, useState } from "react"; import { cn } from "../lib/utils.ts"; -import {Spinner} from "./Spinner.tsx"; +import { Spinner } from "./Spinner.tsx"; type EmailLoginProps = { login: (email: string, password: string) => void; @@ -33,15 +33,15 @@ export const EmailLogin: React.FC = ({ login }) => { if (isLoggingIn) { return ( -
- -
+
+ +
); } return (
-
+

Log in with Email

Email
@@ -56,9 +56,7 @@ export const EmailLogin: React.FC = ({ login }) => { onChange={(event) => setEmail(event.target.value)} required /> -
+
Invalid email format
@@ -75,10 +73,7 @@ export const EmailLogin: React.FC = ({ login }) => { required />
Invalid password format
diff --git a/frontend/src/components/EmailSignUp.tsx b/frontend/src/components/EmailSignUp.tsx index 67b1405..f512504 100644 --- a/frontend/src/components/EmailSignUp.tsx +++ b/frontend/src/components/EmailSignUp.tsx @@ -48,7 +48,7 @@ export const EmailSignUp: React.FC = ({ login }) => { return (
-
+

Sign up with Email

Email
diff --git a/frontend/src/components/MultiplayerLobby.tsx b/frontend/src/components/MultiplayerLobby.tsx index 9bb38d7..f89a63d 100644 --- a/frontend/src/components/MultiplayerLobby.tsx +++ b/frontend/src/components/MultiplayerLobby.tsx @@ -50,7 +50,7 @@ export const MultiplayerLobby: React.FC = ({ return (
-
+
{`${lobby.host.name.substring(0, 15)}'s lobby`}
= ({ Date: Tue, 27 Feb 2024 12:29:28 +0100 Subject: [PATCH 10/18] reformat --- frontend/src/App.css | 12 +++ frontend/src/components/Button.tsx | 2 +- frontend/src/components/Home.tsx | 2 +- .../components/MultiplayerGameOverScreen.tsx | 79 ++++++++++--------- frontend/src/components/Tile.tsx | 4 +- frontend/src/components/TileConfiguration.tsx | 4 +- frontend/src/index.css | 2 +- frontend/src/lib/hexUtils.ts | 62 +++++++-------- frontend/src/lib/utils.ts | 8 +- frontend/src/types/DuelStatistics.ts | 8 +- frontend/src/types/Lobby.ts | 2 +- frontend/src/types/ScoreMap.ts | 8 +- 12 files changed, 106 insertions(+), 87 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 07529f0..f59cf1d 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,3 +1,15 @@ input:focus { outline: none; +} + +/* Chrome, Safari, Edge, Opera */ +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Firefox */ +input[type=number] { + -moz-appearance: textfield; } \ No newline at end of file diff --git a/frontend/src/components/Button.tsx b/frontend/src/components/Button.tsx index b49932a..5731a40 100644 --- a/frontend/src/components/Button.tsx +++ b/frontend/src/components/Button.tsx @@ -11,7 +11,7 @@ type ButtonProps = { export const Button: React.FC = ({ children, color, isActive, onClick }) => { return ( diff --git a/frontend/src/components/MultiplayerGameOverScreen.tsx b/frontend/src/components/MultiplayerGameOverScreen.tsx index 22d65fe..f8520f9 100644 --- a/frontend/src/components/MultiplayerGameOverScreen.tsx +++ b/frontend/src/components/MultiplayerGameOverScreen.tsx @@ -1,43 +1,48 @@ import React from "react"; -import {Spinner} from "./Spinner.tsx"; -import {Lobby} from "../types/Lobby.ts"; -import {Player} from "../types/Player.ts"; +import { Spinner } from "./Spinner.tsx"; +import { Lobby } from "../types/Lobby.ts"; +import { Player } from "../types/Player.ts"; type MultiplayerGameOverScreenProps = { - lobby: Lobby; - player: Player; - startGame: () => void; - backToLobby: () => void; -} + lobby: Lobby; + player: Player; + startGame: () => void; + backToLobby: () => void; +}; -export const MultiplayerGameOverScreen: React.FC = ({lobby, player, startGame, backToLobby}) => { - return ( -
-
- {lobby.winner?.name} won! -
+export const MultiplayerGameOverScreen: React.FC = ({ + lobby, + player, + startGame, + backToLobby, +}) => { + return ( +
+
+ {lobby.winner?.name} won! +
-
- in {((lobby.timeToBeat ?? 0) / 1000).toFixed(3)} seconds -
- {lobby.host.id === player.id ? ( - - ) : ( -
- -
- )} - +
+ in {((lobby.timeToBeat ?? 0) / 1000).toFixed(3)} seconds +
+ {lobby.host.id === player.id ? ( + + ) : ( +
+
- ); -} \ No newline at end of file + )} + +
+ ); +}; diff --git a/frontend/src/components/Tile.tsx b/frontend/src/components/Tile.tsx index b36d8a2..f521617 100644 --- a/frontend/src/components/Tile.tsx +++ b/frontend/src/components/Tile.tsx @@ -14,13 +14,13 @@ export const Tile: React.FC = ({ color, isClickable, isSelected, isWr return ( -
) : ( )}
-
-
Email
+
+
Email
{user.email}
diff --git a/frontend/src/components/Statistic.tsx b/frontend/src/components/Statistic.tsx index 4a0e2fa..d8ee458 100644 --- a/frontend/src/components/Statistic.tsx +++ b/frontend/src/components/Statistic.tsx @@ -10,7 +10,7 @@ type StatisticProps = { export const Statistic: React.FC = ({ name, easy, medium, hard }) => { return (
-
{name}
+
{name}
{easy?.toFixed() || "N/A"} {medium?.toFixed() || "N/A"} From f8aab3e65c8e0edf4a4c87343c4c6ae4a5944ff9 Mon Sep 17 00:00:00 2001 From: paulkreft Date: Tue, 27 Feb 2024 17:40:53 +0100 Subject: [PATCH 14/18] fix checkmark not turning white while hovering parent's border --- frontend/src/assets/checkmark.svg | 15 ++++++++++----- frontend/src/components/ProfileUserInfo.tsx | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/assets/checkmark.svg b/frontend/src/assets/checkmark.svg index 6213b79..47f1f54 100644 --- a/frontend/src/assets/checkmark.svg +++ b/frontend/src/assets/checkmark.svg @@ -1,7 +1,12 @@ - + - - - - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/ProfileUserInfo.tsx b/frontend/src/components/ProfileUserInfo.tsx index 0964541..242b62f 100644 --- a/frontend/src/components/ProfileUserInfo.tsx +++ b/frontend/src/components/ProfileUserInfo.tsx @@ -35,7 +35,7 @@ export const ProfileUserInfo: React.FC = ({ className, use {isEditingName ? (
@@ -53,7 +60,7 @@ export const ProfileUserInfo: React.FC = ({ className, use )}
-
+
Email
{user.email}
From 347261651fc04d3613233a4f5b597c7e1c908c4c Mon Sep 17 00:00:00 2001 From: paulkreft Date: Wed, 28 Feb 2024 10:28:23 +0100 Subject: [PATCH 17/18] ensuring minmax length of username --- .../user/controller/UserControllerIntegrationTest.java | 6 +++--- .../paulkreft/backend/user/service/UserServiceTest.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/test/java/de/neuefische/paulkreft/backend/user/controller/UserControllerIntegrationTest.java b/backend/src/test/java/de/neuefische/paulkreft/backend/user/controller/UserControllerIntegrationTest.java index 24d9f74..a385888 100644 --- a/backend/src/test/java/de/neuefische/paulkreft/backend/user/controller/UserControllerIntegrationTest.java +++ b/backend/src/test/java/de/neuefische/paulkreft/backend/user/controller/UserControllerIntegrationTest.java @@ -50,7 +50,7 @@ class UserControllerIntegrationTest { @BeforeEach public void instantiateTestUser() { Instant now = Instant.parse("2016-06-09T00:00:00.00Z"); - testUser = new User("123", "Paul", "testemail@at.de", "", now, now); + testUser = new User("123", "Paule", "testemail@at.de", "", now, now); } @Test @@ -81,7 +81,7 @@ void testGetUser_whenReturningUserIsLoggedIn_returnReturningUser() throws Except { "id":"123", "email":"testemail@at.de", - "name":"Paul", + "name":"Paule", "lastActive": "2016-06-09T00:00:00Z", "createdAt": "2016-06-09T00:00:00Z" } @@ -178,7 +178,7 @@ void testGetUser_whenReturningEmailUserIsLoggedIn_returnReturningUser() throws E .andExpect(content().json( """ {"id":"123", - "name":"Paul", + "name":"Paule", "email":"testemail@at.de", "lastActive":"2016-06-09T00:00:00Z", "createdAt":"2016-06-09T00:00:00Z" diff --git a/backend/src/test/java/de/neuefische/paulkreft/backend/user/service/UserServiceTest.java b/backend/src/test/java/de/neuefische/paulkreft/backend/user/service/UserServiceTest.java index 8d2c8d2..c1f3109 100644 --- a/backend/src/test/java/de/neuefische/paulkreft/backend/user/service/UserServiceTest.java +++ b/backend/src/test/java/de/neuefische/paulkreft/backend/user/service/UserServiceTest.java @@ -1,6 +1,5 @@ package de.neuefische.paulkreft.backend.user.service; -import de.neuefische.paulkreft.backend.exception.GithubEmailNotFoundException; import de.neuefische.paulkreft.backend.github.service.GithubService; import de.neuefische.paulkreft.backend.statistic.service.StatisticService; import de.neuefische.paulkreft.backend.utils.service.IdService; From 1ff950af9e1e22707793eb6e771189d322bdf77c Mon Sep 17 00:00:00 2001 From: paulkreft Date: Wed, 28 Feb 2024 18:26:55 +0100 Subject: [PATCH 18/18] refactor fallback away from GlobalExceptionHandler --- .../exception/GlobalExceptionHandler.java | 15 ------ .../paulkreft/backend/fallback/Fallback.java | 39 ++++++++++++++ .../exception/GlobalExceptionHandlerTest.java | 46 ---------------- .../backend/fallback/FallbackTest.java | 53 +++++++++++++++++++ 4 files changed, 92 insertions(+), 61 deletions(-) create mode 100644 backend/src/main/java/de/neuefische/paulkreft/backend/fallback/Fallback.java delete mode 100644 backend/src/test/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandlerTest.java create mode 100644 backend/src/test/java/de/neuefische/paulkreft/backend/fallback/FallbackTest.java diff --git a/backend/src/main/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandler.java b/backend/src/main/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandler.java index 96d69c3..5a9a42b 100644 --- a/backend/src/main/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandler.java @@ -1,29 +1,14 @@ package de.neuefische.paulkreft.backend.exception; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.resource.NoResourceFoundException; @ControllerAdvice public class GlobalExceptionHandler { - private final String environment; - - public GlobalExceptionHandler(@Value("${app.environment}") String environment) { - this.environment = environment; - } - - @ExceptionHandler({NoResourceFoundException.class}) - public ModelAndView noResourceFoundHandler(NoResourceFoundException e) { - String basePath = environment.equals("production") ? "/" : "http://localhost:5173/"; - String route = environment.equals("production") ? "" : e.getResourcePath(); - return new ModelAndView("redirect:" + basePath + route); - } @ExceptionHandler({EmailAlreadyRegisteredException.class}) @ResponseStatus(HttpStatus.CONFLICT) diff --git a/backend/src/main/java/de/neuefische/paulkreft/backend/fallback/Fallback.java b/backend/src/main/java/de/neuefische/paulkreft/backend/fallback/Fallback.java new file mode 100644 index 0000000..a336e41 --- /dev/null +++ b/backend/src/main/java/de/neuefische/paulkreft/backend/fallback/Fallback.java @@ -0,0 +1,39 @@ +package de.neuefische.paulkreft.backend.fallback; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.PathResourceResolver; + +import java.io.IOException; + +@Configuration +public class Fallback implements WebMvcConfigurer { + + public static final String DEFAULT_STARTING_PAGE = "static/index.html"; + + static class ReactRoutingPathResourceResolver extends PathResourceResolver { + @Override + protected Resource getResource(String resourcePath, Resource location) throws IOException { + var requestedResource = location.createRelative(resourcePath); + + // Is this a request to a real file? + if (requestedResource.exists() && requestedResource.isReadable()) { + return requestedResource; + } + + // It seems to be only a frontend-routing request (Single-Page-Application). + return new ClassPathResource(DEFAULT_STARTING_PAGE); + } + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**") + .addResourceLocations("classpath:/static/") + .resourceChain(true) + .addResolver(new ReactRoutingPathResourceResolver()); + } +} diff --git a/backend/src/test/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandlerTest.java b/backend/src/test/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandlerTest.java deleted file mode 100644 index 2738666..0000000 --- a/backend/src/test/java/de/neuefische/paulkreft/backend/exception/GlobalExceptionHandlerTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.neuefische.paulkreft.backend.exception; - -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpMethod; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.resource.NoResourceFoundException; - -import static org.junit.jupiter.api.Assertions.assertEquals; - - -class GlobalExceptionHandlerTest { - - @Test - void noResourceFoundHandlerTest_whenEnvDevelopment_returnLocalhostPlusPath() { - // Given - String basePath = "http://localhost:5173/"; - String path = "test"; - String env = "development"; - NoResourceFoundException exception = new NoResourceFoundException(HttpMethod.GET, path); - GlobalExceptionHandler handler = new GlobalExceptionHandler(env); - - // When - ModelAndView expected = new ModelAndView("redirect:" + basePath + path); - ModelAndView actual = handler.noResourceFoundHandler(exception); - - // Then - assertEquals(expected.toString(), actual.toString()); - } - - @Test - void noResourceFoundHandlerTest_whenEnvProduction_returnRelativePath() { - // Given - String basePath = "/"; - String path = "test"; - String env = "production"; - NoResourceFoundException exception = new NoResourceFoundException(HttpMethod.GET, path); - GlobalExceptionHandler handler = new GlobalExceptionHandler(env); - - // When - ModelAndView expected = new ModelAndView("redirect:" + basePath); - ModelAndView actual = handler.noResourceFoundHandler(exception); - - // Then - assertEquals(expected.toString(), actual.toString()); - } -} \ No newline at end of file diff --git a/backend/src/test/java/de/neuefische/paulkreft/backend/fallback/FallbackTest.java b/backend/src/test/java/de/neuefische/paulkreft/backend/fallback/FallbackTest.java new file mode 100644 index 0000000..689e02d --- /dev/null +++ b/backend/src/test/java/de/neuefische/paulkreft/backend/fallback/FallbackTest.java @@ -0,0 +1,53 @@ +package de.neuefische.paulkreft.backend.fallback; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import java.io.IOException; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class FallbackTest { + + @Test + void expectRelativeResource_ifItExists() throws IOException { + + // GIVEN + var resolver = new Fallback.ReactRoutingPathResourceResolver(); + var location = mock(Resource.class); + var relativeLocation = mock(Resource.class); + var resourcePath = "index.html"; + when(location.createRelative(resourcePath)).thenReturn(relativeLocation); + when(relativeLocation.exists()).thenReturn(true); + when(relativeLocation.isReadable()).thenReturn(true); + + // WHEN + var actual = resolver.getResource(resourcePath, location); + + // THEN + Assertions.assertEquals(relativeLocation, actual); + } + + @Test + void expectIndexHtml_ifRequestedResourceDoesNotExist() throws IOException { + + // GIVEN + var resolver = new Fallback.ReactRoutingPathResourceResolver(); + var location = mock(Resource.class); + var relativeLocation = mock(Resource.class); + var resourcePath = "index.html"; + when(location.createRelative(resourcePath)).thenReturn(relativeLocation); + when(relativeLocation.exists()).thenReturn(false); + + // WHEN + var actual = resolver.getResource(resourcePath, location); + + // THEN + ClassPathResource expected = new ClassPathResource("static/index.html"); + Assertions.assertEquals(expected, actual); + } + +} \ No newline at end of file