feat: infraestructura base para E2E tests con Playwright

Proyecto Node.js independiente en e2e/ con Playwright + Chromium headless.
Incluye setup-element.sh para descargar y servir Element Web localmente
(puerto 8090 por defecto, 8080 ocupado por Docker).
Scripts de instalacion y placeholder para ejecucion de tests.
Cierra issue 0022a.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 14:11:58 +00:00
parent dfbea6bffb
commit d04a309313
12 changed files with 340 additions and 2 deletions
+8 -1
View File
@@ -11,4 +11,11 @@ logs/
/agentctl
/dashboard
/verify
/verify
# E2E tests
e2e/node_modules/
e2e/test-results/
e2e/.auth/
e2e/.env
e2e/element-web/
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# install.sh — instalar dependencias para E2E tests
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
E2E_DIR="$REPO_ROOT/e2e"
echo "=== Instalacion de E2E tests ==="
# 1. Verificar Node.js
if ! command -v node &>/dev/null; then
echo "ERROR: Node.js no encontrado."
echo "Instalar Node.js v18+ con:"
echo " curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -"
echo " sudo apt-get install -y nodejs"
exit 1
fi
NODE_VERSION=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_VERSION" -lt 18 ]; then
echo "ERROR: Se requiere Node.js v18+, encontrado v$(node -v)"
exit 1
fi
echo "Node.js $(node -v) OK"
# 2. Instalar dependencias del proyecto
echo "Instalando dependencias npm..."
cd "$E2E_DIR"
npm ci
# 3. Instalar Chromium para Playwright
echo "Instalando Chromium para Playwright..."
npx playwright install chromium
# 4. Instalar dependencias del sistema para Playwright
echo "Instalando dependencias del sistema (requiere sudo)..."
sudo npx playwright install-deps chromium
echo ""
echo "=== Instalacion completa ==="
echo "Siguiente paso: copiar e2e/.env.example a e2e/.env y configurar credenciales"
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# run.sh — ejecutar E2E tests con Playwright
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
E2E_DIR="$REPO_ROOT/e2e"
# Verificar dependencias instaladas
if [ ! -d "$E2E_DIR/node_modules" ]; then
echo "ERROR: node_modules no encontrado. Ejecutar primero:"
echo " ./dev-scripts/e2e/install.sh"
exit 1
fi
# Verificar .env
if [ ! -f "$E2E_DIR/.env" ]; then
echo "ERROR: e2e/.env no encontrado. Crear desde el template:"
echo " cp e2e/.env.example e2e/.env"
echo " # editar e2e/.env con credenciales"
exit 1
fi
echo "Los tests E2E se agregan en el issue 0022c."
echo "Cuando esten listos, ejecutar:"
echo " cd $E2E_DIR && npx playwright test"
+1 -1
View File
@@ -27,6 +27,6 @@ afectados y notas de implementacion.
| 20 | Aislar claude -p del repo | [0020-claude-code-sandbox.md](completed/0020-claude-code-sandbox.md) | completado |
| 21 | Threads default config | (completado via branch) | completado |
| 22 | Tests E2E con Playwright | [0022-e2e-tests-playwright.md](0022-e2e-tests-playwright.md) | pendiente |
| 22a | E2E: Infraestructura base | [0022a-e2e-infra.md](0022a-e2e-infra.md) | pendiente |
| 22a | E2E: Infraestructura base | [0022a-e2e-infra.md](completed/0022a-e2e-infra.md) | completado |
| 22b | E2E: Auth fixtures y helpers | [0022b-e2e-auth-helpers.md](0022b-e2e-auth-helpers.md) | pendiente |
| 22c | E2E: Tests de agentes + docs | [0022c-e2e-agent-tests.md](0022c-e2e-agent-tests.md) | pendiente |
+5
View File
@@ -0,0 +1,5 @@
ELEMENT_URL=http://localhost:8090
MATRIX_HOMESERVER=https://matrix-af2f3d.organic-machine.com
MATRIX_USER=@test-user:matrix-af2f3d.organic-machine.com
MATRIX_PASSWORD=
MATRIX_RECOVERY_KEY=
View File
+92
View File
@@ -0,0 +1,92 @@
{
"name": "agents-e2e",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "agents-e2e",
"version": "1.0.0",
"devDependencies": {
"@playwright/test": "^1.50.0",
"dotenv": "^16.4.7"
}
},
"node_modules/@playwright/test": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "agents-e2e",
"version": "1.0.0",
"private": true,
"description": "E2E tests for agents_and_robots via Playwright + Element Web",
"scripts": {
"test": "npx playwright test",
"test:headed": "npx playwright test --headed",
"test:debug": "npx playwright test --debug"
},
"devDependencies": {
"@playwright/test": "^1.50.0",
"dotenv": "^16.4.7"
}
}
+35
View File
@@ -0,0 +1,35 @@
import { defineConfig, devices } from "@playwright/test";
import * as dotenv from "dotenv";
import * as path from "path";
dotenv.config({ path: path.resolve(__dirname, ".env") });
export default defineConfig({
testDir: "./tests",
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: 1,
reporter: "list",
// LLMs son lentos — timeouts generosos
timeout: 60_000,
expect: { timeout: 30_000 },
use: {
baseURL: process.env.ELEMENT_URL || "http://localhost:8080",
headless: true,
screenshot: "only-on-failure",
trace: "on-first-retry",
actionTimeout: 30_000,
},
globalSetup: "./global-setup.ts",
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
});
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
# setup-element.sh — descargar y servir Element Web localmente
set -euo pipefail
ELEMENT_VERSION="v1.11.92"
ELEMENT_DIR="$(cd "$(dirname "$0")/.." && pwd)/element-web"
PORT="${ELEMENT_PORT:-8090}"
PIDFILE="$ELEMENT_DIR/.server.pid"
HOMESERVER="${MATRIX_HOMESERVER:-https://matrix-af2f3d.organic-machine.com}"
SERVER_NAME="${MATRIX_SERVER_NAME:-matrix-af2f3d.organic-machine.com}"
usage() {
echo "Uso: $0 {start|stop|status}"
echo ""
echo " start Descargar Element Web (si falta) y servir en puerto $PORT"
echo " stop Detener el servidor local"
echo " status Verificar si el servidor esta corriendo"
exit 1
}
download_element() {
if [ -d "$ELEMENT_DIR" ] && [ -f "$ELEMENT_DIR/index.html" ]; then
echo "Element Web ya descargado en $ELEMENT_DIR"
return 0
fi
local tarball="element-${ELEMENT_VERSION}.tar.gz"
local url="https://github.com/element-hq/element-web/releases/download/${ELEMENT_VERSION}/element-${ELEMENT_VERSION}.tar.gz"
echo "Descargando Element Web ${ELEMENT_VERSION}..."
mkdir -p "$ELEMENT_DIR"
curl -fSL "$url" -o "/tmp/$tarball"
tar xzf "/tmp/$tarball" --strip-components=1 -C "$ELEMENT_DIR"
rm -f "/tmp/$tarball"
echo "Generando config.json para homeserver $HOMESERVER..."
cat > "$ELEMENT_DIR/config.json" <<CONF
{
"default_server_config": {
"m.homeserver": {
"base_url": "$HOMESERVER",
"server_name": "$SERVER_NAME"
}
},
"brand": "Element",
"disable_guests": true,
"disable_3pid_login": true
}
CONF
echo "Element Web ${ELEMENT_VERSION} listo en $ELEMENT_DIR"
}
start_server() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Element Web ya corriendo (PID $(cat "$PIDFILE")) en http://localhost:$PORT"
return 0
fi
download_element
echo "Iniciando servidor en http://localhost:$PORT ..."
if command -v python3 &>/dev/null; then
(cd "$ELEMENT_DIR" && python3 -m http.server "$PORT" --bind 0.0.0.0) &>/dev/null &
elif command -v npx &>/dev/null; then
npx --yes serve -s "$ELEMENT_DIR" -l "$PORT" &>/dev/null &
else
echo "Error: necesitas python3 o npx (Node.js) para servir archivos"
exit 1
fi
echo $! > "$PIDFILE"
# Esperar a que el servidor arranque
for i in 1 2 3 4 5; do
if curl -sf "http://localhost:$PORT/" >/dev/null 2>&1; then
echo "Element Web serving en http://localhost:$PORT (PID $!)"
return 0
fi
sleep 1
done
echo "WARN: servidor iniciado (PID $!) pero no responde aun en http://localhost:$PORT"
}
stop_server() {
if [ ! -f "$PIDFILE" ]; then
echo "No hay servidor corriendo (no se encontro pidfile)"
return 0
fi
local pid
pid=$(cat "$PIDFILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
echo "Servidor detenido (PID $pid)"
else
echo "Proceso $pid ya no existe"
fi
rm -f "$PIDFILE"
}
server_status() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Element Web corriendo (PID $(cat "$PIDFILE")) en http://localhost:$PORT"
else
echo "Element Web no esta corriendo"
[ -f "$PIDFILE" ] && rm -f "$PIDFILE"
fi
}
case "${1:-}" in
start) start_server ;;
stop) stop_server ;;
status) server_status ;;
*) usage ;;
esac
View File