From a0364793bd942124790b7cd1de63e06faeb37640 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 18:30:20 +0000 Subject: [PATCH 01/11] fix: disable setup page once setup has been completed --- site/src/pages/SetupPage/SetupPage.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/site/src/pages/SetupPage/SetupPage.tsx b/site/src/pages/SetupPage/SetupPage.tsx index 56c9b9b78f5f0..26d13d5aa552a 100644 --- a/site/src/pages/SetupPage/SetupPage.tsx +++ b/site/src/pages/SetupPage/SetupPage.tsx @@ -5,6 +5,7 @@ import { Helmet } from "react-helmet-async" import { pageTitle } from "utils/page" import { setupMachine } from "xServices/setup/setupXService" import { SetupPageView } from "./SetupPageView" +import { useNavigate } from "react-router-dom" export const SetupPage: FC = () => { const [authState, authSend] = useAuth() @@ -23,10 +24,20 @@ export const SetupPage: FC = () => { }, }) const { error } = setupState.context + const navigate = useNavigate() useEffect(() => { + // If the user is logged in, navigate to the app if (authState.matches("signedIn")) { - window.location.assign("/workspaces") + navigate("/", { state: { isRedirect: true } }) + } + + // If we've already completed setup, navigate to the login page + if ( + !authState.matches("loadingInitialAuthData") && + !authState.matches("configuringTheFirstUser") + ) { + navigate("/login", { state: { isRedirect: true } }) } }, [authState]) From 550d74167dc0a7f0b4a6a01a8688df1c147ac849 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 18:55:01 +0000 Subject: [PATCH 02/11] I think I just need to mock this... --- site/src/pages/SetupPage/SetupPage.test.tsx | 6 ++++++ site/src/pages/SetupPage/SetupPage.tsx | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index 7d53fe1dce8c2..3e6590be9cd46 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -35,6 +35,12 @@ describe("Setup Page", () => { rest.get("/api/v2/users/me", (req, res, ctx) => { return res(ctx.status(401), ctx.json({ message: "no user here" })) }), + rest.get("/api/v2/users/first", (req, res, ctx) => { + return res( + ctx.status(404), + ctx.json({ message: "no first user has been created" }), + ) + }), ) }) diff --git a/site/src/pages/SetupPage/SetupPage.tsx b/site/src/pages/SetupPage/SetupPage.tsx index 26d13d5aa552a..384c6b5a577ce 100644 --- a/site/src/pages/SetupPage/SetupPage.tsx +++ b/site/src/pages/SetupPage/SetupPage.tsx @@ -39,7 +39,7 @@ export const SetupPage: FC = () => { ) { navigate("/login", { state: { isRedirect: true } }) } - }, [authState]) + }, [authState, navigate]) return ( <> From 7c8e3e6dafbe59a8b5727167f764441452bff0a8 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 19:08:23 +0000 Subject: [PATCH 03/11] provide router during tests --- site/src/pages/SetupPage/SetupPage.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index 3e6590be9cd46..c750ee811d65e 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { rest } from "msw" -import { render } from "testHelpers/renderHelpers" +import { renderWithRouter } from "testHelpers/renderHelpers" import { server } from "testHelpers/server" import { SetupPage } from "./SetupPage" import { Language as PageViewLanguage } from "./SetupPageView" @@ -45,7 +45,7 @@ describe("Setup Page", () => { }) it("shows validation error message", async () => { - render() + renderWithRouter() await fillForm({ email: "test" }) const errorMessage = await screen.findByText(PageViewLanguage.emailInvalid) expect(errorMessage).toBeDefined() @@ -69,14 +69,14 @@ describe("Setup Page", () => { ) }), ) - render() + renderWithRouter() await fillForm() const errorMessage = await screen.findByText(fieldErrorMessage) expect(errorMessage).toBeDefined() }) it("redirects to workspaces page when success", async () => { - render() + renderWithRouter() // simulates the user will be authenticated server.use( From 3a0b36f738113d2dbe4ea67606e51d5532b9bd4c Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 19:13:08 +0000 Subject: [PATCH 04/11] even better --- site/src/pages/SetupPage/SetupPage.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index c750ee811d65e..b1ab5001226d9 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { rest } from "msw" -import { renderWithRouter } from "testHelpers/renderHelpers" +import { renderWithAuth } from "testHelpers/renderHelpers" import { server } from "testHelpers/server" import { SetupPage } from "./SetupPage" import { Language as PageViewLanguage } from "./SetupPageView" @@ -45,7 +45,7 @@ describe("Setup Page", () => { }) it("shows validation error message", async () => { - renderWithRouter() + renderWithAuth() await fillForm({ email: "test" }) const errorMessage = await screen.findByText(PageViewLanguage.emailInvalid) expect(errorMessage).toBeDefined() @@ -69,14 +69,14 @@ describe("Setup Page", () => { ) }), ) - renderWithRouter() + renderWithAuth() await fillForm() const errorMessage = await screen.findByText(fieldErrorMessage) expect(errorMessage).toBeDefined() }) it("redirects to workspaces page when success", async () => { - renderWithRouter() + renderWithAuth() // simulates the user will be authenticated server.use( From 42ab0823de7f42079dbff1a02b046893af8f4b70 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 20:02:11 +0000 Subject: [PATCH 05/11] wait --- site/src/pages/SetupPage/SetupPage.test.tsx | 26 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index b1ab5001226d9..a546c728acd38 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -16,9 +16,13 @@ const fillForm = async ({ email?: string password?: string } = {}) => { - const usernameField = screen.getByLabelText(PageViewLanguage.usernameLabel) - const emailField = screen.getByLabelText(PageViewLanguage.emailLabel) - const passwordField = screen.getByLabelText(PageViewLanguage.passwordLabel) + const usernameField = await screen.findByLabelText( + PageViewLanguage.usernameLabel, + ) + const emailField = await screen.findByLabelText(PageViewLanguage.emailLabel) + const passwordField = await screen.findByLabelText( + PageViewLanguage.passwordLabel, + ) await userEvent.type(usernameField, username) await userEvent.type(emailField, email) await userEvent.type(passwordField, password) @@ -75,6 +79,19 @@ describe("Setup Page", () => { expect(errorMessage).toBeDefined() }) + it("redirects to login if setup has already completed", async () => { + renderWithAuth() + + // simulates setup having already been completed + server.use( + rest.get("/api/v2/users/first", (req, res, ctx) => { + return res(ctx.status(404), ctx.json({ message: "hooray, you exist!" })) + }), + ) + + await waitFor(() => expect(window.location).toBeAt("/login")) + }) + it("redirects to workspaces page when success", async () => { renderWithAuth() @@ -83,6 +100,9 @@ describe("Setup Page", () => { rest.get("/api/v2/users/me", (req, res, ctx) => { return res(ctx.status(200), ctx.json(MockUser)) }), + rest.get("/api/v2/users/first", (req, res, ctx) => { + return res(ctx.status(404), ctx.json({ message: "hooray, you exist!" })) + }), ) await fillForm() From 089ea8f36171eb470bff37adb666c9906ed099da Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 18 Aug 2023 20:12:51 +0000 Subject: [PATCH 06/11] am I getting warmer? --- site/src/pages/SetupPage/SetupPage.test.tsx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index a546c728acd38..1d62d17fb51c6 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -1,7 +1,10 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { rest } from "msw" -import { renderWithAuth } from "testHelpers/renderHelpers" +import { + renderWithAuth, + waitForLoaderToBeRemoved, +} from "testHelpers/renderHelpers" import { server } from "testHelpers/server" import { SetupPage } from "./SetupPage" import { Language as PageViewLanguage } from "./SetupPageView" @@ -49,7 +52,8 @@ describe("Setup Page", () => { }) it("shows validation error message", async () => { - renderWithAuth() + renderWithAuth(, { route: "/setup", path: "/setup" }) + await waitForLoaderToBeRemoved() await fillForm({ email: "test" }) const errorMessage = await screen.findByText(PageViewLanguage.emailInvalid) expect(errorMessage).toBeDefined() @@ -73,15 +77,15 @@ describe("Setup Page", () => { ) }), ) - renderWithAuth() + + renderWithAuth(, { route: "/setup", path: "/setup" }) + await waitForLoaderToBeRemoved() await fillForm() const errorMessage = await screen.findByText(fieldErrorMessage) expect(errorMessage).toBeDefined() }) it("redirects to login if setup has already completed", async () => { - renderWithAuth() - // simulates setup having already been completed server.use( rest.get("/api/v2/users/first", (req, res, ctx) => { @@ -89,12 +93,12 @@ describe("Setup Page", () => { }), ) + renderWithAuth(, { route: "/setup", path: "/setup" }) + await waitForLoaderToBeRemoved() await waitFor(() => expect(window.location).toBeAt("/login")) }) it("redirects to workspaces page when success", async () => { - renderWithAuth() - // simulates the user will be authenticated server.use( rest.get("/api/v2/users/me", (req, res, ctx) => { @@ -105,6 +109,9 @@ describe("Setup Page", () => { }), ) + renderWithAuth(, { route: "/setup", path: "/setup" }) + await waitForLoaderToBeRemoved() + await fillForm() await waitFor(() => expect(window.location).toBeAt("/workspaces")) }) From 82f08a40dba870cf1e14c6e1761c644c6cdab7fc Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 22 Aug 2023 19:35:00 +0000 Subject: [PATCH 07/11] try a simpler approach --- site/src/pages/SetupPage/SetupPage.test.tsx | 28 ++++++------------- site/src/pages/SetupPage/SetupPage.tsx | 31 ++++++++++----------- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index 1d62d17fb51c6..cf3dba7f584c3 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -1,10 +1,7 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { rest } from "msw" -import { - renderWithAuth, - waitForLoaderToBeRemoved, -} from "testHelpers/renderHelpers" +import { render, waitForLoaderToBeRemoved } from "testHelpers/renderHelpers" import { server } from "testHelpers/server" import { SetupPage } from "./SetupPage" import { Language as PageViewLanguage } from "./SetupPageView" @@ -19,13 +16,9 @@ const fillForm = async ({ email?: string password?: string } = {}) => { - const usernameField = await screen.findByLabelText( - PageViewLanguage.usernameLabel, - ) - const emailField = await screen.findByLabelText(PageViewLanguage.emailLabel) - const passwordField = await screen.findByLabelText( - PageViewLanguage.passwordLabel, - ) + const usernameField = screen.getByLabelText(PageViewLanguage.usernameLabel) + const emailField = screen.getByLabelText(PageViewLanguage.emailLabel) + const passwordField = screen.getByLabelText(PageViewLanguage.passwordLabel) await userEvent.type(usernameField, username) await userEvent.type(emailField, email) await userEvent.type(passwordField, password) @@ -52,8 +45,7 @@ describe("Setup Page", () => { }) it("shows validation error message", async () => { - renderWithAuth(, { route: "/setup", path: "/setup" }) - await waitForLoaderToBeRemoved() + render() await fillForm({ email: "test" }) const errorMessage = await screen.findByText(PageViewLanguage.emailInvalid) expect(errorMessage).toBeDefined() @@ -78,8 +70,7 @@ describe("Setup Page", () => { }), ) - renderWithAuth(, { route: "/setup", path: "/setup" }) - await waitForLoaderToBeRemoved() + render() await fillForm() const errorMessage = await screen.findByText(fieldErrorMessage) expect(errorMessage).toBeDefined() @@ -93,7 +84,7 @@ describe("Setup Page", () => { }), ) - renderWithAuth(, { route: "/setup", path: "/setup" }) + render() await waitForLoaderToBeRemoved() await waitFor(() => expect(window.location).toBeAt("/login")) }) @@ -109,10 +100,9 @@ describe("Setup Page", () => { }), ) - renderWithAuth(, { route: "/setup", path: "/setup" }) - await waitForLoaderToBeRemoved() - + render() await fillForm() + await waitForLoaderToBeRemoved() await waitFor(() => expect(window.location).toBeAt("/workspaces")) }) }) diff --git a/site/src/pages/SetupPage/SetupPage.tsx b/site/src/pages/SetupPage/SetupPage.tsx index 384c6b5a577ce..17aef0f3188a0 100644 --- a/site/src/pages/SetupPage/SetupPage.tsx +++ b/site/src/pages/SetupPage/SetupPage.tsx @@ -1,11 +1,11 @@ import { useMachine } from "@xstate/react" import { useAuth } from "components/AuthProvider/AuthProvider" -import { FC, useEffect } from "react" +import { FC } from "react" import { Helmet } from "react-helmet-async" import { pageTitle } from "utils/page" import { setupMachine } from "xServices/setup/setupXService" import { SetupPageView } from "./SetupPageView" -import { useNavigate } from "react-router-dom" +import { Navigate } from "react-router-dom" export const SetupPage: FC = () => { const [authState, authSend] = useAuth() @@ -24,22 +24,21 @@ export const SetupPage: FC = () => { }, }) const { error } = setupState.context - const navigate = useNavigate() - useEffect(() => { - // If the user is logged in, navigate to the app - if (authState.matches("signedIn")) { - navigate("/", { state: { isRedirect: true } }) - } + const userIsSignedIn = authState.matches("signedIn") + const setupIsComplete = + !authState.matches("loadingInitialAuthData") && + !authState.matches("configuringTheFirstUser") - // If we've already completed setup, navigate to the login page - if ( - !authState.matches("loadingInitialAuthData") && - !authState.matches("configuringTheFirstUser") - ) { - navigate("/login", { state: { isRedirect: true } }) - } - }, [authState, navigate]) + // If the user is logged in, navigate to the app + if (userIsSignedIn) { + return + } + + // If we've already completed setup, navigate to the login page + if (setupIsComplete) { + return + } return ( <> From d52b6b7560038385eef68427640203398f7d3d46 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 22 Aug 2023 19:48:55 +0000 Subject: [PATCH 08/11] this is still failing but I just need to see a diff --- site/src/pages/SetupPage/SetupPage.test.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index cf3dba7f584c3..667506b737062 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -80,12 +80,14 @@ describe("Setup Page", () => { // simulates setup having already been completed server.use( rest.get("/api/v2/users/first", (req, res, ctx) => { - return res(ctx.status(404), ctx.json({ message: "hooray, you exist!" })) + return res( + ctx.status(200), + ctx.json({ message: "hooray, someone exists!" }), + ) }), ) render() - await waitForLoaderToBeRemoved() await waitFor(() => expect(window.location).toBeAt("/login")) }) @@ -96,13 +98,14 @@ describe("Setup Page", () => { return res(ctx.status(200), ctx.json(MockUser)) }), rest.get("/api/v2/users/first", (req, res, ctx) => { - return res(ctx.status(404), ctx.json({ message: "hooray, you exist!" })) + return res( + ctx.status(200), + ctx.json({ message: "hooray, someone exists!" }), + ) }), ) render() - await fillForm() - await waitForLoaderToBeRemoved() await waitFor(() => expect(window.location).toBeAt("/workspaces")) }) }) From ea70289896255c36724766d69d8dbea8dd9a6e86 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 22 Aug 2023 20:07:12 +0000 Subject: [PATCH 09/11] I'm good at testing I swear!! --- site/src/pages/SetupPage/SetupPage.test.tsx | 63 +++++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index 667506b737062..e3f2039486d3f 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -1,7 +1,8 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { rest } from "msw" -import { render, waitForLoaderToBeRemoved } from "testHelpers/renderHelpers" +import { createMemoryRouter } from "react-router-dom" +import { render, renderWithRouter } from "testHelpers/renderHelpers" import { server } from "testHelpers/server" import { SetupPage } from "./SetupPage" import { Language as PageViewLanguage } from "./SetupPageView" @@ -76,6 +77,26 @@ describe("Setup Page", () => { expect(errorMessage).toBeDefined() }) + it("redirects to workspaces page when setup is successful", async () => { + render() + + // Update responses before submitting the form + server.use( + rest.get("/api/v2/users/me", (req, res, ctx) => { + return res(ctx.status(200), ctx.json(MockUser)) + }), + rest.get("/api/v2/users/first", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ message: "hooray, someone exists!" }), + ) + }), + ) + + await fillForm() + await waitFor(() => expect(window.location).toBeAt("/workspaces")) + }) + it("redirects to login if setup has already completed", async () => { // simulates setup having already been completed server.use( @@ -87,11 +108,26 @@ describe("Setup Page", () => { }), ) - render() - await waitFor(() => expect(window.location).toBeAt("/login")) + renderWithRouter( + createMemoryRouter( + [ + { + path: "/setup", + element: , + }, + { + path: "/login", + element:

Login

, + }, + ], + { initialEntries: ["/setup"] }, + ), + ) + + await screen.findByText("Login") }) - it("redirects to workspaces page when success", async () => { + it("redirects to the app when already logged in", async () => { // simulates the user will be authenticated server.use( rest.get("/api/v2/users/me", (req, res, ctx) => { @@ -105,7 +141,22 @@ describe("Setup Page", () => { }), ) - render() - await waitFor(() => expect(window.location).toBeAt("/workspaces")) + renderWithRouter( + createMemoryRouter( + [ + { + path: "/setup", + element: , + }, + { + path: "/", + element:

Workspaces

, + }, + ], + { initialEntries: ["/setup"] }, + ), + ) + + await screen.findByText("Workspaces") }) }) From 166b98ffa662cfb22a6120824a4ee358754de790 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 22 Aug 2023 20:20:11 +0000 Subject: [PATCH 10/11] I'm silly!!!! --- site/src/pages/SetupPage/SetupPage.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index e3f2039486d3f..090c43cdab97a 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -77,7 +77,7 @@ describe("Setup Page", () => { expect(errorMessage).toBeDefined() }) - it("redirects to workspaces page when setup is successful", async () => { + it("redirects to the app when setup is successful", async () => { render() // Update responses before submitting the form @@ -94,7 +94,7 @@ describe("Setup Page", () => { ) await fillForm() - await waitFor(() => expect(window.location).toBeAt("/workspaces")) + await waitFor(() => expect(window.location).toBeAt("/")) }) it("redirects to login if setup has already completed", async () => { From 3e0976d286b0e2c25815f47da68bb794fd3978c9 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 22 Aug 2023 20:45:56 +0000 Subject: [PATCH 11/11] YAY --- site/src/pages/SetupPage/SetupPage.test.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index 090c43cdab97a..90272e00fea9f 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -78,21 +78,37 @@ describe("Setup Page", () => { }) it("redirects to the app when setup is successful", async () => { - render() + let userHasBeenCreated = false - // Update responses before submitting the form server.use( rest.get("/api/v2/users/me", (req, res, ctx) => { + if (!userHasBeenCreated) { + return res(ctx.status(401), ctx.json({ message: "no user here" })) + } return res(ctx.status(200), ctx.json(MockUser)) }), rest.get("/api/v2/users/first", (req, res, ctx) => { + if (!userHasBeenCreated) { + return res( + ctx.status(404), + ctx.json({ message: "no first user has been created" }), + ) + } return res( ctx.status(200), ctx.json({ message: "hooray, someone exists!" }), ) }), + rest.post("/api/v2/users/first", (req, res, ctx) => { + userHasBeenCreated = true + return res( + ctx.status(200), + ctx.json({ data: "user setup was successful!" }), + ) + }), ) + render() await fillForm() await waitFor(() => expect(window.location).toBeAt("/")) })