Skip to content
Merged
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
79 changes: 79 additions & 0 deletions .github/scripts/start-background-service.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash

set -euo pipefail

service_name="${BACKGROUND_SERVICE_NAME:-}"
run_command="${BACKGROUND_RUN_COMMAND:-}"
wait_on_resources_raw="${BACKGROUND_WAIT_ON:-}"
log_path="${BACKGROUND_LOG_PATH:-}"
workdir="${BACKGROUND_WORKDIR:-$PWD}"
wait_timeout_ms="${BACKGROUND_WAIT_TIMEOUT_MS:-60000}"
wait_interval_ms="${BACKGROUND_WAIT_INTERVAL_MS:-500}"
tail_lines="${BACKGROUND_TAIL_LINES:-200}"
wait_on_version="${BACKGROUND_WAIT_ON_VERSION:-8.0.1}"

if [[ -z "${service_name}" || -z "${run_command}" || -z "${wait_on_resources_raw}" || -z "${log_path}" ]]; then
printf '%s\n' "::error::BACKGROUND_SERVICE_NAME, BACKGROUND_RUN_COMMAND, BACKGROUND_WAIT_ON, and BACKGROUND_LOG_PATH are required." >&2
exit 1
fi

mkdir -p "$(dirname "${log_path}")"
: > "${log_path}"

wait_on_resources=()
while IFS= read -r resource; do
if [[ -n "${resource}" ]]; then
wait_on_resources+=("${resource}")
fi
done < <(printf '%s\n' "${wait_on_resources_raw}" | sed '/^[[:space:]]*$/d')

if [[ "${#wait_on_resources[@]}" -eq 0 ]]; then
printf '%s\n' "::error::${service_name} is missing wait-on resources." >&2
exit 1
fi

dump_log_tail() {
if [[ ! -f "${log_path}" ]]; then
echo "No log file found at ${log_path}"
return
fi

echo "::group::${service_name} log tail"
tail -n "${tail_lines}" "${log_path}" || true
echo "::endgroup::"
}

echo "::group::Start ${service_name}"
echo "Working directory: ${workdir}"
echo "Log file: ${log_path}"
printf 'Wait-on targets:\n'
printf ' - %s\n' "${wait_on_resources[@]}"
echo "::endgroup::"

pushd "${workdir}" >/dev/null
nohup bash -lc "${run_command}" >"${log_path}" 2>&1 &
pid=$!
disown "${pid}" 2>/dev/null || true
popd >/dev/null

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
{
echo "pid=${pid}"
echo "log_path=${log_path}"
} >> "${GITHUB_OUTPUT}"
fi

sleep 1
if ! kill -0 "${pid}" 2>/dev/null; then
printf '%s\n' "::error::${service_name} exited before it became ready." >&2
dump_log_tail
exit 1
fi

if ! bunx "wait-on@${wait_on_version}" "${wait_on_resources[@]}" --timeout "${wait_timeout_ms}" --interval "${wait_interval_ms}" --log --verbose; then
printf '%s\n' "::error::${service_name} failed to become ready." >&2
dump_log_tail
exit 1
fi

echo "::notice::${service_name} is ready (pid ${pid})."
158 changes: 125 additions & 33 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permissions: {}

env:
DENO_DIR: my_cache_directory
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
test_all:
Expand Down Expand Up @@ -86,49 +86,65 @@ jobs:
run: supabase test db
- name: Lint SQL
run: supabase db lint -s public --fail-on warning
- uses: JarvusInnovations/background-action@v1
- id: bootstrap_edge_server
name: Bootstrap Edge server
with:
run: supabase functions serve &
wait-on: |
env:
BACKGROUND_SERVICE_NAME: Bootstrap Edge server
BACKGROUND_RUN_COMMAND: supabase functions serve
BACKGROUND_WAIT_ON: |
http-get://127.0.0.1:54321/functions/v1/ok
# IMPORTANT: to use environment variables in wait-on, you must use this form: ${{ env.VAR }}
# See wait-on section below for all resource types and prefixes

tail: stderr,stdout # true = stderr,stdout
# This will allow you to monitor the progress live

log-output-resume: stderr,stdout
# Eliminates previously output stderr log entries from post-run output

wait-for: 1m

log-output: stderr,stdout # same as true

log-output-if: true
# failure = exit-early or timeout

working-directory: .
BACKGROUND_LOG_PATH: ${{ runner.temp }}/bootstrap-edge-server.log
BACKGROUND_WAIT_TIMEOUT_MS: 60000
run: bash .github/scripts/start-background-service.sh
- name: Run all backend and CLI tests
run: bun run test:all
- uses: JarvusInnovations/background-action@v1
- name: Show Edge server logs after backend test failure
if: failure() && steps.bootstrap_edge_server.outputs.log_path != ''
run: |
echo "::group::Bootstrap Edge server log"
tail -n 200 "${{ steps.bootstrap_edge_server.outputs.log_path }}" || true
echo "::endgroup::"
- id: start_cloudflare_workers
name: Start Cloudflare Workers for testing
with:
run: |
env:
BACKGROUND_SERVICE_NAME: Cloudflare Workers
BACKGROUND_RUN_COMMAND: |
chmod +x scripts/start-cloudflare-workers.sh
./scripts/start-cloudflare-workers.sh
wait-on: |
BACKGROUND_WAIT_ON: |
http-get://127.0.0.1:8787/ok
http-get://127.0.0.1:8788/ok
http-get://127.0.0.1:8789/ok
tail: stderr,stdout
log-output-resume: stderr,stdout
wait-for: 2m
log-output: stderr,stdout
log-output-if: true
working-directory: .
BACKGROUND_LOG_PATH: ${{ runner.temp }}/cloudflare-workers.log
BACKGROUND_WAIT_TIMEOUT_MS: 120000
BACKGROUND_TAIL_LINES: 400
run: bash .github/scripts/start-background-service.sh
- name: Run Cloudflare Workers backend tests
run: bun run test:cloudflare:backend
- name: Show Cloudflare Worker logs after Cloudflare test failure
if: failure() && steps.start_cloudflare_workers.outputs.log_path != ''
run: |
echo "::group::Cloudflare Worker log"
tail -n 400 "${{ steps.start_cloudflare_workers.outputs.log_path }}" || true
echo "::endgroup::"
- name: Upload background service logs
if: failure()
uses: actions/upload-artifact@v6
with:
name: test-all-background-service-logs
path: |
${{ steps.bootstrap_edge_server.outputs.log_path }}
${{ steps.start_cloudflare_workers.outputs.log_path }}
if-no-files-found: ignore
retention-days: 7
- name: Stop background services
if: always()
run: |
for pid in "${{ steps.bootstrap_edge_server.outputs.pid }}" "${{ steps.start_cloudflare_workers.outputs.pid }}"; do
if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then
kill "${pid}" 2>/dev/null || true
fi
done

test_playwright:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -160,8 +176,76 @@ jobs:
version: 2.84.2
- name: Link Supabase templates
run: ln -sfn supabase/templates templates
- id: start_playwright_stripe_emulator
name: Start Stripe emulator for Playwright
env:
BACKGROUND_SERVICE_NAME: Stripe emulator
BACKGROUND_RUN_COMMAND: STRIPE_EMULATOR_PORT=4510 bun run stripe:emulator
BACKGROUND_WAIT_ON: |
tcp:127.0.0.1:4510
BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-stripe-emulator.log
BACKGROUND_WAIT_TIMEOUT_MS: 60000
BACKGROUND_TAIL_LINES: 300
run: bash .github/scripts/start-background-service.sh
- id: start_playwright_backend
name: Start Playwright backend
env:
BACKGROUND_SERVICE_NAME: Playwright backend
BACKGROUND_RUN_COMMAND: |
rm -f .context/playwright/gha-backend.ready
PLAYWRIGHT_READY_FILE=.context/playwright/gha-backend.ready \
ENV=local \
STRIPE_SECRET_KEY=sk_test_emulator \
STRIPE_API_BASE_URL=http://host.docker.internal:4510 \
STRIPE_WEBHOOK_SECRET=testsecret \
WEBAPP_URL=http://localhost:5173 \
bun run backend:playwright
BACKGROUND_WAIT_ON: |
file:${{ github.workspace }}/.context/playwright/gha-backend.ready
BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-backend.log
BACKGROUND_WAIT_TIMEOUT_MS: 360000
BACKGROUND_TAIL_LINES: 400
run: bash .github/scripts/start-background-service.sh
- id: start_playwright_frontend
name: Start Playwright frontend
env:
BACKGROUND_SERVICE_NAME: Playwright frontend
BACKGROUND_RUN_COMMAND: CAPTCHA_KEY='' bun run serve:worktree
BACKGROUND_WAIT_ON: |
http-get://localhost:5173
BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-frontend.log
BACKGROUND_WAIT_TIMEOUT_MS: 360000
BACKGROUND_TAIL_LINES: 300
run: bash .github/scripts/start-background-service.sh
- name: Run Playwright tests
run: bun run test:front
env:
SKIP_STRIPE_EMULATOR_START: "true"
SKIP_BACKEND_START: "true"
SKIP_FRONTEND_START: "true"
run: bunx playwright test
- name: Show Playwright service logs after failure
if: failure()
run: |
echo "::group::Stripe emulator log"
tail -n 200 "${{ steps.start_playwright_stripe_emulator.outputs.log_path }}" || true
echo "::endgroup::"
echo "::group::Playwright backend log"
tail -n 300 "${{ steps.start_playwright_backend.outputs.log_path }}" || true
echo "::endgroup::"
echo "::group::Playwright frontend log"
tail -n 200 "${{ steps.start_playwright_frontend.outputs.log_path }}" || true
echo "::endgroup::"
- name: Upload Playwright background service logs
if: failure()
uses: actions/upload-artifact@v6
with:
name: playwright-background-service-logs
path: |
${{ steps.start_playwright_stripe_emulator.outputs.log_path }}
${{ steps.start_playwright_backend.outputs.log_path }}
${{ steps.start_playwright_frontend.outputs.log_path }}
if-no-files-found: ignore
retention-days: 7
- name: Upload Playwright artifacts
if: failure()
uses: actions/upload-artifact@v6
Expand All @@ -172,3 +256,11 @@ jobs:
test-results/
if-no-files-found: ignore
retention-days: 7
- name: Stop Playwright background services
if: always()
run: |
for pid in "${{ steps.start_playwright_frontend.outputs.pid }}" "${{ steps.start_playwright_backend.outputs.pid }}" "${{ steps.start_playwright_stripe_emulator.outputs.pid }}"; do
if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then
kill "${pid}" 2>/dev/null || true
fi
done
27 changes: 18 additions & 9 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { PlaywrightTestConfig } from '@playwright/test'
import { env } from 'node:process'
import { defineConfig, devices } from '@playwright/test'
import { getPlaywrightStripeApiBaseUrl, getStripeEmulatorPort } from './scripts/playwright-stripe'
import {
getPlaywrightStripeApiBaseUrl,
getStripeEmulatorPort,
} from './scripts/playwright-stripe'
import { getSupabaseWorktreeConfig } from './scripts/supabase-worktree-config'

/**
Expand All @@ -21,7 +24,8 @@ const webServer: PlaywrightTestConfig['webServer'] = []
const { ports: supabasePorts } = getSupabaseWorktreeConfig()
const localSupabaseUrl = `http://127.0.0.1:${supabasePorts.api}`
const localApiDomain = `127.0.0.1:${supabasePorts.api}/functions/v1`
const localSupabaseAnonKey = env.SUPABASE_ANON_KEY || 'sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH'
const localSupabaseAnonKey
= env.SUPABASE_ANON_KEY || 'sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH'
const localStripeEmulatorPort = getStripeEmulatorPort(env)
const localStripeApiBaseUrl = getPlaywrightStripeApiBaseUrl(env)

Expand Down Expand Up @@ -50,13 +54,18 @@ else {
console.log('Skipping backend server')
}

webServer.push({
command: `ENV=local SUPA_URL=${localSupabaseUrl} SUPA_ANON=${localSupabaseAnonKey} API_DOMAIN=${localApiDomain} CAPTCHA_KEY='' bun run serve:local`,
port: 5173,
timeout: webServerTimeout,
reuseExistingServer,
stdout: 'pipe',
})
if (env.SKIP_FRONTEND_START) {
console.log('Skipping frontend server')
}
else {
webServer.push({
command: `ENV=local SUPA_URL=${localSupabaseUrl} SUPA_ANON=${localSupabaseAnonKey} API_DOMAIN=${localApiDomain} CAPTCHA_KEY='' bun run serve:local`,
port: 5173,
timeout: webServerTimeout,
reuseExistingServer,
stdout: 'pipe',
})
}

/**
* See https://playwright.dev/docs/test-configuration.
Expand Down
Loading