Skip to content

Commit

Permalink
add e2e tests w/ playwright (#41)
Browse files Browse the repository at this point in the history
* add e2e tests

* add ci env

* change makefile

* update tests to use correct fixture
  • Loading branch information
devksingh4 authored Aug 1, 2024
1 parent 8320466 commit d15b19f
Show file tree
Hide file tree
Showing 13 changed files with 366 additions and 24 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,11 @@ jobs:
- name: Run live testing
run: make test_live_integration
env:
RB_JWT_SECRET: ${{ secrets.RB_JWT_SECRET }}
RB_JWT_SECRET: ${{ secrets.RB_JWT_SECRET }}
- name: Run E2E testing
run: make test_e2e
env:
RB_PLAYWRIGHT_USERNAME: ${{ secrets.RB_PLAYWRIGHT_USERNAME }}
RB_PLAYWRIGHT_PASSWORD: ${{ secrets.RB_PLAYWRIGHT_PASSWORD }}
RB_JWT_SECRET: ${{ secrets.RB_JWT_SECRET }}
CI: true
8 changes: 8 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ jobs:
run: make test_live_integration
env:
RB_JWT_SECRET: ${{ secrets.RB_JWT_SECRET }}
CI: true
- name: Run E2E testing
run: make test_e2e
env:
RB_PLAYWRIGHT_USERNAME: ${{ secrets.RB_PLAYWRIGHT_USERNAME }}
RB_PLAYWRIGHT_PASSWORD: ${{ secrets.RB_PLAYWRIGHT_PASSWORD }}
RB_JWT_SECRET: ${{ secrets.RB_JWT_SECRET }}
CI: true
deploy-aws-prod:
runs-on: ubuntu-latest
name: Deploy to AWS PROD
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ __pycache__
test.json
build
.vscode/settings.json
node_modules/
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,19 @@ install_deps_python:
install_deps_node:
cd clientv2 && corepack enable && yarn

install_deps_node_e2e:
cd e2e && corepack enable && yarn && yarn run install

test_live_integration: install_deps_python
pytest -rP tests/live_integration/

test_unit: install_deps_python install_deps_node
pytest -rP api/
cd clientv2 && yarn test

test_e2e: install_deps_node_e2e
cd e2e && yarn run test
test_e2e_ui: install_deps_node_e2e
cd e2e && yarn run test --ui
generate_jwt:
python utils/generate_jwt.py
5 changes: 5 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
82 changes: 82 additions & 0 deletions e2e/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { test as base } from '@playwright/test';
import {sign} from 'jsonwebtoken';
// Extend basic test by providing a "todoPage" fixture.

function generateJWT(email: string, role: string = 'student', env: string = 'dev'): string {
const JWT_SECRET = process.env.RB_JWT_SECRET;
if (!JWT_SECRET) {
console.error("JWT Secret not found in environment variable RB_JWT_SECRET.");
return "";
}

const iat = Math.floor(Date.now() / 1000); // Current time in seconds since the Unix epoch
const exp = iat + (24 * 60 * 60); // Token expiration time (24 hours later)

const payload = {
iss: 'custom_jwt',
permissions: [`${role}:resume-book-${env}`],
email: email,
exp: exp,
iat: iat,
nbf: iat,
aud: 'https://resumes.aws.qa.acmuiuc.org',
};

return sign(payload, JWT_SECRET, { algorithm: 'HS256' });
}
interface BecomeUserParams {
email?: string
role: string;
}
async function becomeUser(page, {email, role}: BecomeUserParams) {
if (!email) {
email = process.env.RB_PLAYWRIGHT_USERNAME!;
}
if (email !== process.env.RB_PLAYWRIGHT_USERNAME!) {
const jwt = generateJWT(email, role);
await page.route('**/*', async route => {
if (route.request().url().startsWith('https://resumes.aws.qa.acmuiuc.org/')) {
const token = generateJWT("newuser@testing.illinois.edu")
const headers = {
...route.request().headers(),
'Authorization': `Bearer ${jwt}`
};

// Continue with modified headers
route.continue({ headers });
} else {
// Continue with the normal routing for other URLs
route.continue();
}
});
}
if (role === 'student') {
await page.goto('https://resumes.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(process.env.RB_PLAYWRIGHT_USERNAME!);
await page.getByPlaceholder('NetID@illinois.edu').press('Enter');
await page.getByPlaceholder('Password').click();
await page.getByPlaceholder('Password').fill(process.env.RB_PLAYWRIGHT_PASSWORD!);
await page.getByRole('button', { name: 'Sign in' }).click();
await page.getByRole('button', { name: 'No' }).click();
} else if (role === 'recruiter') {
await page.goto('https://resumes.qa.acmuiuc.org/login');
await page.getByRole('button', { name: 'ACM@UIUC Partner Login' }).click();
await page.getByTestId('auth-email-username-field').click();
await page.getByTestId('auth-email-username-field').fill(process.env.RB_PLAYWRIGHT_USERNAME!);
await page.getByRole('button', { name: 'Continue' }).click();
await page.getByLabel('Password', { exact: true }).fill(process.env.RB_PLAYWRIGHT_PASSWORD!);
await page.getByLabel('Password', { exact: true }).click();
await page.getByRole('button', { name: 'Continue' }).click();
}
}

export const test = base.extend<{ generateJWT: CallableFunction, becomeUser: (page, params: BecomeUserParams) => Promise<void> }>({
generateJWT: async ({}, use) => {
use(generateJWT)
},
becomeUser: async ({}, use) => {
use(becomeUser)
},
});
20 changes: 20 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "e2e",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"install": "playwright install --with-deps",
"test": "playwright test"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.45.3",
"@types/node": "^22.0.2"
},
"dependencies": {
"jsonwebtoken": "^9.0.2"
}
}
48 changes: 48 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: false,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* 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',
/* 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('/')`. */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
16 changes: 16 additions & 0 deletions e2e/tests/recruiter/login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

import { expect } from '@playwright/test';
import { test } from '../../base';
import { describe } from 'node:test';

describe("Recruiter login tests", () => {
test('A recruiter can login and view the search screen', async ({ page, becomeUser }) => {
await becomeUser(page, {role: 'recruiter'});
await page.getByRole('link', { name: 'My Account' }).click();
await expect(page.getByRole('button', { name: 'Email resumebook-e2e-testing@' })).toBeVisible();
await expect(page.getByLabel('My Account')).toContainText('RoleRecruiter');
await expect(page.getByLabel('My Account')).toContainText('Emailresumebook-e2e-testing@acm.illinois.edu');
await expect(page.getByLabel('My Account')).toContainText('NameResume Book User');
await expect(page.getByRole('heading')).toContainText('Search Resume Book');
});
})
16 changes: 16 additions & 0 deletions e2e/tests/student/edit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { expect } from '@playwright/test';
import { test } from '../../base';
import { describe } from 'node:test';

describe("Test that users can edit their profile", () => {
test('Editing and immediately saving a profile succeeds', async ({ page, becomeUser }) => {
await becomeUser(page, {email: 'mainuser1@testing.illinois.edu', role: 'student'})
await page.getByRole('link', { name: 'My Profile' }).click();
expect(page.getByText('Resume Book User')).toBeTruthy()
expect(page.getByText('Skills')).toBeTruthy()
expect(page.getByText('Botting')).toBeTruthy()
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('button', { name: 'Save' }).click();
expect(await page.waitForSelector('text="Profile saved!"')).toBeTruthy();
});
})
19 changes: 19 additions & 0 deletions e2e/tests/student/login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect } from '@playwright/test';
import { test } from '../../base';
import { describe } from 'node:test';

describe("Student login tests", () => {
test('A new student can login and view the profile creation screen', async ({ page, becomeUser }) => {
await becomeUser(page, {email: 'noone@testing.illinois.edu', role: 'student'})
await page.getByRole('link', { name: 'My Profile' }).click();
expect(page.getByText('Welcome to Resume Book')).toBeTruthy()
expect(page.getByText('User,Resume Book')).toBeTruthy();
});
test('An existing user can log in and view their profile', async ({ page, becomeUser }) => {
await becomeUser(page, {email: 'mainuser1@testing.illinois.edu', role: 'student'})
await page.getByRole('link', { name: 'My Profile' }).click();
expect(page.getByText('Resume Book User')).toBeTruthy()
expect(page.getByText('Skills')).toBeTruthy()
expect(page.getByText('Botting')).toBeTruthy()
});
})
Loading

0 comments on commit d15b19f

Please sign in to comment.