diff --git a/frontend/src/Router.test.tsx b/frontend/src/Router.test.tsx new file mode 100644 index 000000000..533ca9231 --- /dev/null +++ b/frontend/src/Router.test.tsx @@ -0,0 +1,78 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { describe, it, expect } from "vitest"; + +import { segmentsToRoute } from "./Router"; + +describe("Router", () => { + describe("segmentsToRoute", () => { + it("returns home for route with no segments", () => { + const segments: string[] = []; + expect(segmentsToRoute(segments)).toEqual({ type: "home" }); + }); + + it("returns home for route with and empty string segment", () => { + const segments: string[] = [""]; + expect(segmentsToRoute(segments)).toEqual({ type: "home" }); + }); + + it("returns profile for route with profile", () => { + const segments: string[] = ["profile"]; + expect(segmentsToRoute(segments)).toEqual({ type: "profile" }); + }); + + it("returns browser session list for sessions", () => { + const segments: string[] = ["sessions"]; + expect(segmentsToRoute(segments)).toEqual({ + type: "browser-session-list", + }); + }); + + it("returns compat session list for compat-sessions", () => { + const segments: string[] = ["compat-sessions"]; + expect(segmentsToRoute(segments)).toEqual({ + type: "compat-session-list", + }); + }); + + it("returns oauth session list for oauth2-sessions", () => { + const segments: string[] = ["oauth2-sessions"]; + expect(segmentsToRoute(segments)).toEqual({ + type: "oauth2-session-list", + }); + }); + + it("returns client detail route correctly", () => { + const segments: string[] = ["clients", "client-id"]; + expect(segmentsToRoute(segments)).toEqual({ + type: "client", + id: "client-id", + }); + }); + + it("returns session detail route correctly", () => { + const segments: string[] = ["sessions", "session-id"]; + expect(segmentsToRoute(segments)).toEqual({ + type: "session", + id: "session-id", + }); + }); + + it("returns unknown for other segments", () => { + const segments: string[] = ["just", "testing"]; + expect(segmentsToRoute(segments)).toEqual({ type: "unknown", segments }); + }); + }); +}); diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index 9549d8dee..ae2ac3476 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -25,7 +25,7 @@ type Location = { }; type HomeRoute = { type: "home" }; -type EmailListRoute = { type: "email-list" }; +type ProfileRoute = { type: "profile" }; type OAuth2ClientRoute = { type: "client"; id: string }; type OAuth2SessionList = { type: "oauth2-session-list" }; type BrowserSessionRoute = { type: "session"; id: string }; @@ -36,7 +36,7 @@ type UnknownRoute = { type: "unknown"; segments: string[] }; export type Route = | HomeRoute - | EmailListRoute + | ProfileRoute | OAuth2ClientRoute | OAuth2SessionList | BrowserSessionRoute @@ -49,8 +49,8 @@ const routeToSegments = (route: Route): string[] => { switch (route.type) { case "home": return []; - case "email-list": - return ["emails"]; + case "profile": + return ["profile"]; case "verify-email": return ["emails", route.id, "verify"]; case "client": @@ -90,7 +90,7 @@ const segmentMatches = ( return true; }; -const segmentsToRoute = (segments: string[]): Route => { +export const segmentsToRoute = (segments: string[]): Route => { const matches = (...pattern: PatternItem[]): boolean => segmentMatches(segments, ...pattern); @@ -99,8 +99,8 @@ const segmentsToRoute = (segments: string[]): Route => { return { type: "home" }; } - if (matches("emails")) { - return { type: "email-list" }; + if (matches("profile")) { + return { type: "profile" }; } if (matches("sessions")) { @@ -169,7 +169,7 @@ export const routeAtom = atom( ); const Home = lazy(() => import("./pages/Home")); -const EmailList = lazy(() => import("./pages/EmailList")); +const Profile = lazy(() => import("./pages/Profile")); const OAuth2Client = lazy(() => import("./pages/OAuth2Client")); const BrowserSession = lazy(() => import("./pages/BrowserSession")); const BrowserSessionList = lazy(() => import("./pages/BrowserSessionList")); @@ -183,8 +183,8 @@ const InnerRouter: React.FC = () => { switch (route.type) { case "home": return ; - case "email-list": - return ; + case "profile": + return ; case "oauth2-session-list": return ; case "browser-session-list": diff --git a/frontend/src/__snapshots__/Router.test.tsx.snap b/frontend/src/__snapshots__/Router.test.tsx.snap new file mode 100644 index 000000000..ca9514cd7 --- /dev/null +++ b/frontend/src/__snapshots__/Router.test.tsx.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Router > routes 1`] = `"Loading..."`; diff --git a/frontend/src/components/Layout.test.tsx b/frontend/src/components/Layout.test.tsx new file mode 100644 index 000000000..b233b18ae --- /dev/null +++ b/frontend/src/components/Layout.test.tsx @@ -0,0 +1,79 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @vitest-environment happy-dom + +import { render } from "@testing-library/react"; +import { Provider } from "jotai"; +import { useHydrateAtoms } from "jotai/utils"; +import { Suspense } from "react"; +import { describe, expect, it, vi, afterAll, beforeEach } from "vitest"; + +import { appConfigAtom, locationAtom } from "../Router"; +import { currentUserIdAtom, GqlResult } from "../atoms"; + +import Layout from "./Layout"; + +beforeEach(async () => { + // For some reason, the locationAtom gets updated with `about:black` on render, + // so we need to set a "real" location and wait for the next tick + window.location.assign("https://example.com/"); + // Wait the next tick for the location to update + await new Promise((resolve) => setTimeout(resolve, 0)); +}); + +const HydrateLocation: React.FC> = ({ + children, + path, +}) => { + useHydrateAtoms([ + [appConfigAtom, { root: "/" }], + [locationAtom, { pathname: path }], + ]); + return <>{children}; +}; + +const WithLocation: React.FC> = ({ + children, + path, +}) => { + return ( + + + {children} + + + ); +}; + +describe("", () => { + beforeEach(() => { + vi.spyOn(currentUserIdAtom, "read").mockResolvedValue( + "abc123" as unknown as GqlResult, + ); + }); + afterAll(() => { + vi.restoreAllMocks(); + }); + it("renders app navigation correctly", async () => { + const component = render( + + + , + ); + + expect(await component.findByText("Profile")).toMatchSnapshot(); + expect(await component.findByText("Home")).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index dda7e6fca..ba92a52d7 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -43,7 +43,7 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => { Home - Emails + Profile
{children}
diff --git a/frontend/src/components/NavBar/NavBar.stories.tsx b/frontend/src/components/NavBar/NavBar.stories.tsx index 054166443..06de4cc86 100644 --- a/frontend/src/components/NavBar/NavBar.stories.tsx +++ b/frontend/src/components/NavBar/NavBar.stories.tsx @@ -30,7 +30,7 @@ const meta = { Home - Emails + Emails External diff --git a/frontend/src/components/NavItem/NavItem.stories.tsx b/frontend/src/components/NavItem/NavItem.stories.tsx index 908ddb94b..c5421ecfe 100644 --- a/frontend/src/components/NavItem/NavItem.stories.tsx +++ b/frontend/src/components/NavItem/NavItem.stories.tsx @@ -53,7 +53,7 @@ export const Active: Story = { export const Inactive: Story = { args: { - route: { type: "email-list" }, - children: "Emails", + route: { type: "profile" }, + children: "Profile", }, }; diff --git a/frontend/src/components/NavItem/NavItem.test.tsx b/frontend/src/components/NavItem/NavItem.test.tsx index be9544643..0771041a1 100644 --- a/frontend/src/components/NavItem/NavItem.test.tsx +++ b/frontend/src/components/NavItem/NavItem.test.tsx @@ -77,7 +77,7 @@ describe("NavItem", () => { it("renders a different route", () => { const component = create( - Emails + Emails , ); const tree = component.toJSON(); diff --git a/frontend/src/components/NavItem/__snapshots__/NavItem.test.tsx.snap b/frontend/src/components/NavItem/__snapshots__/NavItem.test.tsx.snap index 704d61fab..2a1b1c395 100644 --- a/frontend/src/components/NavItem/__snapshots__/NavItem.test.tsx.snap +++ b/frontend/src/components/NavItem/__snapshots__/NavItem.test.tsx.snap @@ -36,7 +36,7 @@ exports[`NavItem > renders a different route 1`] = ` > Emails diff --git a/frontend/src/components/UserHome/UserHome.tsx b/frontend/src/components/UserHome/UserHome.tsx index 0f915e6f3..70c797f90 100644 --- a/frontend/src/components/UserHome/UserHome.tsx +++ b/frontend/src/components/UserHome/UserHome.tsx @@ -65,7 +65,7 @@ const UserHome: React.FC<{ {data.unverifiedEmails.totalCount > 0 && !dismiss && ( You have {data.unverifiedEmails.totalCount} unverified email - address(es). Check + address(es). Check )} @@ -78,7 +78,7 @@ const UserHome: React.FC<{ {data.confirmedEmails.totalCount > 1 && ( {data.confirmedEmails.totalCount - 1} additional emails.{" "} - View all + View all )} diff --git a/frontend/src/components/UserHome/__snapshots__/UserHome.test.tsx.snap b/frontend/src/components/UserHome/__snapshots__/UserHome.test.tsx.snap index 58f3c7ab4..177ce7ce1 100644 --- a/frontend/src/components/UserHome/__snapshots__/UserHome.test.tsx.snap +++ b/frontend/src/components/UserHome/__snapshots__/UserHome.test.tsx.snap @@ -47,7 +47,7 @@ exports[`UserHome > render a with additional emails 1`] = ` additional emails. View all @@ -128,7 +128,7 @@ exports[`UserHome > render a with an unverified email 1`] = ` 1 unverified email address(es). Check diff --git a/frontend/src/components/VerifyEmail/VerifyEmail.tsx b/frontend/src/components/VerifyEmail/VerifyEmail.tsx index 8d3048b64..e24003544 100644 --- a/frontend/src/components/VerifyEmail/VerifyEmail.tsx +++ b/frontend/src/components/VerifyEmail/VerifyEmail.tsx @@ -127,7 +127,7 @@ const VerifyEmail: React.FC<{ e.currentTarget?.reset(); if (result.data?.verifyEmail.status === "VERIFIED") { - setRoute({ type: "email-list" }); + setRoute({ type: "profile" }); } else { fieldRef.current?.focus(); fieldRef.current?.select(); diff --git a/frontend/src/components/VerifyEmail/__snapshots__/VerifyEmail.test.tsx.snap b/frontend/src/components/VerifyEmail/__snapshots__/VerifyEmail.test.tsx.snap new file mode 100644 index 000000000..9e89b6078 --- /dev/null +++ b/frontend/src/components/VerifyEmail/__snapshots__/VerifyEmail.test.tsx.snap @@ -0,0 +1,179 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[` > renders an active session 1`] = ` +[ +
+ + + +

+ Verify your email +

+

+ Enter the 6-digit code sent to + + + ernie@sesame.st + +

+
, +
+
+ + +
+ + +
, +] +`; + +exports[` > renders verify screen for email 1`] = ` +[ +
+ + + +

+ Verify your email +

+

+ Enter the 6-digit code sent to + + + ernie@sesame.st + +

+
, +
+
+ + +
+ + +
, +] +`; diff --git a/frontend/src/components/__snapshots__/Layout.test.tsx.snap b/frontend/src/components/__snapshots__/Layout.test.tsx.snap new file mode 100644 index 000000000..bf44867bd --- /dev/null +++ b/frontend/src/components/__snapshots__/Layout.test.tsx.snap @@ -0,0 +1,20 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[` > renders app navigation correctly 1`] = ` +
+ Profile + +`; + +exports[` > renders app navigation correctly 2`] = ` + + Home + +`; diff --git a/frontend/src/pages/EmailList.tsx b/frontend/src/pages/Profile.tsx similarity index 94% rename from frontend/src/pages/EmailList.tsx rename to frontend/src/pages/Profile.tsx index 307463789..2c9c7073b 100644 --- a/frontend/src/pages/EmailList.tsx +++ b/frontend/src/pages/Profile.tsx @@ -20,7 +20,7 @@ import NotLoggedIn from "../components/NotLoggedIn"; import UserEmailList from "../components/UserEmailList"; import { isErr, unwrapErr, unwrapOk } from "../result"; -const EmailList: React.FC = () => { +const Profile: React.FC = () => { const result = useAtomValue(currentUserIdAtom); if (isErr(result)) return ; @@ -30,4 +30,4 @@ const EmailList: React.FC = () => { return ; }; -export default EmailList; +export default Profile;