diff --git a/.husky/pre-commit b/.husky/pre-commit
index 64947b3..de2afa0 100644
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,6 +1,3 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
 # Get all staged files
 STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
 
@@ -8,6 +5,7 @@ if [ -n "$STAGED_FILES" ]; then
   echo "Running lint with fix on staged files..."
   # Run lint on all files (modifies files in the working directory)
   yarn lint --fix
+  yarn prettier:write
 
   echo "Re-adding originally staged files to the staging area..."
   # Re-add only the originally staged files
diff --git a/e2e/base.ts b/e2e/base.ts
deleted file mode 100644
index f85d085..0000000
--- a/e2e/base.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { test as base } from '@playwright/test';
-import {
-  SecretsManagerClient,
-  GetSecretValueCommand,
-} from "@aws-sdk/client-secrets-manager";
-
-export const getSecretValue = async (
-  secretId: string,
-): Promise<Record<string, string | number | boolean> | null> => {
-  const smClient = new SecretsManagerClient();
-  const data = await smClient.send(
-    new GetSecretValueCommand({ SecretId: secretId }),
-  );
-  if (!data.SecretString) {
-    return null;
-  }
-  try {
-    return JSON.parse(data.SecretString) as Record<
-      string,
-      string | number | boolean
-    >;
-  } catch {
-    return null;
-  }
-};
-
-async function getSecrets() {
-  let response = { PLAYWRIGHT_USERNAME: '', PLAYWRIGHT_PASSWORD: '' }
-  let keyData;
-  if (!process.env.PLAYWRIGHT_USERNAME || !process.env.PLAYWRIGHT_PASSWORD) {
-    keyData = await getSecretValue('infra-core-api-config')
-  }
-  response['PLAYWRIGHT_USERNAME'] = process.env.PLAYWRIGHT_USERNAME || (keyData ? keyData['playwright_username'] : '');
-  response['PLAYWRIGHT_PASSWORD'] = process.env.PLAYWRIGHT_PASSWORD || (keyData ? keyData['playwright_password'] : '');
-  return response;
-}
-
-const secrets = await getSecrets();
-
-async function becomeUser(page) {
-  await page.goto('https://manage.qa.acmuiuc.org/login');
-  await page.getByRole('button', { name: 'Sign in with Illinois NetID' }).click();
-  await page.getByPlaceholder('NetID@illinois.edu').click();
-  await page.getByPlaceholder('NetID@illinois.edu').fill(secrets['PLAYWRIGHT_USERNAME']);
-  await page.getByPlaceholder('NetID@illinois.edu').press('Enter');
-  await page.getByPlaceholder('Password').click();
-  await page.getByPlaceholder('Password').fill(secrets['PLAYWRIGHT_PASSWORD']);
-  await page.getByRole('button', { name: 'Sign in' }).click();
-  await page.getByRole('button', { name: 'No' }).click();
-}
-
-export const test = base.extend<{ becomeUser: (page) => Promise<void> }>({
-  becomeUser: async ({ }, use) => {
-    use(becomeUser)
-  },
-});
diff --git a/e2e/tests/login.spec.ts b/e2e/tests/login.spec.ts
deleted file mode 100644
index 8453159..0000000
--- a/e2e/tests/login.spec.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import { expect } from '@playwright/test';
-import { test } from '../base';
-import { describe } from 'node:test';
-
-describe("Login tests", () => {
-  test('A user can login and view the home screen', async ({ page, becomeUser }) => {
-    await becomeUser(page);
-    await expect(page.locator('a').filter({ hasText: 'Management Portal DEV ENV' })).toBeVisible();
-    await expect(page.locator('a').filter({ hasText: 'Events' })).toBeVisible();
-    await expect(page.locator('a').filter({ hasText: 'Ticketing/Merch' })).toBeVisible();
-    await expect(page.locator('a').filter({ hasText: 'IAM' })).toBeVisible();
-    await expect(page.getByRole('link', { name: 'ACM Logo Management Portal' })).toBeVisible();
-    await expect(page.getByRole('link', { name: 'P', exact: true })).toBeVisible();
-    await page.getByRole('link', { name: 'P', exact: true }).click();
-    await expect(page.getByLabel('PMy Account')).toContainText('Name Playwright Core User');
-    await expect(page.getByLabel('PMy Account')).toContainText('Emailcore-e2e-testing@acm.illinois.edu');
-    expect(page.url()).toEqual('https://manage.qa.acmuiuc.org/home');
-  });
-})
diff --git a/playwright.config.ts b/playwright.config.ts
index 3174725..6610300 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -1,7 +1,7 @@
 import { defineConfig, devices } from '@playwright/test';
 
 export default defineConfig({
-  testDir: './e2e/tests',
+  testDir: './tests/e2e/',
   /* Run tests in files in parallel */
   fullyParallel: false,
   /* Fail the build on CI if you accidentally left test.only in the source code. */
@@ -11,7 +11,7 @@ export default defineConfig({
   /* Opt out of parallel tests on CI. */
   workers: process.env.CI ? 1 : undefined,
   /* Reporter to use. See https://playwright.dev/docs/test-reporters */
-  reporter: 'html',
+  reporter: process.env.CI ? 'github' : 'html',
   /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
   use: {
     /* Base URL to use in actions like `await page.goto('/')`. */
diff --git a/src/ui/pages/events/ViewEvents.page.tsx b/src/ui/pages/events/ViewEvents.page.tsx
index a77f953..0d1334f 100644
--- a/src/ui/pages/events/ViewEvents.page.tsx
+++ b/src/ui/pages/events/ViewEvents.page.tsx
@@ -169,7 +169,7 @@ export const ViewEventsPage: React.FC = () => {
           {showPrevious ? 'Hide Previous Events' : 'Show Previous Events'}
         </Button>
       </div>
-      <Table style={{ tableLayout: 'fixed', width: '100%' }}>
+      <Table style={{ tableLayout: 'fixed', width: '100%' }} data-testid="events-table">
         <Table.Thead>
           <Table.Tr>
             <Table.Th>Title</Table.Th>
diff --git a/tests/e2e/base.ts b/tests/e2e/base.ts
new file mode 100644
index 0000000..a1b9746
--- /dev/null
+++ b/tests/e2e/base.ts
@@ -0,0 +1,82 @@
+import { test as base } from "@playwright/test";
+import {
+  SecretsManagerClient,
+  GetSecretValueCommand,
+} from "@aws-sdk/client-secrets-manager";
+
+export const getSecretValue = async (
+  secretId: string,
+): Promise<Record<string, string | number | boolean> | null> => {
+  const smClient = new SecretsManagerClient();
+  const data = await smClient.send(
+    new GetSecretValueCommand({ SecretId: secretId }),
+  );
+  if (!data.SecretString) {
+    return null;
+  }
+  try {
+    return JSON.parse(data.SecretString) as Record<
+      string,
+      string | number | boolean
+    >;
+  } catch {
+    return null;
+  }
+};
+
+async function getSecrets() {
+  let response = { PLAYWRIGHT_USERNAME: "", PLAYWRIGHT_PASSWORD: "" };
+  let keyData;
+  if (!process.env.PLAYWRIGHT_USERNAME || !process.env.PLAYWRIGHT_PASSWORD) {
+    keyData = await getSecretValue("infra-core-api-config");
+  }
+  response["PLAYWRIGHT_USERNAME"] =
+    process.env.PLAYWRIGHT_USERNAME ||
+    (keyData ? keyData["playwright_username"] : "");
+  response["PLAYWRIGHT_PASSWORD"] =
+    process.env.PLAYWRIGHT_PASSWORD ||
+    (keyData ? keyData["playwright_password"] : "");
+  return response;
+}
+
+const secrets = await getSecrets();
+
+export function capitalizeFirstLetter(string: string) {
+  return string.charAt(0).toUpperCase() + string.slice(1);
+}
+
+async function becomeUser(page) {
+  await page.goto("https://manage.qa.acmuiuc.org/login");
+  await page
+    .getByRole("button", { name: "Sign in with Illinois NetID" })
+    .click();
+  await page.getByPlaceholder("NetID@illinois.edu").click();
+  await page
+    .getByPlaceholder("NetID@illinois.edu")
+    .fill(secrets["PLAYWRIGHT_USERNAME"]);
+  await page.getByPlaceholder("NetID@illinois.edu").press("Enter");
+  await page.getByPlaceholder("Password").click();
+  await page.getByPlaceholder("Password").fill(secrets["PLAYWRIGHT_PASSWORD"]);
+  await page.getByRole("button", { name: "Sign in" }).click();
+  await page.getByRole("button", { name: "No" }).click();
+}
+
+export async function getUpcomingEvents() {
+  const data = await fetch(
+    "https://infra-core-api.aws.qa.acmuiuc.org/api/v1/events?upcomingOnly=true",
+  );
+  return (await data.json()) as Record<string, string>[];
+}
+
+export async function getAllEvents() {
+  const data = await fetch(
+    "https://infra-core-api.aws.qa.acmuiuc.org/api/v1/events",
+  );
+  return (await data.json()) as Record<string, string>[];
+}
+
+export const test = base.extend<{ becomeUser: (page) => Promise<void> }>({
+  becomeUser: async ({}, use) => {
+    use(becomeUser);
+  },
+});
diff --git a/tests/e2e/events.spec.ts b/tests/e2e/events.spec.ts
new file mode 100644
index 0000000..2b6bad0
--- /dev/null
+++ b/tests/e2e/events.spec.ts
@@ -0,0 +1,48 @@
+import { expect } from "@playwright/test";
+import { capitalizeFirstLetter, getUpcomingEvents, test } from "./base";
+import { describe } from "node:test";
+
+describe("Events tests", () => {
+  test("A user can login and view the upcoming events", async ({
+    page,
+    becomeUser,
+  }) => {
+    await becomeUser(page);
+    await page.locator("a").filter({ hasText: "Events" }).click();
+    await expect(page.getByRole("heading")).toContainText(
+      "Core Management Service (NonProd)",
+    );
+    await expect(
+      page.getByRole("button", { name: "New Calendar Event" }),
+    ).toBeVisible();
+    await expect(
+      page.getByRole("button", { name: "Show Previous Events" }),
+    ).toBeVisible();
+
+    const table = page.getByTestId("events-table");
+    await expect(table).toBeVisible();
+
+    const rows = await table.locator("tbody tr").all();
+    const expectedTableData = await getUpcomingEvents();
+
+    for (let i = 0; i < rows.length; i++) {
+      const row = rows[i];
+      const expectedData = expectedTableData[i];
+      const title = await row.locator("td:nth-child(1)").innerText();
+      const location = await row.locator("td:nth-child(4)").innerText();
+      const description = await row.locator("td:nth-child(5)").innerText();
+      const host = await row.locator("td:nth-child(6)").innerText();
+      const featured = await row.locator("td:nth-child(7)").innerText();
+      const repeats = await row.locator("td:nth-child(8)").innerText();
+
+      expect(title).toEqual(expectedData.title);
+      expect(location).toEqual(expectedData.location);
+      expect(description).toEqual(expectedData.description);
+      expect(host).toEqual(expectedData.host);
+      expect(featured).toEqual(expectedData.featured ? "Yes" : "No");
+      expect(repeats).toEqual(capitalizeFirstLetter(expectedData.repeats));
+    }
+
+    expect(page.url()).toEqual("https://manage.qa.acmuiuc.org/events/manage");
+  });
+});
diff --git a/tests/e2e/login.spec.ts b/tests/e2e/login.spec.ts
new file mode 100644
index 0000000..e0aeab5
--- /dev/null
+++ b/tests/e2e/login.spec.ts
@@ -0,0 +1,34 @@
+import { expect } from "@playwright/test";
+import { test } from "./base";
+import { describe } from "node:test";
+
+describe("Login tests", () => {
+  test("A user can login and view the home screen", async ({
+    page,
+    becomeUser,
+  }) => {
+    await becomeUser(page);
+    await expect(
+      page.locator("a").filter({ hasText: "Management Portal DEV ENV" }),
+    ).toBeVisible();
+    await expect(page.locator("a").filter({ hasText: "Events" })).toBeVisible();
+    await expect(
+      page.locator("a").filter({ hasText: "Ticketing/Merch" }),
+    ).toBeVisible();
+    await expect(page.locator("a").filter({ hasText: "IAM" })).toBeVisible();
+    await expect(
+      page.getByRole("link", { name: "ACM Logo Management Portal" }),
+    ).toBeVisible();
+    await expect(
+      page.getByRole("link", { name: "P", exact: true }),
+    ).toBeVisible();
+    await page.getByRole("link", { name: "P", exact: true }).click();
+    await expect(page.getByLabel("PMy Account")).toContainText(
+      "Name Playwright Core User",
+    );
+    await expect(page.getByLabel("PMy Account")).toContainText(
+      "Emailcore-e2e-testing@acm.illinois.edu",
+    );
+    expect(page.url()).toEqual("https://manage.qa.acmuiuc.org/home");
+  });
+});