mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
fix: Setup redirect (#4064)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { screen } from "@testing-library/react"
|
||||
import { rest } from "msw"
|
||||
import { Route } from "react-router-dom"
|
||||
import { renderWithAuth } from "testHelpers/renderHelpers"
|
||||
import { server } from "testHelpers/server"
|
||||
|
||||
describe("RequireAuth", () => {
|
||||
it("redirects to /setup if there is no first user", async () => {
|
||||
// appear logged out
|
||||
server.use(
|
||||
rest.get("/api/v2/users/me", (req, res, ctx) => {
|
||||
return res(ctx.status(401), ctx.json({ message: "no user here" }))
|
||||
}),
|
||||
)
|
||||
// No first user
|
||||
server.use(
|
||||
rest.get("/api/v2/users/first", async (req, res, ctx) => {
|
||||
return res(ctx.status(404))
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithAuth(<h1>Test</h1>, {
|
||||
routes: <Route path="setup" element={<h1>Setup</h1>} />,
|
||||
})
|
||||
|
||||
await screen.findByText("Setup")
|
||||
})
|
||||
})
|
||||
@@ -15,8 +15,11 @@ export const RequireAuth: React.FC<React.PropsWithChildren<RequireAuthProps>> =
|
||||
const location = useLocation()
|
||||
const isHomePage = location.pathname === "/"
|
||||
const navigateTo = isHomePage ? "/login" : embedRedirect(location.pathname)
|
||||
|
||||
if (authState.matches("signedOut")) {
|
||||
return <Navigate to={navigateTo} state={{ isRedirect: !isHomePage }} />
|
||||
} else if (authState.matches("waitingForTheFirstUser")) {
|
||||
return <Navigate to="/setup" />
|
||||
} else if (authState.hasTag("loading")) {
|
||||
return <FullScreenLoader />
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react"
|
||||
import { fireEvent, screen } from "@testing-library/react"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { rest } from "msw"
|
||||
import { Route, Routes } from "react-router-dom"
|
||||
import { Language } from "../../components/SignInForm/SignInForm"
|
||||
import { history, render } from "../../testHelpers/renderHelpers"
|
||||
import { history, render, waitForLoaderToBeRemoved } from "../../testHelpers/renderHelpers"
|
||||
import { server } from "../../testHelpers/server"
|
||||
import { LoginPage } from "./LoginPage"
|
||||
|
||||
@@ -36,6 +37,7 @@ describe("LoginPage", () => {
|
||||
|
||||
// When
|
||||
render(<LoginPage />)
|
||||
await waitForLoaderToBeRemoved()
|
||||
const email = screen.getByLabelText(Language.emailLabel)
|
||||
const password = screen.getByLabelText(Language.passwordLabel)
|
||||
await userEvent.type(email, "test@coder.com")
|
||||
@@ -99,9 +101,14 @@ describe("LoginPage", () => {
|
||||
)
|
||||
|
||||
// When
|
||||
render(<LoginPage />)
|
||||
render(
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />}></Route>
|
||||
<Route path="/setup" element={<h1>Setup</h1>}></Route>
|
||||
</Routes>,
|
||||
)
|
||||
|
||||
// Then
|
||||
await waitFor(() => expect(history.location.pathname).toEqual("/setup"))
|
||||
await screen.findByText("Setup")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useActor } from "@xstate/react"
|
||||
import { FullScreenLoader } from "components/Loader/FullScreenLoader"
|
||||
import { SignInLayout } from "components/SignInLayout/SignInLayout"
|
||||
import React, { useContext } from "react"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
@@ -28,26 +29,32 @@ export const LoginPage: React.FC = () => {
|
||||
|
||||
if (authState.matches("signedIn")) {
|
||||
return <Navigate to={redirectTo} replace />
|
||||
} else if (authState.matches("waitingForTheFirstUser")) {
|
||||
return <Navigate to="/setup" />
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{pageTitle("Login")}</title>
|
||||
</Helmet>
|
||||
<SignInLayout>
|
||||
<SignInForm
|
||||
authMethods={authState.context.methods}
|
||||
redirectTo={redirectTo}
|
||||
isLoading={isLoading}
|
||||
loginErrors={{
|
||||
[LoginErrors.AUTH_ERROR]: authError,
|
||||
[LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null,
|
||||
[LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError,
|
||||
[LoginErrors.GET_METHODS_ERROR]: getMethodsError,
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</SignInLayout>
|
||||
{authState.hasTag("loading") ? (
|
||||
<FullScreenLoader />
|
||||
) : (
|
||||
<SignInLayout>
|
||||
<SignInForm
|
||||
authMethods={authState.context.methods}
|
||||
redirectTo={redirectTo}
|
||||
isLoading={isLoading}
|
||||
loginErrors={{
|
||||
[LoginErrors.AUTH_ERROR]: authError,
|
||||
[LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null,
|
||||
[LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError,
|
||||
[LoginErrors.GET_METHODS_ERROR]: getMethodsError,
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</SignInLayout>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ type RenderWithAuthResult = RenderResult & { user: typeof MockUser }
|
||||
*/
|
||||
export function renderWithAuth(
|
||||
ui: JSX.Element,
|
||||
{ route = "/", path }: { route?: string; path?: string } = {},
|
||||
{ route = "/", path, routes }: { route?: string; path?: string; routes?: JSX.Element } = {},
|
||||
): RenderWithAuthResult {
|
||||
const renderResult = wrappedRender(
|
||||
<HelmetProvider>
|
||||
@@ -59,6 +59,7 @@ export function renderWithAuth(
|
||||
<ThemeProvider theme={dark}>
|
||||
<Routes>
|
||||
<Route path={path ?? route} element={<RequireAuth>{ui}</RequireAuth>} />
|
||||
{routes}
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
</I18nextProvider>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useInterpret } from "@xstate/react"
|
||||
import { createContext, FC, ReactNode } from "react"
|
||||
import { useNavigate } from "react-router"
|
||||
import { ActorRefFrom } from "xstate"
|
||||
import { authMachine } from "./auth/authXService"
|
||||
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
|
||||
@@ -25,17 +24,10 @@ interface XServiceContextType {
|
||||
export const XServiceContext = createContext({} as XServiceContextType)
|
||||
|
||||
export const XServiceProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const navigate = useNavigate()
|
||||
const redirectToSetupPage = () => {
|
||||
navigate("setup")
|
||||
}
|
||||
|
||||
return (
|
||||
<XServiceContext.Provider
|
||||
value={{
|
||||
authXService: useInterpret(() =>
|
||||
authMachine.withConfig({ actions: { redirectToSetupPage } }),
|
||||
),
|
||||
authXService: useInterpret(authMachine),
|
||||
buildInfoXService: useInterpret(buildInfoMachine),
|
||||
entitlementsXService: useInterpret(entitlementsMachine),
|
||||
siteRolesXService: useInterpret(siteRolesMachine),
|
||||
|
||||
@@ -408,7 +408,6 @@ export const authMachine =
|
||||
tags: "loading",
|
||||
},
|
||||
waitingForTheFirstUser: {
|
||||
entry: "redirectToSetupPage",
|
||||
on: {
|
||||
SIGN_IN: {
|
||||
target: "signingIn",
|
||||
|
||||
Reference in New Issue
Block a user