diff --git a/site/components/SignIn/SignInForm.test.tsx b/site/components/SignIn/SignInForm.test.tsx index 0f1797de4b606..1306fd43fa0e0 100644 --- a/site/components/SignIn/SignInForm.test.tsx +++ b/site/components/SignIn/SignInForm.test.tsx @@ -55,7 +55,29 @@ describe("SignInForm", () => { act(() => elem.click()) // Then - // Should redirect because login was successfully + // Should redirect because login was successful await waitFor(() => expect(singletonRouter).toMatchObject({ asPath: "/" })) }) + + it("respects ?redirect query parameter when complete", async () => { + // Given + const loginHandler = (_email: string, _password: string) => Promise.resolve() + // Set a path to redirect to after login is successful + mockRouter.setCurrentUrl("/login?redirect=%2Fsome%2Fother%2Fpath") + + // When + // Render the component + const { container } = render() + // Set user / password + const inputs = container.querySelectorAll("input") + fireEvent.change(inputs[0], { target: { value: "test@coder.com" } }) + fireEvent.change(inputs[1], { target: { value: "password" } }) + // Click sign-in + const elem = await screen.findByText("Sign In") + act(() => elem.click()) + + // Then + // Should redirect to /some/other/path because ?redirect was specified and login was successful + await waitFor(() => expect(singletonRouter).toMatchObject({ asPath: "/some/other/path" })) + }) }) diff --git a/site/components/SignIn/SignInForm.tsx b/site/components/SignIn/SignInForm.tsx index a456cf57ed56e..fdb0e0c43ae3f 100644 --- a/site/components/SignIn/SignInForm.tsx +++ b/site/components/SignIn/SignInForm.tsx @@ -1,6 +1,6 @@ import { makeStyles } from "@material-ui/core/styles" import { FormikContextType, useFormik } from "formik" -import { useRouter } from "next/router" +import { NextRouter, useRouter } from "next/router" import React from "react" import { useSWRConfig } from "swr" import * as Yup from "yup" @@ -9,6 +9,7 @@ import { Welcome } from "./Welcome" import { FormTextField } from "../Form" import * as API from "./../../api" import { LoadingButton } from "./../Button" +import { firstOrItem } from "../../util/array" /** * BuiltInAuthFormValues describes a form using built-in (email/password) @@ -61,7 +62,9 @@ export const SignInForm: React.FC = ({ await loginHandler(email, password) // Tell SWR to invalidate the cache for the user endpoint await mutate("/api/v2/users/me") - await router.push("/") + + const redirect = getRedirectFromRouter(router) + await router.push(redirect) } catch (err) { helpers.setFieldError("password", "The username or password is incorrect.") } @@ -117,3 +120,12 @@ export const SignInForm: React.FC = ({ ) } + +const getRedirectFromRouter = (router: NextRouter) => { + const defaultRedirect = "/" + if (router.query?.redirect) { + return firstOrItem(router.query.redirect, defaultRedirect) + } else { + return defaultRedirect + } +}