Skip to content
Closed
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
94 changes: 75 additions & 19 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@ on:
branches: [ main, develop ]

jobs:
e2e-tests:
timeout-minutes: 60
build:
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: [chromium, firefox, webkit, "Mobile Chrome", "Mobile Safari"]

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -62,8 +58,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'tests/e2e/package-lock.json'

- name: Cache global npm packages
uses: actions/cache@v4
Expand All @@ -90,21 +84,83 @@ jobs:
npm install tailwindcss --no-save
cd ../../tests/e2e && npm install

- name: Cache Playwright Browsers

- name: Build backend (dev mode)
run: cargo build -p kiko-backend

- name: Build frontend (release mode)
run: cd crates/kiko-frontend && RUSTFLAGS="-C target-feature=-nontrapping-fptoint" trunk build --release

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: |
target/debug/kiko-backend
crates/kiko-frontend/dist/
retention-days: 1

e2e-tests:
needs: build
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: [chromium, firefox, webkit]

steps:
- uses: actions/checkout@v4

- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts

- name: Make backend executable
run: chmod +x target/debug/kiko-backend

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Cache Cargo binaries
uses: actions/cache@v4
with:
path: ~/.cargo/bin/
key: ${{ runner.os }}-cargo-bin-trunk-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-bin-trunk-
${{ runner.os }}-cargo-bin-

- name: Install trunk
run: |
if ! command -v trunk &> /dev/null; then
cargo install trunk --locked
fi

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'tests/e2e/package-lock.json'

- name: Install E2E test dependencies
run: cd tests/e2e && npm install

- name: Cache Playwright Browsers with deps
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('tests/e2e/package-lock.json') }}
key: ${{ runner.os }}-playwright-${{ matrix.project }}-${{ hashFiles('tests/e2e/package-lock.json') }}-with-deps
restore-keys: |
${{ runner.os }}-playwright-${{ matrix.project }}-
${{ runner.os }}-playwright-

- name: Install Playwright Browsers
run: cd tests/e2e && npx playwright install --with-deps

- name: Build Rust project
run: |
cargo build --release
cd crates/kiko-frontend && RUSTFLAGS="-C target-feature=-nontrapping-fptoint" trunk build --release
- name: Install Playwright browser with dependencies
run: cd tests/e2e && npx playwright install ${{ matrix.project }} --with-deps

- name: Run E2E tests
run: cd tests/e2e && npx playwright test --project="${{ matrix.project }}"
Expand All @@ -115,14 +171,14 @@ jobs:
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
name: playwright-report-${{ matrix.project }}
path: tests/e2e/playwright-report/
retention-days: 30

- name: Upload test screenshots
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-screenshots
name: test-screenshots-${{ matrix.project }}
path: tests/e2e/test-screenshots/
retention-days: 30
57 changes: 57 additions & 0 deletions bin/ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash

# Simple script to start pre-built servers for CI/E2E testing

set -e

# Kill any existing processes on our target ports
echo "🧹 Cleaning up existing processes..."
lsof -ti:3030 2>/dev/null | xargs kill -9 2>/dev/null || true
lsof -ti:8080 2>/dev/null | xargs kill -9 2>/dev/null || true

# Start backend from pre-built binary
echo "📡 Starting backend server..."
chmod +x ./target/debug/kiko-backend
./target/debug/kiko-backend &
BACKEND_PID=$!

# Wait for backend to be ready
echo "⏳ Waiting for backend to be ready..."
for i in {1..20}; do
if curl -s -f http://localhost:3030/health >/dev/null 2>&1; then
echo "✅ Backend is ready!"
break
fi
if [ $i -eq 20 ]; then
echo "❌ Backend failed to start"
kill $BACKEND_PID 2>/dev/null || true
exit 1
fi
sleep 1
done

# Start frontend using trunk serve in release mode
echo "🎨 Starting frontend server..."
cd crates/kiko-frontend
trunk serve --release --port 8080 &
FRONTEND_PID=$!

# Wait a bit for frontend to start
sleep 2

echo "🎉 Both servers are running!"
echo "📡 Backend: http://localhost:3030"
echo "🎨 Frontend: http://localhost:8080"

# Function to cleanup on exit
cleanup() {
echo "🛑 Shutting down servers..."
kill $BACKEND_PID 2>/dev/null || true
kill $FRONTEND_PID 2>/dev/null || true
exit 0
}

trap cleanup EXIT INT TERM

# Keep script running
wait
5 changes: 4 additions & 1 deletion crates/kiko-frontend/src/components/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ pub fn header() -> Html {
html! {
<header class="px-6 py-3 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<div class="max-w-7xl mx-auto flex justify-between items-center">
<h1 class="text-lg font-bold">{ "Kiko Pointing Poker" }</h1>
<a href="/" alt="Back to homepage" class="text-lg font-bold flex gap-x-4">
<div>{"Kiko"}</div>
<div>{"🫵"}</div>
</a>
<ThemeToggle />
</div>
</header>
Expand Down
1 change: 0 additions & 1 deletion tests/e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"test:e2e:debug": "playwright test --debug",
"test:e2e:ui": "playwright test --ui",
"test:report": "playwright show-report",
"postinstall": "playwright install"
"install:browsers": "playwright install"
},
"devDependencies": {
"@playwright/test": "^1.40.0"
Expand Down
18 changes: 5 additions & 13 deletions tests/e2e/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ module.exports = defineConfig({
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Use more workers on CI for faster execution */
workers: process.env.CI ? 4 : undefined,
/* Use single worker on CI to avoid resource contention */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [["html", { open: "never" }]],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
Expand All @@ -39,25 +39,17 @@ module.exports = defineConfig({
use: { ...devices["Desktop Firefox"] },
},
{
name: "webkit",
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
{
name: "Mobile Chrome",
use: { ...devices["Pixel 7"] },
},
{
name: "Mobile Safari",
use: { ...devices["iPhone 15"] },
},
],

/* Run your local dev server before starting the tests */
webServer: {
command: "./bin/dev",
command: process.env.CI ? "./bin/ci" : "./bin/dev",
cwd: "../..",
url: "http://localhost:8080",
reuseExistingServer: !process.env.CI,
timeout: 3 * 60 * 1000, // 3 minutes for server startup
timeout: process.env.CI ? 60 * 1000 : 3 * 60 * 1000, // 1 minute for CI, 3 minutes for dev
},
});
3 changes: 2 additions & 1 deletion tests/e2e/src/home-page.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ test.describe('Home Page', () => {
});

test('should display the main heading', async ({ page }) => {
await expect(page.locator('h1')).toHaveText('Kiko Pointing Poker');
await expect(page.locator('header a')).toContainText('Kiko');
await expect(page.locator('header a')).toContainText('🫵');
});

test('should display create session form', async ({ page }) => {
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/src/session-complete.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ test.describe("Complete Session Tests", () => {

// Should show main header
await expect(
page.getByRole("heading", { name: "Kiko Pointing Poker" })
page.locator('header a')
).toBeVisible();

// Should show join form since we're not joined
Expand Down Expand Up @@ -433,7 +433,7 @@ test.describe("Complete Session Tests", () => {
// If no connection status visible, at least verify the page loaded
if (!foundConnection) {
await expect(
page.getByRole("heading", { name: "Kiko Pointing Poker" })
page.locator('header a')
).toBeVisible();
}
});
Expand All @@ -451,7 +451,7 @@ test.describe("Complete Session Tests", () => {

// Session should still be functional
await expect(
page.getByRole("heading", { name: "Kiko Pointing Poker" })
page.locator('header a')
).toBeVisible();
});
});
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/src/session-page.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test.describe("Session Page", () => {
await page.goto("/session/nonexistent-session");

// The page should load with the main header
await expect(page.locator("h1")).toHaveText("Kiko Pointing Poker");
await expect(page.locator('header a')).toContainText('Kiko');
});

test("should show loading state initially", async ({ page }) => {
Expand Down Expand Up @@ -102,7 +102,7 @@ test.describe("Session Page", () => {

// If no specific connection text found, at least check that the page loaded
if (!foundConnectionText) {
await expect(page.locator("h1")).toHaveText("Kiko Pointing Poker");
await expect(page.locator('header a')).toContainText('Kiko');
}
});

Expand Down
Loading