From 433a5ef6ffd375ad41907fb34bf1a8a0fd6bc92d Mon Sep 17 00:00:00 2001 From: James Johnson Date: Tue, 14 Apr 2026 15:16:00 -0500 Subject: [PATCH 1/4] address PR 279 review feedback and improve dev stack ergonomics Fixes flagged in PR 279 Copilot review: - drop duplicate EXPOSE and redundant distro ruby packages from Dockerfile - clean up ruby-build install.sh invocation (PREFIX env, not bogus positional) - mysqladmin healthcheck uses --password= so empty DB_PASSWORD doesn't prompt - drop redundant gem install bundler / bundle install from CI (ruby/setup-ruby with bundler-cache handles it) - document E2E as a throwaway container (docker compose run --rm) so it doesn't collide with the main web service Dev stack improvements (more Sail-like): - APP_PORT / FORWARD_DB_PORT support for port forwarding (defaults preserved) - faster db healthcheck polling (interval 2s, start_period 30s) so the stack comes up in seconds instead of waiting 30s between probes - wait-on promoted to a real devDependency (no more npx download on every run) and bumped timeout to 60s to absorb rails boot - cypress_cache named volume so the cypress binary persists and is shared with throwaway run --rm containers - README documents combined dev + test DB setup, explains headless vs interactive E2E, and covers a host-side test server sidecar on port 3001 for running cypress interactively without flipping RAILS_ENV on the dev stack --- README.md | 55 ++++++++------- docker-compose.yml | 12 ++-- docker/Dockerfile | 11 ++- package-lock.json | 168 ++++++++++++++++++++++++++++++++++----------- package.json | 5 +- 5 files changed, 171 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 362c2920..fe19e585 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,17 @@ cp .env.example .env docker compose up ``` -In another terminal, set up the database: +In another terminal, install JS dependencies and set up the databases: ```sh +# install node deps +docker compose exec web npm install + +# create dev db, load schema, and seed docker compose exec web bin/rails db:setup -# OR to drop an existing db first -# docker compose exec web bin/rails db:reset +# create test db, load schema, skip seeding +docker compose exec -e RAILS_ENV=test web bin/rails db:create db:schema:load ``` Open . @@ -90,50 +94,45 @@ docker compose exec web bundle exec rspec Docker and devcontainer environments can only run Cypress **headlessly** (no GUI). For the interactive runner, you need to run Cypress from your host machine. -#### Headless (Docker or devcontainer) +#### Dev Container (Headless only) -Boots a test server and runs the full suite: +You can only run cypress within a docker container headlessly (no GUI): ```sh -docker compose exec web npm run test:e2e +# run headless +npm run test:e2e ``` -#### Interactive (from host) +#### From Host (headless or interactive) + +Runs Cypress from your host machine against a test Rails server in a throwaway container. The dev stack can keep running — the test server lives on a separate port so there's no conflict and no need to flip `RAILS_ENV`. **One-time host setup:** 1. Install [Node.js](https://nodejs.org/) (version 22). -2. Install dependencies locally: +2. Install JS deps on the host: ```sh npm install ``` -**Each run:** - -1. Put Rails in test mode. In your `.env`, change: - - ```sh - RAILS_ENV=test - ``` - - (`USER_LOOKUP_SKELETON=1` is already the default.) - -2. Start the stack: +**Each run:** open two terminals. - ```sh - docker compose up - ``` +Terminal 1 — start the test Rails server (container port 3000 → host 3001): -3. In another terminal on your host, open Cypress: +```sh +docker compose run --rm -p 3001:3000 web npm run test:e2e:server +``` - ```sh - npx cypress open - ``` +Terminal 2 — run Cypress, pointed at port 3001: - Cypress will connect to the Rails server at . +```sh +# headless +npx cypress run --config baseUrl=http://localhost:3001 -When you're done, set `RAILS_ENV=development` back in `.env` and restart the stack. +# interactive GUI +npx cypress open --config baseUrl=http://localhost:3001 +``` ## Deployment diff --git a/docker-compose.yml b/docker-compose.yml index 665ece9f..c9f5c839 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,13 +14,15 @@ services: - CMD - mysqladmin - ping - - "-p${DB_PASSWORD}" - retries: 3 + - "--password=${DB_PASSWORD}" + interval: 2s timeout: 5s + retries: 10 + start_period: 30s volumes: - db-data:/var/lib/mysql ports: - - "3306:3306" + - "${FORWARD_DB_PORT:-3306}:3306" web: build: @@ -33,6 +35,7 @@ services: volumes: - .:/app - bundle_cache:/app/vendor/bundle + - cypress_cache:/home/vscode/.cache/Cypress env_file: - .env environment: @@ -40,7 +43,7 @@ services: DB_HOST: db DB_PORT: 3306 ports: - - "3000:3000" + - "${APP_PORT:-3000}:3000" depends_on: db: condition: service_healthy @@ -48,3 +51,4 @@ services: volumes: db-data: bundle_cache: + cypress_cache: diff --git a/docker/Dockerfile b/docker/Dockerfile index a0997395..c8e171c8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,8 +9,6 @@ ARG USER_UID=1000 ARG USER_GID=1000 ARG SKIP_BUNDLE_INSTALL=false -EXPOSE 3000 - # Enable repositories and module streams RUN dnf install -y dnf-plugins-core && \ dnf config-manager --set-enabled crb && \ @@ -50,8 +48,6 @@ RUN dnf groupinstall -y "Development Tools" && \ net-tools \ nodejs \ npm \ - ruby \ - ruby-devel \ libffi-devel \ git-credential-libsecret \ libyaml \ @@ -73,9 +69,9 @@ RUN dnf install -y epel-release && \ dnf clean all && \ rm -rf /var/cache/dnf -# Install Ruby from source +# Install Ruby from source via ruby-build (installed into /usr/local) RUN git clone https://github.com/rbenv/ruby-build.git /tmp/ruby-build && \ - /tmp/ruby-build/install.sh chdir=/tmp/ruby-build && \ + PREFIX=/usr/local /tmp/ruby-build/install.sh && \ RUBY_CFLAGS="-std=gnu99" ruby-build ${RUBY_VERSION} /usr/local && \ rm -rf /tmp/ruby-build @@ -119,6 +115,9 @@ RUN mkdir -p $APP_HOME && \ # Switch to non-root user USER ${USERNAME} +# Pre-create Cypress cache dir so the named volume inherits vscode ownership +RUN mkdir -p /home/${USERNAME}/.cache/Cypress + # Copy Gemfile for bundle install COPY --chown=${USERNAME}:${USERNAME} Gemfile Gemfile.lock ./ diff --git a/package-lock.json b/package-lock.json index 5fdd1a57..c3600058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,8 @@ "vite": "^6.0.5", "vite-plugin-full-reload": "^1.2.0", "vite-plugin-ruby": "^5.1.1", - "vue-tsc": "^2.1.10" + "vue-tsc": "^2.1.10", + "wait-on": "^9.0.1" } }, "node_modules/@alloc/quick-lru": { @@ -238,7 +239,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -262,7 +262,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -347,7 +346,6 @@ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -725,7 +723,6 @@ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -1981,6 +1978,60 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz", + "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@headlessui/vue": { "version": "1.7.23", "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.23.tgz", @@ -2863,6 +2914,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tailwindcss/aspect-ratio": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", @@ -3020,7 +3078,6 @@ "integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/types": "8.33.0", @@ -3482,7 +3539,6 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3755,14 +3811,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -3880,7 +3936,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", @@ -4371,7 +4426,6 @@ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -4446,7 +4500,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@cypress/request": "^3.0.6", "@cypress/xvfb": "^1.2.4", @@ -4557,7 +4610,6 @@ "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.1.tgz", "integrity": "sha512-pRXZuk3oR7P5kTl/CRvopcTihlvmZiY+xSBjbNI6e8MjYtgZQodWEqLUUOrIZeLH8CfxapPR/Bmb0NKAvILP5g==", "license": "MIT", - "peer": true, "dependencies": { "jquery": ">=1.7" } @@ -4750,7 +4802,6 @@ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -4886,7 +4937,6 @@ "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4987,7 +5037,6 @@ "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "globals": "^13.24.0", @@ -5512,9 +5561,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -5572,9 +5621,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -6150,11 +6199,29 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } }, + "node_modules/joi": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz", + "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -6976,7 +7043,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7696,7 +7762,6 @@ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -8040,10 +8105,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/pump": { "version": "3.0.2", @@ -8318,7 +8386,6 @@ "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -8665,7 +8732,6 @@ "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -8859,7 +8925,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9012,7 +9077,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9151,7 +9215,6 @@ "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -9267,7 +9330,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9287,7 +9349,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz", "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/compiler-sfc": "3.5.16", @@ -9365,6 +9426,33 @@ "integrity": "sha512-pPO2Fr+1UgdhUK9dc5OhjLSeYi5LZAm/VZBXOVQgLHCO31bRjAof/+W/e/6SHCbnb1xdZb1gW5T9MX0k9ejSAQ==", "license": "MIT" }, + "node_modules/wait-on": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.5.tgz", + "integrity": "sha512-qgnbHDfDTRIp73ANEJNRW/7kn8CrDUcvZz18xotJQku/P4saTGkbIzvnMZebPmVvVNUiRq1qWAPyqCH+W4H8KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.15.0", + "joi": "^18.1.2", + "lodash": "^4.18.1", + "minimist": "^1.2.8", + "rxjs": "^7.8.2" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/wait-on/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 9a2148cd..68e0dcea 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "cypress:run": "npx cypress run --browser chromium", "cypress:headless": "npx cypress run --browser chromium --config baseUrl=http://localhost:3000", "test:e2e:server": "RAILS_ENV=test USER_LOOKUP_SKELETON=1 bin/rails server", - "test:e2e:run": "npx wait-on --timeout 10000 http://localhost:3000 && npx cypress run --browser chromium --config baseUrl=http://localhost:3000", + "test:e2e:run": "npx wait-on --timeout 60000 http://localhost:3000 && npx cypress run --browser chromium --config baseUrl=http://localhost:3000", "test:e2e": "concurrently --kill-others --success first \"npm run test:e2e:server\" \"npm run test:e2e:run\"" }, "dependencies": { @@ -62,6 +62,7 @@ "vite": "^6.0.5", "vite-plugin-full-reload": "^1.2.0", "vite-plugin-ruby": "^5.1.1", - "vue-tsc": "^2.1.10" + "vue-tsc": "^2.1.10", + "wait-on": "^9.0.1" } } From 607d92b2c44c7f58c4a66403db51ef36a7160896 Mon Sep 17 00:00:00 2001 From: James Johnson Date: Tue, 14 Apr 2026 18:06:25 -0500 Subject: [PATCH 2/4] =?UTF-8?q?chore(deps):=20upgrade=20cypress=2013=20?= =?UTF-8?q?=E2=86=92=2015=20and=20companion=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cypress: ^13.17.0 → ^15.13.1 - cypress-real-events: ^1.13.0 → ^1.15.0 - eslint-plugin-cypress: ^4.1.0 → ^6.3.0 Primary motivation: Cypress 13's bundled Electron ~27 hangs on the spec-list screen when opening the UI on recent macOS. Cypress 15 ships Electron 37, which resolves the hang. Also picks up two majors of upstream fixes and security updates. Node 22 already satisfies Cypress 15's Node 20+ requirement. No code changes were required: no use of removed/renamed APIs (cy.exec().code, SelectorPlayground, Webpack preprocessor, etc.), and the only cy.origin spec (happyPath.cy.ts) crosses registrable domains so Cypress 14's injectDocumentDomain default flip is a no-op for us. --- package-lock.json | 220 +++++++++++++++++++++++++++------------------- package.json | 6 +- 2 files changed, 132 insertions(+), 94 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3600058..360ad0e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,13 +34,13 @@ "@vue/eslint-config-typescript": "^14.1.4", "autoprefixer": "^10.4.20", "concurrently": "^9.1.0", - "cypress": "^13.17.0", + "cypress": "^15.13.1", "cypress-browser-permissions": "^1.1.0", "cypress-on-rails": "^0.1.0", - "cypress-real-events": "^1.13.0", + "cypress-real-events": "^1.15.0", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-cypress": "^4.1.0", + "eslint-plugin-cypress": "^6.3.0", "eslint-plugin-vue": "^9.32.0", "postcss": "^8.4.49", "postcss-flexbugs-fixes": "^5.0.2", @@ -116,17 +116,6 @@ "node": ">=6.9.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@csstools/cascade-layer-name-parser": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", @@ -1304,9 +1293,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz", - "integrity": "sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.10.tgz", + "integrity": "sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1316,14 +1305,14 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~4.0.0", + "form-data": "~4.0.4", "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.14.0", + "qs": "~6.14.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", @@ -3025,6 +3014,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", @@ -3732,13 +3728,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4102,16 +4091,6 @@ "node": ">=8" } }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4168,9 +4147,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", "dev": true, "license": "MIT", "dependencies": { @@ -4180,7 +4159,7 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "@colors/colors": "1.5.0" + "colors": "1.4.0" } }, "node_modules/cli-truncate": { @@ -4242,6 +4221,17 @@ "dev": true, "license": "MIT" }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4494,27 +4484,27 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.17.0.tgz", - "integrity": "sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==", + "version": "15.13.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.13.1.tgz", + "integrity": "sha512-jLkgo75zlwo7PhXp0XJot+zIfFSDzN1SvTml6Xf3ETM1XHRWnH3Q4LAR3orCo/BsnxPnhjG3m5HYSvn9DAtwBg==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.6", + "@cypress/request": "^3.0.10", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", + "@types/tmp": "^0.2.3", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "ci-info": "^4.0.0", + "ci-info": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", + "cli-table3": "0.6.1", "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", @@ -4526,11 +4516,10 @@ "extract-zip": "2.0.1", "figures": "^3.2.0", "fs-extra": "^9.1.0", - "getos": "^3.2.1", + "hasha": "5.2.2", "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", "listr2": "^3.8.3", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "log-symbols": "^4.0.0", "minimist": "^1.2.8", "ospath": "^1.2.2", @@ -4538,10 +4527,11 @@ "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", - "semver": "^7.5.3", "supports-color": "^8.1.1", - "tmp": "~0.2.3", + "systeminformation": "^5.31.1", + "tmp": "~0.2.4", "tree-kill": "1.2.2", + "tslib": "1.14.1", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -4549,7 +4539,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + "node": "^20.1.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/cypress-browser-permissions": { @@ -4576,15 +4566,22 @@ } }, "node_modules/cypress-real-events": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.14.0.tgz", - "integrity": "sha512-XmI8y3OZLh6cjRroPalzzS++iv+pGCaD9G9kfIbtspgv7GVsDt30dkZvSXfgZb4rAN+3pOkMVB7e0j4oXydW7Q==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.15.0.tgz", + "integrity": "sha512-in98xxTnnM9Z7lZBvvVozm99PBT2eEOjXRG5LKWyYvQnj9mGWXMiPNpfw7e7WiraBFh7XlXIxnE9Cu5o+52kQQ==", "dev": true, "license": "MIT", "peerDependencies": { - "cypress": "^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x || ^14.x" + "cypress": "^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x || ^14.x || ^15.x" } }, + "node_modules/cypress/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/cypress/node_modules/proxy-from-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", @@ -4592,6 +4589,13 @@ "dev": true, "license": "MIT" }, + "node_modules/cypress/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -5006,22 +5010,22 @@ } }, "node_modules/eslint-plugin-cypress": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-4.3.0.tgz", - "integrity": "sha512-CgS/S940MJlT8jtnWGKI0LvZQBGb/BB0QCpgBOxFMM/Z6znD+PZUwBhCTwHKN2GEr5AOny3xB92an0QfzBGooQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-6.3.1.tgz", + "integrity": "sha512-iTJtdIZbyCUlagEI4YlVcwgPFV7X379Qi/upujaD4kvOaQkMvzmpt90vfSnaqgqprp/HPIvhnzv3fdI7mYV4QQ==", "dev": true, "license": "MIT", "dependencies": { - "globals": "^15.15.0" + "globals": "^17.5.0" }, "peerDependencies": { "eslint": ">=9" } }, "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", "dev": true, "license": "MIT", "engines": { @@ -5753,16 +5757,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "async": "^3.2.0" - } - }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -5899,6 +5893,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -6322,16 +6343,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "> 0.8" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8135,9 +8146,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -8471,14 +8482,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -8726,6 +8737,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/systeminformation": { + "version": "5.31.5", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.5.tgz", + "integrity": "sha512-5SyLdip4/3alxD4Kh+63bUQTJmu7YMfYQTC+koZy7X73HgNqZSD2P4wOZQWtUncvPvcEmnfIjCoygN4MRoEejQ==", + "dev": true, + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/tailwindcss": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", diff --git a/package.json b/package.json index 68e0dcea..d8f15197 100644 --- a/package.json +++ b/package.json @@ -43,13 +43,13 @@ "@vue/eslint-config-typescript": "^14.1.4", "autoprefixer": "^10.4.20", "concurrently": "^9.1.0", - "cypress": "^13.17.0", + "cypress": "^15.13.1", "cypress-browser-permissions": "^1.1.0", "cypress-on-rails": "^0.1.0", - "cypress-real-events": "^1.13.0", + "cypress-real-events": "^1.15.0", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-cypress": "^4.1.0", + "eslint-plugin-cypress": "^6.3.0", "eslint-plugin-vue": "^9.32.0", "postcss": "^8.4.49", "postcss-flexbugs-fixes": "^5.0.2", From 45f90d8f38412c5f65149b4a38ab3e711a47360b Mon Sep 17 00:00:00 2001 From: James Johnson Date: Tue, 14 Apr 2026 18:48:50 -0500 Subject: [PATCH 3/4] chore(docker): parameterize cypress_cache mount via APP_USER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cypress_cache volume previously hardcoded /home/vscode/.cache/Cypress. The Dockerfile already parameterizes the container user via ARG USERNAME (default vscode), but compose couldn't follow if anyone ever built with --build-arg USERNAME=other. Using ${USERNAME:-vscode} directly would collide with the host shell's $USERNAME (typically set to the host user on Linux), so we use the APP_USER namespace instead — matches the existing APP_PORT / FORWARD_DB_PORT pattern in this compose file. Default behavior unchanged: /home/vscode/.cache/Cypress. --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c9f5c839..d47eafef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,7 @@ services: volumes: - .:/app - bundle_cache:/app/vendor/bundle - - cypress_cache:/home/vscode/.cache/Cypress + - cypress_cache:/home/${APP_USER:-vscode}/.cache/Cypress env_file: - .env environment: From f7f0f2a05f50ad833d4c38bfd03fc7caf6676ed9 Mon Sep 17 00:00:00 2001 From: James Johnson Date: Tue, 14 Apr 2026 19:49:50 -0500 Subject: [PATCH 4/4] chore(deps): address PR 281 copilot review feedback - bind test:e2e:server to 0.0.0.0:3000 so host-run Cypress can reach the containerized Rails server through published port mapping - clarify README: headless container section now covers both devcontainer shell and docker compose exec workflows --- README.md | 9 ++++++--- package.json | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe19e585..12da5ae9 100644 --- a/README.md +++ b/README.md @@ -94,13 +94,16 @@ docker compose exec web bundle exec rspec Docker and devcontainer environments can only run Cypress **headlessly** (no GUI). For the interactive runner, you need to run Cypress from your host machine. -#### Dev Container (Headless only) +#### Inside a container (headless only) -You can only run cypress within a docker container headlessly (no GUI): +Cypress can only run headlessly inside a container — no GUI. ```sh -# run headless +# from a devcontainer shell npm run test:e2e + +# from the host, against the running dev stack +docker compose exec web npm run test:e2e ``` #### From Host (headless or interactive) diff --git a/package.json b/package.json index d8f15197..aac964df 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "cypress:rails": "bin/foreman start -f Procfile.test", "cypress:run": "npx cypress run --browser chromium", "cypress:headless": "npx cypress run --browser chromium --config baseUrl=http://localhost:3000", - "test:e2e:server": "RAILS_ENV=test USER_LOOKUP_SKELETON=1 bin/rails server", + "test:e2e:server": "RAILS_ENV=test USER_LOOKUP_SKELETON=1 bin/rails server -b 0.0.0.0 -p 3000", "test:e2e:run": "npx wait-on --timeout 60000 http://localhost:3000 && npx cypress run --browser chromium --config baseUrl=http://localhost:3000", "test:e2e": "concurrently --kill-others --success first \"npm run test:e2e:server\" \"npm run test:e2e:run\"" },