Skip to content

Commit 99b147e

Browse files
authored
Merge pull request #41 from gensyn/copilot/add-playwright-tests-task-tracker
Add Playwright E2E tests for SSH Docker integration
2 parents bcefbfe + 8d4b4f6 commit 99b147e

28 files changed

Lines changed: 2671 additions & 0 deletions

.github/workflows/hassfest.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
- name: "Move files to where hassfest expects them"
2121
run: |
2222
mkdir -p custom_components/ssh_docker
23+
rm -rf tests
2324
find . -maxdepth 1 -not -name 'custom_components' -not -name '.' -exec mv {} custom_components/ssh_docker/ \;
2425
2526
- name: "Run hassfest"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Playwright E2E Tests
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
playwright-e2e:
8+
runs-on: ubuntu-latest
9+
permissions:
10+
contents: read
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Build Docker images
16+
run: docker compose build
17+
18+
- name: Run Playwright E2E tests
19+
# `docker compose run` starts the declared dependencies (homeassistant,
20+
# docker-host) and then runs the playwright-tests container.
21+
# The exit code of the run command mirrors the test container's exit code.
22+
run: docker compose run --rm playwright-tests
23+
24+
- name: Stop services
25+
if: always()
26+
run: docker compose down -v
27+
28+
- name: Upload test results
29+
if: always()
30+
uses: actions/upload-artifact@v4
31+
with:
32+
name: playwright-e2e-results
33+
path: playwright-results/
34+
if-no-files-found: ignore

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ htmlcov
44
.coverage
55
custom_components/
66
.pytest_cache/
7+
playwright-results/
8+
docker-compose.override.yaml
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# docker-compose.override.yaml.example
2+
#
3+
# Override file for **local development and PyCharm debugging**.
4+
# Copy this file to docker-compose.override.yaml (which is git-ignored) to
5+
# expose the HA and docker-host ports on your local machine so that the
6+
# Playwright tests can be run and debugged directly from your IDE.
7+
#
8+
# Usage:
9+
# cp docker-compose.override.yaml.example docker-compose.override.yaml
10+
# docker compose up -d homeassistant docker-host
11+
# # Then run pytest from PyCharm (see tests/playwright/README.md)
12+
13+
services:
14+
homeassistant:
15+
ports:
16+
- "8123:8123" # http://localhost:8123
17+
18+
docker-host:
19+
ports:
20+
- "2222:22" # SSH reachable at localhost:2222

docker-compose.yaml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
services:
2+
3+
# ── Home Assistant ──────────────────────────────────────────────────────────
4+
homeassistant:
5+
image: ghcr.io/home-assistant/home-assistant:stable
6+
container_name: homeassistant_test
7+
volumes:
8+
# Persistent HA config (survives container restarts; start fresh with
9+
# `docker compose down -v`).
10+
- ha_config:/config
11+
# Mount the ssh_docker integration source as a custom component so HA
12+
# loads it on startup without any extra copy step.
13+
- ./:/config/custom_components/ssh_docker:ro
14+
# Mount the ssh_command functional copy (required dependency of ssh_docker).
15+
- ./tests/playwright/ssh_command:/config/custom_components/ssh_command:ro
16+
# Startup wrapper that pre-populates /etc/hosts before launching HA.
17+
# Alpine Linux (musl libc) cannot resolve Docker container hostnames via
18+
# Python's socket module because of iptables/UDP limitations in this
19+
# environment. The wrapper uses busybox nslookup (which works) to add
20+
# entries to /etc/hosts so that all resolver calls succeed via the
21+
# "files" nsswitch path.
22+
- ./tests/playwright/ha-init-wrapper.sh:/ha-init-wrapper.sh:ro
23+
environment:
24+
- TZ=UTC
25+
entrypoint: ["/bin/sh", "/ha-init-wrapper.sh"]
26+
# Clear the external search domain that musl's resolver would try first,
27+
# which causes timeouts in this Azure-hosted environment.
28+
dns_search: "."
29+
restart: unless-stopped
30+
31+
# ── Docker host ─────────────────────────────────────────────────────────────
32+
# Ubuntu 24.04 container running sshd and a mock Docker CLI.
33+
# The SSH Docker integration connects here via SSH and issues docker commands.
34+
# No DinD / privileged mode required — the mock CLI simulates container state
35+
# using plain files.
36+
# Credentials: user=foo password=pass
37+
docker-host:
38+
build:
39+
context: tests/playwright
40+
dockerfile: Dockerfile.dockerhost
41+
container_name: docker_host
42+
43+
# ── Playwright E2E test runner ──────────────────────────────────────────────
44+
# Not started by default (`docker compose up`); invoke explicitly:
45+
# docker compose run --rm playwright-tests
46+
playwright-tests:
47+
build:
48+
context: .
49+
dockerfile: tests/playwright/Dockerfile
50+
environment:
51+
- HOMEASSISTANT_URL=http://homeassistant:8123
52+
- DOCKER_HOST_NAME=docker_host
53+
- SSH_USER=foo
54+
- SSH_PASSWORD=pass
55+
- HA_USERNAME=admin
56+
- HA_PASSWORD=admin
57+
volumes:
58+
# Test results (JUnit XML) written here are available on the host after
59+
# the container exits, e.g. for CI artifact upload.
60+
- ./playwright-results:/app/playwright-results
61+
depends_on:
62+
- homeassistant
63+
- docker-host
64+
65+
volumes:
66+
ha_config:

run_playwright_tests.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env bash
2+
# run_playwright_tests.sh
3+
#
4+
# Runs the Playwright E2E test suite in a fully isolated Docker environment.
5+
# No local Python environment or browser installation is required.
6+
#
7+
# The suite spins up Home Assistant, a mock Docker host, and the Playwright
8+
# test runner via docker compose, then tears everything down on exit.
9+
#
10+
# Usage:
11+
# ./run_playwright_tests.sh
12+
13+
set -euo pipefail
14+
15+
# ── Colour helpers ────────────────────────────────────────────────────────────
16+
RED='\033[0;31m'
17+
GREEN='\033[0;32m'
18+
YELLOW='\033[1;33m'
19+
BLUE='\033[0;34m'
20+
BOLD='\033[1m'
21+
NC='\033[0m'
22+
23+
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
24+
success() { echo -e "${GREEN}[PASS]${NC} $*"; }
25+
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
26+
error() { echo -e "${RED}[FAIL]${NC} $*"; }
27+
header() { echo -e "\n${BOLD}$*${NC}"; }
28+
29+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30+
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yaml"
31+
32+
# ── Resolve docker compose command ───────────────────────────────────────────
33+
get_compose_cmd() {
34+
if command -v docker &>/dev/null && sudo docker compose version &>/dev/null 2>&1; then
35+
echo "sudo docker compose"
36+
else
37+
error "docker compose is not available. Please install Docker with the Compose plugin."
38+
exit 1
39+
fi
40+
}
41+
42+
# ── Main ──────────────────────────────────────────────────────────────────────
43+
main() {
44+
if [[ $# -gt 0 ]]; then
45+
error "This script takes no arguments."
46+
echo "Usage: $0"
47+
exit 1
48+
fi
49+
50+
if [[ ! -f "$COMPOSE_FILE" ]]; then
51+
error "docker-compose.yaml not found at $COMPOSE_FILE"
52+
exit 1
53+
fi
54+
55+
header "════════════════════════════════════════════════════"
56+
header " Playwright E2E tests (docker compose)"
57+
header "════════════════════════════════════════════════════"
58+
59+
local compose_cmd
60+
compose_cmd="$(get_compose_cmd)"
61+
62+
info "Building Docker images…"
63+
$compose_cmd -f "$COMPOSE_FILE" build
64+
65+
info "Running test container (this may take several minutes on first run)…"
66+
local exit_code=0
67+
$compose_cmd -f "$COMPOSE_FILE" run --rm playwright-tests || exit_code=$?
68+
69+
info "Stopping services…"
70+
$compose_cmd -f "$COMPOSE_FILE" down -v || true
71+
72+
if [[ $exit_code -eq 0 ]]; then
73+
echo ""
74+
success "All Playwright E2E tests passed."
75+
exit 0
76+
else
77+
echo ""
78+
error "Playwright E2E tests failed (exit code ${exit_code})."
79+
exit "${exit_code}"
80+
fi
81+
}
82+
83+
main "$@"

0 commit comments

Comments
 (0)