Files
Oleg Isonen 0a4e3a48dd docs: Explain Form capabilities with focused screenshots (#6591)
## Summary

Explain what the Form component can do, with short chapters for actions,
email, uploads, search and filters, submission feedback, and Preview
testing.

- Replace the old guide with the reviewed text.
- Add five tightly cropped screenshots, each in light and dark themes.
- Update links from the Variables and Webhook Form guides to the new
sections.

## Checklist

- [x] Update the Form documentation and review its behavior claims
against the implementation.
- [x] Add five matching light/dark screenshot pairs with descriptive alt
text.
- [x] Verify 21 local links and anchors, all 10 image references,
formatting, and the final diff.
- [x] Fix the CI database port conflict in browser shard 2.
- [x] Re-run the shard 3 email tests locally with the CI build and
schema artifacts: 4/4 passed in 61 seconds.
- [x] Use sanitized CI diagnostics to investigate the MCP submit
failures, then remove all temporary diagnostic code.
- [x] Pin the MCP test fixture to installed Playwright Chromium and run
E2E shards with two workers.
- [x] Review documentation impact of CI changes: no additional docs are
needed.
- [x] Confirm required CI checks pass.

The CI follow-up keeps test setup stable: Docker assigns free PostgreSQL
and PostgREST host ports, the MCP test fixture uses the installed
Playwright Chromium, and each E2E shard uses two workers. Local startup
passed with port 55434 occupied; the focused email tests passed 4/4. The
final clean CI run passed all ten E2E shards, Main, Visual regression,
Lint PR, and Check submodules. Temporary diagnostics have been removed.
The exact cause of the earlier browser startup failures was not proven.
These CI changes do not change Form behavior.
2026-10-24 15:01:02 +02:00

240 lines
7.4 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
mode="${1:-run}"
if [ "$#" -gt 0 ]; then
shift
fi
case "$mode" in
run)
;;
dev-backend)
export E2E_SKIP_CLEANUP=true
export E2E_RUN_TESTS=false
export E2E_START_POSTGREST=true
export E2E_DB_BOOTSTRAP=if-empty
export E2E_INSTALL_PLAYWRIGHT=false
;;
dev)
export E2E_SKIP_CLEANUP=true
export E2E_DB_BOOTSTRAP=if-empty
export E2E_INSTALL_PLAYWRIGHT=false
export E2E_BUILDER_URL="${E2E_BUILDER_URL:-https://127.0.0.1:3000}"
;;
schema-cache)
export E2E_DB_BOOTSTRAP=migrations
export E2E_WRITE_SCHEMA_SNAPSHOT=true
export E2E_RUN_TESTS=false
;;
supabase-migrations)
export E2E_BACKEND_MODE=supabase
export E2E_BUILD_DATABASE_IMAGE=false
export E2E_DB_BOOTSTRAP=migrations
export E2E_RUN_TESTS=false
;;
ci-shard)
export E2E_BACKEND_MODE=snapshot
export E2E_BUILD_DATABASE_IMAGE=false
export E2E_SKIP_BUILDER_BUILD=true
export E2E_SKIP_CLEANUP=true
export E2E_PGPORT="${E2E_PGPORT:-0}"
export E2E_POSTGREST_PORT="${E2E_POSTGREST_PORT:-0}"
;;
*)
echo "Unknown Builder E2E mode: $mode" >&2
exit 1
;;
esac
PLAYWRIGHT_ARGS=("$@")
if [ "${PLAYWRIGHT_ARGS[0]:-}" = "--" ]; then
PLAYWRIGHT_ARGS=("${PLAYWRIGHT_ARGS[@]:1}")
fi
export PGPORT="${E2E_PGPORT:-55434}"
export POSTGREST_PORT="${E2E_POSTGREST_PORT:-55435}"
export POSTGRES_DB="${E2E_POSTGRES_DB:-webstudio}"
# The Supabase image owns the schema as this required bootstrap role. Callers
# can still replace migration connections through E2E_DATABASE_URL and
# E2E_DIRECT_URL.
export POSTGRES_USER="supabase_admin"
export POSTGRES_PASSWORD="${E2E_POSTGRES_PASSWORD:-pass}"
export E2E_BACKEND_MODE="${E2E_BACKEND_MODE:-test}"
source "$ROOT_DIR/apps/builder/backend/lib.sh"
source "$ROOT_DIR/apps/builder/e2e/run-step.sh"
builder_backend_init "$E2E_BACKEND_MODE"
builder_backend_init_schema_snapshot
export E2E_DB_BOOTSTRAP="${E2E_DB_BOOTSTRAP:-auto}"
export E2E_BUILD_DATABASE_IMAGE="${E2E_BUILD_DATABASE_IMAGE:-true}"
export E2E_GENERATE_PRISMA="${E2E_GENERATE_PRISMA:-auto}"
export E2E_DOCKER_PULL_TIMEOUT_SECONDS="${E2E_DOCKER_PULL_TIMEOUT_SECONDS:-300}"
export E2E_DOCKER_TIMEOUT_SECONDS="${E2E_DOCKER_TIMEOUT_SECONDS:-60}"
export E2E_MIGRATIONS_TIMEOUT_SECONDS="${E2E_MIGRATIONS_TIMEOUT_SECONDS:-300}"
export E2E_INSTALL_PLAYWRIGHT="${E2E_INSTALL_PLAYWRIGHT:-auto}"
export E2E_PLAYWRIGHT_INSTALL_TIMEOUT_SECONDS="${E2E_PLAYWRIGHT_INSTALL_TIMEOUT_SECONDS:-600}"
export E2E_RUN_TESTS="${E2E_RUN_TESTS:-true}"
export E2E_START_POSTGREST="${E2E_START_POSTGREST:-$E2E_RUN_TESTS}"
export E2E_BUILDER_BUILD_TIMEOUT_SECONDS="${E2E_BUILDER_BUILD_TIMEOUT_SECONDS:-600}"
export E2E_TEST_COMMAND_TIMEOUT_SECONDS="${E2E_TEST_COMMAND_TIMEOUT_SECONDS:-900}"
export E2E_TEST_SELECTION_TIMEOUT_SECONDS="${E2E_TEST_SELECTION_TIMEOUT_SECONDS:-60}"
export E2E_WRITE_SCHEMA_SNAPSHOT="${E2E_WRITE_SCHEMA_SNAPSHOT:-false}"
cleanup() {
if [ "${E2E_SKIP_CLEANUP:-}" = "true" ]; then
return
fi
builder_backend_down --volumes --remove-orphans
}
trap cleanup EXIT
bootstrap_database() {
if [ "$E2E_DB_BOOTSTRAP" = "if-empty" ]; then
builder_backend_bootstrap_if_empty
else
builder_backend_bootstrap "$E2E_DB_BOOTSTRAP"
fi
if [ "$E2E_WRITE_SCHEMA_SNAPSHOT" = "true" ]; then
builder_backend_write_schema_snapshot
fi
}
install_playwright_chromium() {
chromium_executable_exists() {
pnpm --dir "$ROOT_DIR" --filter=@webstudio-is/builder exec node -e '
const { chromium } = require("@playwright/test");
const { existsSync } = require("node:fs");
process.exit(existsSync(chromium.executablePath()) ? 0 : 1);
' >/dev/null 2>&1
}
case "$E2E_INSTALL_PLAYWRIGHT" in
true)
pnpm --dir "$ROOT_DIR" --filter=@webstudio-is/builder exec playwright install --with-deps chromium
;;
auto)
if chromium_executable_exists; then
echo "Skipping Playwright Chromium install; cached executable already exists"
else
pnpm --dir "$ROOT_DIR" --filter=@webstudio-is/builder exec playwright install --with-deps chromium
fi
;;
false)
echo "Skipping Playwright Chromium install"
;;
*)
echo "Unknown E2E_INSTALL_PLAYWRIGHT value: $E2E_INSTALL_PLAYWRIGHT" >&2
exit 1
;;
esac
}
build_e2e_apps() {
pnpm --dir "$ROOT_DIR" e2e:builder:build
}
verify_e2e_apps_built() {
local builder_server="$ROOT_DIR/apps/builder/build/server"
local builder_assets="$ROOT_DIR/apps/builder/build/client/assets"
if [ ! -d "$builder_server" ] || [ ! -d "$builder_assets" ]; then
echo "E2E_SKIP_BUILDER_BUILD requires prebuilt Builder artifacts" >&2
return 1
fi
}
run_builder_e2e_tests() {
(
cd "$ROOT_DIR/apps/builder"
if [ "${#PLAYWRIGHT_ARGS[@]}" -eq 0 ]; then
pnpm e2e:ci
else
pnpm e2e:ci "${PLAYWRIGHT_ARGS[@]}"
fi
)
}
validate_builder_e2e_selection() {
(
cd "$ROOT_DIR/apps/builder"
export E2E_BUILDER_URL="${E2E_BUILDER_URL:-https://127.0.0.1:3000}"
if [ "${#PLAYWRIGHT_ARGS[@]}" -eq 0 ]; then
pnpm e2e:ci --list
else
pnpm e2e:ci --list "${PLAYWRIGHT_ARGS[@]}"
fi
)
}
prepare_e2e_database_image() {
if [ "$E2E_BUILD_DATABASE_IMAGE" = "true" ]; then
builder_compose build --pull db
return
fi
builder_backend_pull_db
}
if [ "$E2E_RUN_TESTS" = "true" ]; then
run_step "validate builder e2e test selection" \
"$E2E_TEST_SELECTION_TIMEOUT_SECONDS" validate_builder_e2e_selection
fi
run_step "prepare e2e database image" "$E2E_DOCKER_PULL_TIMEOUT_SECONDS" \
prepare_e2e_database_image
run_step "start e2e database" "$E2E_DOCKER_TIMEOUT_SECONDS" \
builder_backend_start_db
db_port="$PGPORT"
if [ "$db_port" = 0 ]; then
db_port="$(builder_compose port db 5432)"
db_port="${db_port##*:}"
fi
export DATABASE_URL="${E2E_DATABASE_URL:-postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${db_port}/${POSTGRES_DB}?pgbouncer=true}"
export DIRECT_URL="${E2E_DIRECT_URL:-postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${db_port}/${POSTGRES_DB}}"
run_step "generate prisma client" "$E2E_MIGRATIONS_TIMEOUT_SECONDS" \
builder_generate_prisma_client "$E2E_GENERATE_PRISMA"
run_step "wait for e2e database" "$E2E_DOCKER_TIMEOUT_SECONDS" \
builder_backend_wait_for_db "$E2E_DOCKER_TIMEOUT_SECONDS"
run_step "bootstrap database schema" "$E2E_MIGRATIONS_TIMEOUT_SECONDS" \
bootstrap_database
if [ "$E2E_START_POSTGREST" = "true" ]; then
run_step "start e2e postgrest" "$E2E_DOCKER_TIMEOUT_SECONDS" \
builder_backend_start_postgrest
fi
postgrest_port="$POSTGREST_PORT"
if [ "$E2E_START_POSTGREST" = "true" ] && [ "$postgrest_port" = 0 ]; then
postgrest_port="$(builder_compose port postgrest 3000)"
postgrest_port="${postgrest_port##*:}"
fi
export POSTGREST_URL="${E2E_POSTGREST_URL:-http://localhost:${postgrest_port}}"
if [ "$E2E_RUN_TESTS" = "true" ]; then
run_step "install playwright chromium" "$E2E_PLAYWRIGHT_INSTALL_TIMEOUT_SECONDS" \
install_playwright_chromium
if [ "${E2E_BUILDER_URL:-}" = "" ]; then
if [ "${E2E_SKIP_BUILDER_BUILD:-false}" = "true" ]; then
run_step "verify prebuilt builder artifacts" "$E2E_BUILDER_BUILD_TIMEOUT_SECONDS" \
verify_e2e_apps_built
else
run_step "build builder and generated preview dependencies" "$E2E_BUILDER_BUILD_TIMEOUT_SECONDS" \
build_e2e_apps
fi
fi
run_step "run builder e2e tests" "$E2E_TEST_COMMAND_TIMEOUT_SECONDS" \
run_builder_e2e_tests
fi