Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests/e2e auth #2

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b39cb7f
feat: implement e2e tests for auth
hazardsoft Sep 5, 2023
aa8b8cf
refactor: remove playwright show-report command
hazardsoft Sep 5, 2023
dfa0e5c
Merge branch 'main' into tests/e2e-auth
hazardsoft Sep 5, 2023
c972b09
build: update pnpm-lock.yml file to reflect e2e module deps
hazardsoft Sep 5, 2023
025db01
test: add github workflow job to run integration tests
hazardsoft Sep 6, 2023
9f32818
build: use apt-get to install docker compose plugin instead of manual…
hazardsoft Sep 6, 2023
590f074
build: indicate mandatory field "shell" in github action command run
hazardsoft Sep 6, 2023
901ebbc
build: run docker compose plugin installation as root
hazardsoft Sep 6, 2023
bf6ac1f
build: install docker compose plugin manually
hazardsoft Sep 6, 2023
29ec4fa
build: fix docker compose plugin installation
hazardsoft Sep 6, 2023
5ca9c01
build: fix filename to extract docker compose plugin to
hazardsoft Sep 6, 2023
4e27595
build: trying to fix path to integration script
hazardsoft Sep 6, 2023
0e30113
build: add env vars for integration tests job
hazardsoft Sep 6, 2023
da01392
build: add several env vars
hazardsoft Sep 6, 2023
7959078
build: fix DATABSE_URL
hazardsoft Sep 6, 2023
072a293
build: fix DATABASE_URL
hazardsoft Sep 6, 2023
ba603bf
build: add e2e tests to github workflow
hazardsoft Sep 6, 2023
b440964
build: install Playwright with browsers for e2e tests
hazardsoft Sep 6, 2023
aab851c
test: fix auth test in case API_SECRET is not defined
hazardsoft Sep 6, 2023
81ea10b
test: simplify tests runner with github workflow
hazardsoft Sep 6, 2023
8f90d52
test: upload playwright artifact and keep for 3 days
hazardsoft Sep 6, 2023
8bfb512
test: sharding e2e tests
hazardsoft Sep 6, 2023
cf5d545
test: replace pnpm with npm while running playwright
hazardsoft Sep 6, 2023
ed82e15
test: playwright and its deps installed into e2e folder
hazardsoft Sep 6, 2023
70e6a14
test: add debug info about healthcheck script location
hazardsoft Sep 7, 2023
3891c53
test: update script path detection
hazardsoft Sep 7, 2023
9156069
test
hazardsoft Sep 7, 2023
b66da90
test
hazardsoft Sep 7, 2023
8286bbb
test
hazardsoft Sep 7, 2023
5e9efc5
delete
hazardsoft Sep 7, 2023
be7b517
test: add merged blob report
hazardsoft Sep 7, 2023
14b6a51
test: fix shards
hazardsoft Sep 7, 2023
b0ef443
test: get rid of github context
hazardsoft Sep 7, 2023
3296136
test: use playwright docker image to run e2e tests
hazardsoft Sep 7, 2023
fa7fc21
test: remove sudo use
hazardsoft Sep 7, 2023
1240fb4
Revert "test: remove sudo use"
hazardsoft Sep 7, 2023
1087fca
test: remove several secrets
hazardsoft Sep 7, 2023
77df6a3
test: comment use of playwright container
hazardsoft Sep 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actions/database/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: 'Run Database'
description: 'Runs PostgreSQL Database'
runs:
using: composite
steps:
- name: Run Database
shell: bash
run: source ${{ github.workspace }}/scripts/db-startup.sh
14 changes: 14 additions & 0 deletions .github/actions/docker-compose/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: 'Docker-compose'
description: 'Installs docker-compose command'
runs:
using: 'composite'
steps:
- name: Create folder for Docker plugins
shell: bash
run: sudo mkdir -p /usr/local/lib/docker/cli-plugins
- name: Download Docker-Compose plugin
shell: bash
run: sudo curl -SL https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
- name: Make plugin executable
shell: bash
run: sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
9 changes: 9 additions & 0 deletions .github/actions/playwright/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: 'Playwright'
description: 'Install Playwright and all browsers'
runs:
using: composite
steps:
- name: Install Playwright with browsers'
shell: bash
run: pnpm dlx playwright install --with-deps
working-directory: e2e
56 changes: 55 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,65 @@ on:
branches:
- main

env:
VITE_API_URL: http://localhost:3000
API_SECRET: ${{ secrets.API_SECRET }}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_DB: quotes
DATABASE_URL: postgres://postgres:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/quotes
CI: 1

jobs:
unit-tests:
unit-and-integration-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/build
- name: Run unit tests
run: pnpm test:backend:unit
- uses: ./.github/actions/docker-compose
- name: Run integration tests
run: pnpm test:backend:int

e2e-tests:
runs-on: ubuntu-latest
# container: mcr.microsoft.com/playwright:v1.37.0-jammy
strategy:
fail-fast: false
matrix:
shard: [1/4, 2/4, 3/4, 4/4]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/build
- uses: ./.github/actions/docker-compose
- uses: ./.github/actions/playwright
- name: Run e2e tests
run: pnpm test:e2e --shard=${{ matrix.shard }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: all-blob-reports
path: e2e/blob-report
retention-days: 3

merge-e2e-reports:
runs-on: ubuntu-latest
if: always()
needs: [e2e-tests]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/build
- uses: actions/download-artifact@v3
with:
name: all-blob-reports
path: e2e/all-blob-reports
- name: Merge Reports
run: npx playwright merge-reports --reporter=html,github ./all-blob-reports
working-directory: e2e
- name: Upload Merged Reports
uses: actions/upload-artifact@v3
with:
name: merged-report-attempt-${{ github.run_attempt }}
path: e2e/playwright-report
retention-days: 3
4 changes: 2 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"checks:fix": "npm run format && npm run lint",
"test:unit": "vitest -c ./vitest.config.unit.ts",
"test:unit:ui": "vitest -c ./vitest.config.unit.ts --ui",
"test:int": "sh ../scripts/run-integration.sh",
"test:int:ui": "sh ../scripts/run-integration.sh --ui"
"test:int": "../scripts/run-integration.sh",
"test:int:ui": "../scripts/run-integration.sh --ui"
},
"dependencies": {
"bcrypt": "^5.1.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/auth/auth.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe('auth.service', () => {
})

it('should throw an error if there is no API_SECRET env var', () => {
process.env.API_SECRET = undefined
expect(() => AuthService.generateJWT(1)).toThrow()
})
})
Expand All @@ -86,6 +87,7 @@ describe('auth.service', () => {
expect(jwt.verify).toHaveBeenCalledWith('token', 'secret')
})
it('should throw an error if there is no API_SECRET env var', () => {
process.env.API_SECRET = undefined
expect(() => AuthService.validateJWT('')).toThrow()
})
})
Expand Down
22 changes: 17 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
version: '3.8'
services:
db:
image: postgres:14.1-alpine
container_name: prisma-vitest-db
image: postgres:15.4-alpine3.18
healthcheck:
test:
[
'CMD-SHELL',
"sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'"
]
interval: 1s
timeout: 5s
retries: 10
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
ports:
- '5432:5432'
- 5432:5432
volumes:
- db:/var/lib/postgresql/data

volumes:
db:
driver: local
name: prisma-vitest-db
4 changes: 4 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
22 changes: 22 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "e2e",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "../scripts/run-e2e.sh",
"test:headed": "../scripts/run-e2e.sh --headed"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@faker-js/faker": "^8.0.2",
"@playwright/test": "^1.37.1",
"@types/node": "18.11.12",
"typescript": "^5.2.2"
},
"prisma": {
"schema": "../prisma/schema.prisma"
}
}
84 changes: 84 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { defineConfig, devices } from '@playwright/test'

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* 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: process.env.CI ? 'blob' : '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'] }
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
}

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
webServer: [
{
command: 'pnpm run --filter=backend dev',
port: 3000,
reuseExistingServer: true
},
{
command: 'pnpm run --filter=frontend dev',
port: 5173,
reuseExistingServer: true
}
]
})
50 changes: 50 additions & 0 deletions e2e/tests/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import E2eConstants from './constants.js'
import { test, expect } from './fixtures/auth.fixture.js'

test.describe('auth', () => {
test('should redirect unauthorized user to the login page', async ({
page
}) => {
await page.goto(E2eConstants.origin)
await expect(page).toHaveURL(E2eConstants.loginUrl)
})

test('should warn if creds are incorrect', async ({ page, loginPage }) => {
await loginPage.populateForm('incorrect', 'password')
await loginPage.login()
await expect(page.getByText('Account not found')).toBeVisible()
})

test('should warn if form is empty', async ({ page, loginPage }) => {
await loginPage.login()
await expect(
page.getByText('Please enter a username and password')
).toBeVisible()
})

test('should redirect to home page after successful sign up', async ({
page,
loginPage,
user_creds,
storage
}) => {
await loginPage.populateForm(user_creds.username, user_creds.password)
await loginPage.signUp()
const localStorage = await storage.getLocalStorage()
expect(localStorage).toHaveProperty('quoots-user')
await expect(page).toHaveURL(E2eConstants.origin)
})

test('should redirect to home page after successful login', async ({
page,
loginPage,
account,
storage
}) => {
await loginPage.populateForm(account.username, account.password)
await loginPage.login()
const localStorage = await storage.getLocalStorage()
expect(localStorage).toHaveProperty('quoots-user')
await expect(page).toHaveURL(E2eConstants.origin)
})
})
6 changes: 6 additions & 0 deletions e2e/tests/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const E2eConstants = {
origin: 'http://localhost:5173',
loginUrl: 'http://localhost:5173/login'
}

export default E2eConstants
56 changes: 56 additions & 0 deletions e2e/tests/fixtures/auth.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { test as base } from '@playwright/test'
import { faker } from '@faker-js/faker'
import { LoginPage } from '../pages/login.page.js'
import prisma from '../helpers/prisma.js'
import { LocalStorage } from '../helpers/LocalStorage.js'

type AuthFixtures = {
loginPage: LoginPage
user_creds: UserDetails
account: UserDetails
storage: LocalStorage
}

type UserDetails = {
username: string
password: string
}

// const test = base.extend<AuthFixtures>({})

const test = base.extend<AuthFixtures>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page)
await loginPage.goto()

await use(loginPage)
},
// eslint-disable-next-line no-empty-pattern
user_creds: async ({}, use) => {
const user: UserDetails = {
username: faker.internet.userName(),
password: faker.internet.password()
}

await use(user)
await prisma.user.deleteMany({ where: { username: user.username } })
},
account: async ({ browser, user_creds }, use) => {
const page = await browser.newPage()
const loginPage = new LoginPage(page)
await loginPage.goto()
await loginPage.populateForm(user_creds.username, user_creds.password)
await loginPage.signUp()
await page.close()

await use(user_creds)
},
storage: async ({ context }, use) => {
const localStorage = new LocalStorage(context)

await use(localStorage)
}
})

export { test }
export { expect } from '@playwright/test'
Loading
Loading