diff --git a/app/web/cypress/.gitignore b/app/web/cypress/.gitignore new file mode 100644 index 00000000..3ceaed39 --- /dev/null +++ b/app/web/cypress/.gitignore @@ -0,0 +1 @@ +!*.mp4 diff --git a/app/web/cypress/e2e/login.cy.ts b/app/web/cypress/e2e/login.cy.ts index 315a00fc..4807d50c 100644 --- a/app/web/cypress/e2e/login.cy.ts +++ b/app/web/cypress/e2e/login.cy.ts @@ -2,7 +2,7 @@ * Created on Sat Nov 25 2023 * Author: Connor Doman */ -describe("Login page", () => { +describe("Login page rendering", () => { beforeEach(() => { cy.visit("/login"); }); @@ -21,6 +21,44 @@ describe("Login page", () => { }); it("should contain a submit button", () => { - cy.get("button[type=submit]").should("exist"); + const signInButton = cy.get("button[type=submit]"); + signInButton.should("exist"); + signInButton.should("contain", "Submit"); + }); + + it("should include a disabled sign up with code button", () => { + const disabledButton = cy.get("button[type=button]"); + disabledButton.should("exist"); + disabledButton.should("contain", "Sign up with Code"); + disabledButton.should("be.disabled"); + }); +}); + +describe("Login page functionality", () => { + it("should not redirect on login failure", () => { + cy.visit("/login"); + cy.get("input[name=email]").type("nobody@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(1000); + cy.url().should("include", "/login"); + }); + + it("should redirect to / on login success", () => { + cy.visit("/login"); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(1000); + cy.url().should("match", /\/$/); + }); + + it("should redirect to provided redirect url on login success", () => { + cy.visit("/login?r=%2Fuser"); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(1000); + cy.url().should("match", /\/user$/); }); }); diff --git a/app/web/cypress/e2e/logout.cy.ts b/app/web/cypress/e2e/logout.cy.ts new file mode 100644 index 00000000..65a4f0d2 --- /dev/null +++ b/app/web/cypress/e2e/logout.cy.ts @@ -0,0 +1,23 @@ +describe("Logout page", () => { + beforeEach(() => {}); + + it("should redirect a logged out user to login", () => { + cy.visit("/logout"); + cy.wait(250); + cy.url().should("include", "/login"); + }); + + it("should clear a user session", () => { + cy.visit("/login"); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(250); + cy.url().should("match", /\/$/); + cy.visit("/logout"); + cy.wait(250); + cy.visit("/user/dashboard"); + cy.wait(500); + cy.url().should("include", "/login"); + }); +}); diff --git a/app/web/cypress/e2e/staff/staff.cy.ts b/app/web/cypress/e2e/staff/staff.cy.ts new file mode 100644 index 00000000..3391a6c6 --- /dev/null +++ b/app/web/cypress/e2e/staff/staff.cy.ts @@ -0,0 +1,28 @@ +describe("Staff page rendering", () => { + beforeEach(() => { + cy.visit("/staff"); + cy.wait(250); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(250); + }); + + it("Should show the staff page", () => { + cy.get("main[aria-label='Staff-only page']"); + }); + + it("Should show a sample list of users", () => { + const usersList = cy.get("[aria-label='Example user list']"); + usersList.should("exist"); + usersList.get("h2").should("contain", "Users List"); + }); +}); + +describe("Staff page functionality", () => { + it("Should redirect to /login if not logged in", () => { + cy.visit("/staff"); + cy.wait(250); + cy.url().should("include", "/login"); + }); +}); diff --git a/app/web/cypress/e2e/upload/upload.cy.ts b/app/web/cypress/e2e/upload/upload.cy.ts new file mode 100644 index 00000000..e3aad235 --- /dev/null +++ b/app/web/cypress/e2e/upload/upload.cy.ts @@ -0,0 +1,49 @@ +describe("Video upload rendering", () => { + beforeEach(() => { + cy.visit("/upload"); + }); + + it("should contain a heading", () => { + cy.get("h1").contains("Upload a Video"); + }); + + it("should contain an video upload form", () => { + cy.get("form[aria-label='Video upload form']").should("exist"); + }); + + it("should contain a file input", () => { + cy.get("input[type=file]").should("exist"); + }); + + it("should contain a submit button", () => { + cy.get("button[aria-label='Submit video']").should("exist"); + }); + + it("should contain a disabled record button", () => { + const recordButton = cy.get("button[aria-label='Record video']"); + recordButton.should("exist"); + recordButton.should("be.disabled"); + }); +}); + +describe("Video upload functionality", () => { + beforeEach(() => { + cy.visit("/upload"); + }); + + it("should not upload a video when not logged in", () => { + cy.intercept( + { + method: "POST", + url: "/api/video/upload", + }, + { data: { success: true, filePath: "test.mp4" } } + ).as("videoUploadApi"); + cy.get("input[type=file]").selectFile("cypress/test-data/test.mp4"); + cy.get("button[aria-label='Submit video']").click(); + cy.wait("@videoUploadApi").then((interception) => { + expect(interception.response.statusCode).to.equal(200); + }); + cy.url().should("include", "/upload/status"); + }); +}); diff --git a/app/web/cypress/e2e/user/dashboard.cy.ts b/app/web/cypress/e2e/user/dashboard.cy.ts new file mode 100644 index 00000000..36502b63 --- /dev/null +++ b/app/web/cypress/e2e/user/dashboard.cy.ts @@ -0,0 +1,50 @@ +describe("User dashboard functionality", () => { + it("should redirect to /login if not logged in", () => { + cy.visit("/user/dashboard"); + cy.wait(250); + cy.url().should("include", "/login"); + }); + + it("should display the user dashboard if logged in", () => { + cy.visit("/user/dashboard"); + cy.wait(250); + cy.url().should("include", "/login"); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(250); + cy.url().should("include", "/user/dashboard"); + }); +}); + +describe("User dashboard rendering", () => { + beforeEach(() => { + cy.visit("/user/dashboard"); + cy.wait(250); + cy.url().should("include", "/login"); + cy.get("input[name=email]").type("johnny@example.com"); + cy.get("input[name=password]").type("password"); + cy.get("button[type=submit]").click(); + cy.wait(250); + cy.url().should("include", "/user/dashboard"); + }); + + it("should display the user dashboard", () => { + cy.get("h1").contains("Hi,"); + cy.get("div[aria-label='User Dashboard']").should("exist"); + }); + + it("should have an appointments section", () => { + cy.get("h2").contains("Upcoming Appointments"); + cy.get("div[aria-label='Upcoming Appointments']").should("exist"); + }); + + it("should have a user card", () => { + cy.get("div[aria-label='Your Personal Information']").should("exist"); + }); + + it("should have a recent messages section", () => { + cy.get("h2").contains("Recent Messages"); + cy.get("div[aria-label='Recent Messages']").should("exist"); + }); +}); diff --git a/app/web/cypress/test-data/test.mp4 b/app/web/cypress/test-data/test.mp4 new file mode 100644 index 00000000..ed139d6d Binary files /dev/null and b/app/web/cypress/test-data/test.mp4 differ diff --git a/app/web/src/app/staff/layout.tsx b/app/web/src/app/staff/layout.tsx new file mode 100644 index 00000000..ac9ebe8c --- /dev/null +++ b/app/web/src/app/staff/layout.tsx @@ -0,0 +1,12 @@ +/* + * Created on Sun Dec 03 2023 + * Author: Connor Doman + */ + +interface StaffAreaLayoutProps { + children?: React.ReactNode; +} + +export default async function StaffAreaLayout({ children }: StaffAreaLayoutProps) { + return
{children}
; +} diff --git a/app/web/src/app/staff/page.tsx b/app/web/src/app/staff/page.tsx index 3548e7c1..e94d1c52 100644 --- a/app/web/src/app/staff/page.tsx +++ b/app/web/src/app/staff/page.tsx @@ -9,9 +9,9 @@ import { TestUserList } from "@components/staff/TestUserList"; export default function StaffPage() { return ( -
+ <> Create New Appointment -
+ ); } diff --git a/app/web/src/components/auth/LogoutHandler.tsx b/app/web/src/components/auth/LogoutHandler.tsx index 75d433f4..be4daa75 100644 --- a/app/web/src/components/auth/LogoutHandler.tsx +++ b/app/web/src/components/auth/LogoutHandler.tsx @@ -8,9 +8,11 @@ import { clearAuthSession, logOut } from "@app/actions"; import { clearSession } from "@lib/session"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; +import { useSearchParams } from "next/navigation"; export default function LogoutHandler() { const router = useRouter(); + const searchParams = useSearchParams(); const [loggedOut, setLoggedOut] = useState(false); useEffect(() => { @@ -22,7 +24,9 @@ export default function LogoutHandler() { }, []); useEffect(() => { - router.push("/"); + const redirectTo = searchParams.get("r"); + const redirectUrl = "/login" + (redirectTo ? "?r=" + encodeURIComponent(redirectTo) : ""); + router.push(redirectUrl); }, [loggedOut]); return
Logging out...
; diff --git a/app/web/src/components/staff/TestUserList.tsx b/app/web/src/components/staff/TestUserList.tsx index b8711842..5913996c 100644 --- a/app/web/src/components/staff/TestUserList.tsx +++ b/app/web/src/components/staff/TestUserList.tsx @@ -25,8 +25,8 @@ export const TestUserList = () => { }, []); return ( - - Users List + + Users List {explanation} {loading ? ( diff --git a/app/web/src/components/upload/UploadVideoForm.tsx b/app/web/src/components/upload/UploadVideoForm.tsx index 12fe7fef..060acd4c 100644 --- a/app/web/src/components/upload/UploadVideoForm.tsx +++ b/app/web/src/components/upload/UploadVideoForm.tsx @@ -12,6 +12,7 @@ import { CardBody, ActionList, ActionListItem, + Form, } from "@patternfly/react-core"; import { useRouter } from "next/navigation"; import style from "@assets/style"; @@ -70,13 +71,13 @@ export const UploadVideoForm = () => { }; return ( - + Upload a Video {responseData?.data?.success ? (

Upload successful. Redirecting...

) : ( - <> +
e.preventDefault()}> { /> - - - +
)}
diff --git a/app/web/src/components/user/UserCard.tsx b/app/web/src/components/user/UserCard.tsx index ee2cb306..c51f8b43 100644 --- a/app/web/src/components/user/UserCard.tsx +++ b/app/web/src/components/user/UserCard.tsx @@ -44,7 +44,7 @@ interface UserCardProps { export const UserCard = ({ user }: UserCardProps) => { return ( - + diff --git a/app/web/src/components/user/UserDashboard.tsx b/app/web/src/components/user/UserDashboard.tsx index f3f1dbc5..789a41a4 100644 --- a/app/web/src/components/user/UserDashboard.tsx +++ b/app/web/src/components/user/UserDashboard.tsx @@ -43,8 +43,8 @@ export const UserDashboard = ({ user }: UserDashboardProps) => { }, []); const upcomingAppointments = ( - - Upcoming Appointments + + Upcoming Appointments @@ -54,8 +54,8 @@ export const UserDashboard = ({ user }: UserDashboardProps) => { const dashboardMain = ; const recentMessages = ( - - Recent Messages + + Recent Messages @@ -63,7 +63,7 @@ export const UserDashboard = ({ user }: UserDashboardProps) => { ); return ( - + Hi, {user?.firstname}