#!/bin/sh # script/test-e2e: build the extension and drive the real popup in a real # Chromium inside a pinned container. Our own extension to # scripts-to-rule-them-all. # # Deliberately NOT called by script/check or script/test: REPO_POLICIES.md # caps make test at 20 seconds and a browser suite does not fit. Run it # yourself before touching popup views; it is the only check that can see # a used-but-not-imported identifier blow up at runtime. The e2e workflow # in .gitea/workflows/e2e.yml runs it on every push, as a job separate # from check so that cap and the local fast path both stay intact. # # Docker is the only prerequisite. The repo reaches the container as a # build context and the extension is built inside it, so nothing here # depends on the node, yarn or make on the machine that starts the run. # That is not a convenience: it is what makes the CI job possible. The # Gitea runner executes the job in a container against the host's docker # socket, so a `docker run -v "$PWD:/work"` source path is resolved by the # host daemon and mounts an empty directory (measured on this repo's # runner), and the runner image ships node 18, which cannot install this # repo's dependencies at all. set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" IMAGE="$("$SCRIPT_DIR/projectname")-e2e-chrome" IIDFILE="" cleanup() { if [ -n "$IIDFILE" ]; then rm -f "$IIDFILE" fi } main() { cd "$ROOT" if ! command -v docker >/dev/null 2>&1; then echo "test-e2e: docker is required to run the e2e suite" >&2 exit 1 fi IIDFILE="$(mktemp)" trap cleanup EXIT trap 'cleanup; exit 130' INT TERM echo "Building the Chrome e2e image (extension included)..." docker build --iidfile "$IIDFILE" -t "$IMAGE" -f tests/e2e/Dockerfile . echo "Running e2e suite in the pinned Playwright container..." # The image is run by ID, not by tag: on a machine where two clones of # this repo run the suite at once, the tag can be moved by the other # build between this build and this run, and the suite would then # silently test the other checkout. # # --ipc=host: Chromium's shared-memory needs more than the default # 64MB /dev/shm or renderers crash. # HOME=/tmp: root's home is not writable in every base image and the # browser profile goes under it. # PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1: without it, # ctx.route() intercepts page requests only, and every fetch made by # the MV3 background service worker — including the phishing # blocklist fetch that src/background/index.js issues at worker # startup — goes to the real internet. The flag is experimental and # Playwright may drop or rename it. It cannot break silently: the # harness probes service-worker interception at launch and aborts # the whole suite if it is not in effect (see the interception # canary in tests/e2e/harness.js). If a future Playwright removes # the flag, that probe is what will fail, and the fix is either a # replacement mechanism or an honest downgrade of the isolation # claim in tests/e2e/network.js and README.md — not deleting the # probe. The image is pinned by digest, so this can only ever bite # on a deliberate bump. docker run --rm \ --ipc=host \ -e HOME=/tmp \ -e PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 \ -e "E2E_TRACE_NETWORK=${E2E_TRACE_NETWORK:-0}" \ "$(cat "$IIDFILE")" \ node tests/e2e/run.js } main "$@"