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"); + }); +});