Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Run e2e tests
on:
workflow_dispatch:
pull_request:
branches:
- main

jobs:
test:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # ratchet:actions/checkout@v7
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # ratchet:pnpm/action-setup@v6
with:
package_json_file: ./e2e/package.json

- name: Set up Docker
uses: docker/setup-docker-action@6d7cfa65f60a9dda7b46e5513fa982536f3c9877 # ratchet:docker/setup-docker-action@v5

- name: Install dependencies
run: pnpm ci
working-directory: e2e

- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
working-directory: e2e

- name: Run Playwright tests
run: pnpm test
working-directory: e2e

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: e2e/playwright-report/
retention-days: 5
7 changes: 7 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# playwright files
/node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
15 changes: 15 additions & 0 deletions e2e/conf/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
auto_https off
}

http://whoami.127.0.0.1.sslip.io {
forward_auth tinyauth:3000 {
uri /api/auth/caddy
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
}
reverse_proxy whoami:80
}

http://tinyauth.127.0.0.1.sslip.io {
reverse_proxy tinyauth:3000
}
22 changes: 22 additions & 0 deletions e2e/config.e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
appUrl: http://tinyauth.127.0.0.1.sslip.io

log:
level: debug

auth:
users:
# user1:password,user2:password,user3:password:token
- user1:$2a$10$h1laww4k5a4bJcG5KwE3nO45YKSC4mOKHxbcccgxr3Y7H9zHlQe8e
- user2:$2a$10$h1laww4k5a4bJcG5KwE3nO45YKSC4mOKHxbcccgxr3Y7H9zHlQe8e
- user3:$2a$10$h1laww4k5a4bJcG5KwE3nO45YKSC4mOKHxbcccgxr3Y7H9zHlQe8e:MVR4JQWNXYKNM6HHJEYEFP2O74QIIEJE
# disable rate limits for multiple workers to work
loginMaxRetries: 0

apps:
whoami:
config:
domain: whoami.127.0.0.1.sslip.io
path:
allow: /foo
users:
allow: user1
24 changes: 24 additions & 0 deletions e2e/docker-compose.e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
caddy:
image: caddy:2.11.4
pull_policy: missing
ports:
- 80:80
volumes:
- ./conf:/etc/caddy

whoami:
image: traefik/whoami:v1.11.0
pull_policy: missing

tinyauth:
build:
context: ../
dockerfile: Dockerfile
args:
- VERSION=e2e
- BUILD_TAGS=nomsgpack
- LDFLAGS=-s -w
command: ["--configfile", "/app/config.yaml"]
volumes:
- ./config.e2e.yaml:/app/config.yaml:ro
47 changes: 47 additions & 0 deletions e2e/fixtures/auth.fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {expect, Page} from '@playwright/test';
import { OTP } from 'otplib';

export class LoginFixture {
constructor(public readonly page: Page) {}

async run(username: string, password: string) {
await expect(this.page.getByText('Welcome back, please login')).toBeVisible();
await this.page.getByLabel('Username').fill(username);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Login' }).click();
}

async expectSuccess(username: string) {
await expect(this.page.getByText(`You are currently logged in as ${username}.`)).toBeVisible()
}
}

export class LogoutFixture {
constructor(public readonly page: Page) {}

async run() {
await expect(this.page.getByText('Click the button below to logout.')).toBeVisible();
await this.page.getByRole('button', { name: 'Logout' }).click();
}

async expectSuccess() {
await expect(this.page.getByText('Welcome back, please login')).toBeVisible();
}
}

export class TOTPFixture {
constructor(public readonly page: Page) {}

async run(secret: string) {
await expect(this.page.getByText('Enter your TOTP code')).toBeVisible();
const otp = new OTP();
const token = await otp.generate({ secret });
await this.page.getByPlaceholder('XXXXXX').fill(token);
// we shouldn't need to click continue, it will auto submit
// await this.page.getByRole('button', { name: 'Continue' }).click();
}

async expectSuccess(username: string) {
await expect(this.page.getByText(`You are currently logged in as ${username}.`)).toBeVisible()
}
}
30 changes: 30 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "e2e",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"down": "docker compose -f docker-compose.e2e.yml down",
"up": "docker compose -f docker-compose.e2e.yml up --build --force-recreate --remove-orphans",
"test": "playwright test",
"report": "playwright show-report"
},
"keywords": [],
"author": "",
"license": "ISC",
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "^11.1.2",
"onFail": "download"
}
},
"type": "module",
"devDependencies": {
"@playwright/test": "^1.61.1",
"@types/node": "^26.1.1"
},
"dependencies": {
"otplib": "^13.4.1"
}
}
49 changes: 49 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './specs',
fullyParallel: true,
forbidOnly: false,
retries: 0,
workers: 4,
reporter: 'html',
use: {
trace: 'on-first-retry',
video: 'on',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
},
],
webServer: {
command: 'pnpm run up',
url: 'http://tinyauth.127.0.0.1.sslip.io/api/healthz',
reuseExistingServer: true,
timeout: 5 * 60 * 1000,
gracefulShutdown: {
signal: 'SIGINT',
timeout: 1000,
},
stderr: 'pipe',
stdout: 'pipe',
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
Loading
Loading