#!/bin/sh
# script/frontend-viewport-test: verify the responsive layout of the built
# frontend in a real browser engine.
#
# Builds dist/, serves it with the same nginx image and the same nginx.conf
# the shipping container uses, points a containerised headless Chrome at it
# over CDP, and asserts on computed layout at every viewport width derived
# from the app's own CSS. See test/viewport/README.md for what this covers
# and what it cannot.
#
# Deliberately not part of script/check: it needs Docker and takes far
# longer than the 20s budget make test has to stay inside.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"

# chromedp/headless-shell 151.0.7922.109, 2026-08-09
BROWSER_IMAGE="chromedp/headless-shell@sha256:2d349b544a1ea6b5b5fd7c0fe99215ff662339c57407ee2e8c0a11af93516b04"
# nginx:stable-alpine, 2026-02-22 (the digest Dockerfile ships)
SERVER_IMAGE="nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6ab"
# node:22-alpine, 2026-02-22 (the digest Dockerfile builds with)
NODE_IMAGE="node@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e34"

RUN_ID="$$-$(date +%s)"
NETWORK="netwatch-viewport-$RUN_ID"
SERVER="netwatch-viewport-server-$RUN_ID"
BROWSER="netwatch-viewport-browser-$RUN_ID"
ARTIFACT_DIR="$ROOT/tmp/viewport"

cleanup() {
    docker rm -f "$BROWSER" > /dev/null 2>&1 || true
    docker rm -f "$SERVER" > /dev/null 2>&1 || true
    docker network rm "$NETWORK" > /dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM

main() {
    cd "$ROOT"

    # Test what ships: the production build, not a dev server.
    "$ROOT/script/test"
    if [ ! -f "$ROOT/dist/index.html" ]; then
        echo "frontend-viewport-test: dist/index.html missing after build" >&2
        exit 1
    fi

    mkdir -p "$ARTIFACT_DIR"

    # An --internal network has no route off the host, so the browser
    # cannot reach the real internet no matter what the page asks for.
    # Latency probes are answered by the harness instead. This also means
    # no port can be published from it, which is why the harness itself
    # runs as a third container on the same network rather than on the
    # host.
    docker network create --internal "$NETWORK" > /dev/null

    docker run -d --rm --name "$SERVER" \
        --network "$NETWORK" --network-alias netwatch \
        -v "$ROOT/dist:/usr/share/nginx/html:ro" \
        -v "$ROOT/nginx.conf:/etc/nginx/conf.d/default.conf:ro" \
        "$SERVER_IMAGE" > /dev/null

    # The image's own entrypoint already exposes CDP on 9222 and passes
    # --no-sandbox, so only extra flags belong here; re-specifying the
    # debugging port collides with it and leaves the endpoint bound to
    # loopback only. --hide-scrollbars keeps innerWidth equal to
    # clientWidth, so the overflow assertion has no scrollbar-sized slack
    # to hide behind, and matches the overlay scrollbars phones use.
    docker run -d --rm --name "$BROWSER" --init --shm-size=1g \
        --network "$NETWORK" \
        "$BROWSER_IMAGE" \
        --hide-scrollbars \
        > /dev/null

    # Chrome refuses DevTools requests whose Host header is neither
    # localhost nor an IP address, so dial the container by address rather
    # than by its network alias.
    browser_ip="$(docker inspect \
        -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
        "$BROWSER")"

    timeout 900 docker run --rm --init \
        --network "$NETWORK" \
        --user "$(id -u):$(id -g)" \
        -v "$ROOT:/app" \
        -w /app \
        -e NETWATCH_ROOT=/app \
        -e NETWATCH_BASE_URL=http://netwatch:8080 \
        -e "NETWATCH_CDP_URL=http://$browser_ip:9222" \
        -e NETWATCH_ARTIFACT_DIR=/app/tmp/viewport \
        "$NODE_IMAGE" \
        node test/viewport/harness.js
}

main "$@"
