Skip to content

Commit

Permalink
feat(tests): add e2e testing to catch up to Week 13 (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
connordoman authored Dec 4, 2023
1 parent 39a9c26 commit 82c38b8
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 18 deletions.
1 change: 1 addition & 0 deletions app/web/cypress/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!*.mp4
42 changes: 40 additions & 2 deletions app/web/cypress/e2e/login.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Created on Sat Nov 25 2023
* Author: Connor Doman
*/
describe("Login page", () => {
describe("Login page rendering", () => {
beforeEach(() => {
cy.visit("/login");
});
Expand All @@ -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$/);
});
});
23 changes: 23 additions & 0 deletions app/web/cypress/e2e/logout.cy.ts
Original file line number Diff line number Diff line change
@@ -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");
});
});
28 changes: 28 additions & 0 deletions app/web/cypress/e2e/staff/staff.cy.ts
Original file line number Diff line number Diff line change
@@ -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");
});
});
49 changes: 49 additions & 0 deletions app/web/cypress/e2e/upload/upload.cy.ts
Original file line number Diff line number Diff line change
@@ -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");
});
});
50 changes: 50 additions & 0 deletions app/web/cypress/e2e/user/dashboard.cy.ts
Original file line number Diff line number Diff line change
@@ -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");
});
});
Binary file added app/web/cypress/test-data/test.mp4
Binary file not shown.
12 changes: 12 additions & 0 deletions app/web/src/app/staff/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 <main aria-label="Staff-only page">{children}</main>;
}
4 changes: 2 additions & 2 deletions app/web/src/app/staff/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { TestUserList } from "@components/staff/TestUserList";

export default function StaffPage() {
return (
<main>
<>
<TestUserList />
<Link href="/staff/appointment/new">Create New Appointment</Link>
</main>
</>
);
}
6 changes: 5 additions & 1 deletion app/web/src/components/auth/LogoutHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -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 <main>Logging out...</main>;
Expand Down
4 changes: 2 additions & 2 deletions app/web/src/components/staff/TestUserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const TestUserList = () => {
}, []);

return (
<Card>
<CardTitle>Users List</CardTitle>
<Card aria-label="Example user list">
<CardTitle component="h2">Users List</CardTitle>
<CardBody>
<Text component={TextVariants.p}>{explanation}</Text>
{loading ? (
Expand Down
15 changes: 10 additions & 5 deletions app/web/src/components/upload/UploadVideoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
CardBody,
ActionList,
ActionListItem,
Form,
} from "@patternfly/react-core";
import { useRouter } from "next/navigation";
import style from "@assets/style";
Expand Down Expand Up @@ -70,13 +71,13 @@ export const UploadVideoForm = () => {
};

return (
<Card style={style.card}>
<Card style={style.card} aria-label="Video uploader">
<CardTitle component="h1">Upload a Video</CardTitle>
<CardBody>
{responseData?.data?.success ? (
<p className="success">Upload successful. Redirecting...</p>
) : (
<>
<Form aria-label="Video upload form" onSubmit={(e) => e.preventDefault()}>
<input
className="file-input"
type="file"
Expand All @@ -86,17 +87,21 @@ export const UploadVideoForm = () => {
/>
<ActionList style={style.actionList}>
<ActionListItem>
<Button variant="primary" onClick={onSubmitClick}>
<Button
variant="primary"
type="submit"
onClick={onSubmitClick}
aria-label="Submit video">
Submit video
</Button>
</ActionListItem>
<ActionListItem>
<Button variant="danger" isDisabled={true}>
<Button variant="danger" isDisabled={true} aria-label="Record video">
Record video
</Button>
</ActionListItem>
</ActionList>
</>
</Form>
)}
</CardBody>
</Card>
Expand Down
2 changes: 1 addition & 1 deletion app/web/src/components/user/UserCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ interface UserCardProps {

export const UserCard = ({ user }: UserCardProps) => {
return (
<Card style={style.card}>
<Card style={style.card} aria-label="Your Personal Information">
<CardHeader>
<Flex justifyContent={{ default: "justifyContentSpaceBetween" }}>
<FlexItem>
Expand Down
10 changes: 5 additions & 5 deletions app/web/src/components/user/UserDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export const UserDashboard = ({ user }: UserDashboardProps) => {
}, []);

const upcomingAppointments = (
<Card style={styles.upcomingAppointments}>
<CardTitle>Upcoming Appointments</CardTitle>
<Card style={styles.upcomingAppointments} aria-label="Upcoming Appointments">
<CardTitle component="h2">Upcoming Appointments</CardTitle>
<CardBody>
<PrivacyPalDataList data={appointments} headings={["Date", "Appointment"]} />
</CardBody>
Expand All @@ -54,16 +54,16 @@ export const UserDashboard = ({ user }: UserDashboardProps) => {
const dashboardMain = <UserCard user={user} />;

const recentMessages = (
<Card>
<CardTitle>Recent Messages</CardTitle>
<Card aria-label="Recent Messages">
<CardTitle component="h2">Recent Messages</CardTitle>
<CardBody>
<PrivacyPalTable data={messages} headings={["Sender", "Message", "Date"]} />
</CardBody>
</Card>
);

return (
<Grid span={12}>
<Grid span={12} aria-label="User Dashboard">
<GridItem span={12}>
<Title headingLevel="h1">Hi, {user?.firstname}</Title>
<Divider />
Expand Down

0 comments on commit 82c38b8

Please sign in to comment.