Skip to content

Commit 9e9a9e0

Browse files
fix: Setup redirect (#4064)
1 parent 40c0fc2 commit 9e9a9e0

File tree

7 files changed

+66
-29
lines changed

7 files changed

+66
-29
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { screen } from "@testing-library/react"
2+
import { rest } from "msw"
3+
import { Route } from "react-router-dom"
4+
import { renderWithAuth } from "testHelpers/renderHelpers"
5+
import { server } from "testHelpers/server"
6+
7+
describe("RequireAuth", () => {
8+
it("redirects to /setup if there is no first user", async () => {
9+
// appear logged out
10+
server.use(
11+
rest.get("/api/v2/users/me", (req, res, ctx) => {
12+
return res(ctx.status(401), ctx.json({ message: "no user here" }))
13+
}),
14+
)
15+
// No first user
16+
server.use(
17+
rest.get("/api/v2/users/first", async (req, res, ctx) => {
18+
return res(ctx.status(404))
19+
}),
20+
)
21+
22+
renderWithAuth(<h1>Test</h1>, {
23+
routes: <Route path="setup" element={<h1>Setup</h1>} />,
24+
})
25+
26+
await screen.findByText("Setup")
27+
})
28+
})

site/src/components/RequireAuth/RequireAuth.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ export const RequireAuth: React.FC<React.PropsWithChildren<RequireAuthProps>> =
1515
const location = useLocation()
1616
const isHomePage = location.pathname === "/"
1717
const navigateTo = isHomePage ? "/login" : embedRedirect(location.pathname)
18+
1819
if (authState.matches("signedOut")) {
1920
return <Navigate to={navigateTo} state={{ isRedirect: !isHomePage }} />
21+
} else if (authState.matches("waitingForTheFirstUser")) {
22+
return <Navigate to="/setup" />
2023
} else if (authState.hasTag("loading")) {
2124
return <FullScreenLoader />
2225
} else {

site/src/pages/LoginPage/LoginPage.test.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { fireEvent, screen, waitFor } from "@testing-library/react"
1+
import { fireEvent, screen } from "@testing-library/react"
22
import userEvent from "@testing-library/user-event"
33
import { rest } from "msw"
4+
import { Route, Routes } from "react-router-dom"
45
import { Language } from "../../components/SignInForm/SignInForm"
5-
import { history, render } from "../../testHelpers/renderHelpers"
6+
import { history, render, waitForLoaderToBeRemoved } from "../../testHelpers/renderHelpers"
67
import { server } from "../../testHelpers/server"
78
import { LoginPage } from "./LoginPage"
89

@@ -36,6 +37,7 @@ describe("LoginPage", () => {
3637

3738
// When
3839
render(<LoginPage />)
40+
await waitForLoaderToBeRemoved()
3941
const email = screen.getByLabelText(Language.emailLabel)
4042
const password = screen.getByLabelText(Language.passwordLabel)
4143
await userEvent.type(email, "test@coder.com")
@@ -99,9 +101,14 @@ describe("LoginPage", () => {
99101
)
100102

101103
// When
102-
render(<LoginPage />)
104+
render(
105+
<Routes>
106+
<Route path="/login" element={<LoginPage />}></Route>
107+
<Route path="/setup" element={<h1>Setup</h1>}></Route>
108+
</Routes>,
109+
)
103110

104111
// Then
105-
await waitFor(() => expect(history.location.pathname).toEqual("/setup"))
112+
await screen.findByText("Setup")
106113
})
107114
})

site/src/pages/LoginPage/LoginPage.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useActor } from "@xstate/react"
2+
import { FullScreenLoader } from "components/Loader/FullScreenLoader"
23
import { SignInLayout } from "components/SignInLayout/SignInLayout"
34
import React, { useContext } from "react"
45
import { Helmet } from "react-helmet-async"
@@ -28,26 +29,32 @@ export const LoginPage: React.FC = () => {
2829

2930
if (authState.matches("signedIn")) {
3031
return <Navigate to={redirectTo} replace />
32+
} else if (authState.matches("waitingForTheFirstUser")) {
33+
return <Navigate to="/setup" />
3134
} else {
3235
return (
3336
<>
3437
<Helmet>
3538
<title>{pageTitle("Login")}</title>
3639
</Helmet>
37-
<SignInLayout>
38-
<SignInForm
39-
authMethods={authState.context.methods}
40-
redirectTo={redirectTo}
41-
isLoading={isLoading}
42-
loginErrors={{
43-
[LoginErrors.AUTH_ERROR]: authError,
44-
[LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null,
45-
[LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError,
46-
[LoginErrors.GET_METHODS_ERROR]: getMethodsError,
47-
}}
48-
onSubmit={onSubmit}
49-
/>
50-
</SignInLayout>
40+
{authState.hasTag("loading") ? (
41+
<FullScreenLoader />
42+
) : (
43+
<SignInLayout>
44+
<SignInForm
45+
authMethods={authState.context.methods}
46+
redirectTo={redirectTo}
47+
isLoading={isLoading}
48+
loginErrors={{
49+
[LoginErrors.AUTH_ERROR]: authError,
50+
[LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null,
51+
[LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError,
52+
[LoginErrors.GET_METHODS_ERROR]: getMethodsError,
53+
}}
54+
onSubmit={onSubmit}
55+
/>
56+
</SignInLayout>
57+
)}
5158
</>
5259
)
5360
}

site/src/testHelpers/renderHelpers.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type RenderWithAuthResult = RenderResult & { user: typeof MockUser }
4949
*/
5050
export function renderWithAuth(
5151
ui: JSX.Element,
52-
{ route = "/", path }: { route?: string; path?: string } = {},
52+
{ route = "/", path, routes }: { route?: string; path?: string; routes?: JSX.Element } = {},
5353
): RenderWithAuthResult {
5454
const renderResult = wrappedRender(
5555
<HelmetProvider>
@@ -59,6 +59,7 @@ export function renderWithAuth(
5959
<ThemeProvider theme={dark}>
6060
<Routes>
6161
<Route path={path ?? route} element={<RequireAuth>{ui}</RequireAuth>} />
62+
{routes}
6263
</Routes>
6364
</ThemeProvider>
6465
</I18nextProvider>

site/src/xServices/StateContext.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useInterpret } from "@xstate/react"
22
import { createContext, FC, ReactNode } from "react"
3-
import { useNavigate } from "react-router"
43
import { ActorRefFrom } from "xstate"
54
import { authMachine } from "./auth/authXService"
65
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
@@ -25,17 +24,10 @@ interface XServiceContextType {
2524
export const XServiceContext = createContext({} as XServiceContextType)
2625

2726
export const XServiceProvider: FC<{ children: ReactNode }> = ({ children }) => {
28-
const navigate = useNavigate()
29-
const redirectToSetupPage = () => {
30-
navigate("setup")
31-
}
32-
3327
return (
3428
<XServiceContext.Provider
3529
value={{
36-
authXService: useInterpret(() =>
37-
authMachine.withConfig({ actions: { redirectToSetupPage } }),
38-
),
30+
authXService: useInterpret(authMachine),
3931
buildInfoXService: useInterpret(buildInfoMachine),
4032
entitlementsXService: useInterpret(entitlementsMachine),
4133
siteRolesXService: useInterpret(siteRolesMachine),

site/src/xServices/auth/authXService.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,6 @@ export const authMachine =
408408
tags: "loading",
409409
},
410410
waitingForTheFirstUser: {
411-
entry: "redirectToSetupPage",
412411
on: {
413412
SIGN_IN: {
414413
target: "signingIn",

0 commit comments

Comments
 (0)