From afd090df05f814de622f5ee2b24091356cc873a1 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 31 Oct 2023 14:32:50 +0000 Subject: [PATCH 1/6] chore(site): add playwright:remote script --- site/e2e/playwright.config.ts | 16 +++++++-- site/e2e/server/run_with_forward.sh | 52 +++++++++++++++++++++++++++++ site/e2e/server/server.mjs | 4 +++ site/package.json | 1 + 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100755 site/e2e/server/run_with_forward.sh create mode 100644 site/e2e/server/server.mjs diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index dcd82a6dfd999..2cf0b7a494e89 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -6,6 +6,8 @@ export const port = process.env.CODER_E2E_PORT ? Number(process.env.CODER_E2E_PORT) : defaultPort; +export const wsEndpoint = process.env.CODER_E2E_WS_ENDPOINT; + const coderMain = path.join(__dirname, "../../enterprise/cmd/coder"); export const STORAGE_STATE = path.join(__dirname, ".auth.json"); @@ -34,9 +36,17 @@ export default defineConfig({ use: { baseURL: `http://localhost:${port}`, video: "retain-on-failure", - launchOptions: { - args: ["--disable-webgl"], - }, + ...(wsEndpoint + ? { + connectOptions: { + wsEndpoint: wsEndpoint, + }, + } + : { + launchOptions: { + args: ["--disable-webgl"], + }, + }), }, webServer: { url: `http://localhost:${port}/api/v2/deployment/config`, diff --git a/site/e2e/server/run_with_forward.sh b/site/e2e/server/run_with_forward.sh new file mode 100755 index 0000000000000..33be216883a39 --- /dev/null +++ b/site/e2e/server/run_with_forward.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +workspace=${1:-} +port=${2:-3000} + +if [[ -z "${workspace}" ]]; then + echo "Usage: $0 [port]" + exit 1 +fi + +# Go to site. +cd "$(dirname "$0")/../.." + +echo "Running \"pnpm install\" to ensure local and remote are up-to-date..." +pnpm install + +echo "Running \"playwright install\" for browser binaries..." +pnpm exec playwright install + +playwright_out="$(mktemp -t playwright_server_out.XXXXXX)" + +rm "$playwright_out" +mkfifo "$playwright_out" +exec 3<>"$playwright_out" + +echo "Starting Playwright server..." +exec pnpm --silent exec node ./e2e/server/server.mjs 1>&3 & +playwright_pid=$! + +trap ' +kill $playwright_pid +exec 3>&- +rm "$playwright_out" +' EXIT + +echo "Waiting for Playwright to start..." +read -r ws_endpoint <&3 +if [[ ${ws_endpoint} != ws://* ]]; then + echo "Playwright failed to start." + echo "${ws_endpoint}" + cat "$playwright_out" + exit 1 +fi +echo "Playwright started at ${ws_endpoint}" + +ws_port=${ws_endpoint##*:} +ws_port=${ws_port%/*} + +echo "Starting SSH tunnel, run test via \"pnpm run playwright:test\"..." + +ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d site ]] && cd site; \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -l" diff --git a/site/e2e/server/server.mjs b/site/e2e/server/server.mjs new file mode 100644 index 0000000000000..952cdb92628fe --- /dev/null +++ b/site/e2e/server/server.mjs @@ -0,0 +1,4 @@ +import { chromium } from "@playwright/test"; + +const server = await chromium.launchServer({ headless: false }); +console.log(server.wsEndpoint()); diff --git a/site/package.json b/site/package.json index 1b9943b56e040..0060781fabce8 100644 --- a/site/package.json +++ b/site/package.json @@ -18,6 +18,7 @@ "lint:types": "tsc --noEmit", "playwright:install": "playwright install --with-deps chromium", "playwright:test": "playwright test --config=e2e/playwright.config.ts", + "playwright:remote": "./e2e/server/run_with_forward.sh", "gen:provisioner": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./e2e/ --ts_proto_opt=outputJsonMethods=false,outputEncodeMethods=encode-no-creation,outputClientImpl=false,nestJs=false,outputPartialMethods=false,fileSuffix=Generated,suffix=hey -I ../provisionersdk/proto ../provisionersdk/proto/provisioner.proto && pnpm exec prettier --ignore-path '/dev/null' --cache --write './e2e/provisionerGenerated.ts'", "storybook": "STORYBOOK=true storybook dev -p 6006", "storybook:build": "storybook build", From 9f7fe08f53816634efecc1fa44c9769d60a2c2db Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 31 Oct 2023 14:47:19 +0000 Subject: [PATCH 2/6] shfmt --- site/e2e/server/run_with_forward.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/e2e/server/run_with_forward.sh b/site/e2e/server/run_with_forward.sh index 33be216883a39..8b6223c8bf6a8 100755 --- a/site/e2e/server/run_with_forward.sh +++ b/site/e2e/server/run_with_forward.sh @@ -5,8 +5,8 @@ workspace=${1:-} port=${2:-3000} if [[ -z "${workspace}" ]]; then - echo "Usage: $0 [port]" - exit 1 + echo "Usage: $0 [port]" + exit 1 fi # Go to site. @@ -36,11 +36,11 @@ rm "$playwright_out" echo "Waiting for Playwright to start..." read -r ws_endpoint <&3 -if [[ ${ws_endpoint} != ws://* ]]; then - echo "Playwright failed to start." - echo "${ws_endpoint}" - cat "$playwright_out" - exit 1 +if [[ ${ws_endpoint} != ws://* ]]; then + echo "Playwright failed to start." + echo "${ws_endpoint}" + cat "$playwright_out" + exit 1 fi echo "Playwright started at ${ws_endpoint}" From d5a851e51a93e485a650926177cca5fad1ec021e Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 31 Oct 2023 14:50:02 +0000 Subject: [PATCH 3/6] exec --- site/e2e/server/run_with_forward.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/server/run_with_forward.sh b/site/e2e/server/run_with_forward.sh index 8b6223c8bf6a8..19119278aaf77 100755 --- a/site/e2e/server/run_with_forward.sh +++ b/site/e2e/server/run_with_forward.sh @@ -49,4 +49,4 @@ ws_port=${ws_port%/*} echo "Starting SSH tunnel, run test via \"pnpm run playwright:test\"..." -ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d site ]] && cd site; \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -l" +ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d site ]] && cd site; exec \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -l" From 209f34c1ce9260712fffa719de739f0bd862aee9 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 8 Nov 2023 19:44:10 +0200 Subject: [PATCH 4/6] rewrite script, now `scripts/remote_playwright.sh` --- docs/contributing/frontend.md | 17 ++++++ scripts/remote_playwright.sh | 81 +++++++++++++++++++++++++++++ site/e2e/server/run_with_forward.sh | 52 ------------------ site/e2e/server/server.mjs | 4 -- 4 files changed, 98 insertions(+), 56 deletions(-) create mode 100755 scripts/remote_playwright.sh delete mode 100755 site/e2e/server/run_with_forward.sh delete mode 100644 site/e2e/server/server.mjs diff --git a/docs/contributing/frontend.md b/docs/contributing/frontend.md index b1e4858ce5485..3a207dd5cd44d 100644 --- a/docs/contributing/frontend.md +++ b/docs/contributing/frontend.md @@ -217,6 +217,23 @@ to test if the page is being rendered correctly, you should consider using the > ℹ️ For scenarios where you need to be authenticated, you can use > `test.use({ storageState: getStatePath("authState") })`. +For ease of debugging, it's possible to run a Playwright test in headful mode +running a Playwright server on your local machine, and executing the test inside +your workspace. + +You can either run `scripts/remote_playwright.sh` from `coder/coder` on your +local machine, or execute the following command if you don't have the repo +available: + +```bash +bash <(curl -sSL https://raw.githubusercontent.com/coder/coder/main/scripts/remote_playwright.sh) [workspace] +``` + +The `scripts/remote_playwright.sh` script will start a Playwright server on your +local machine and forward the necessary ports to your workspace. At the end of +the script, you will land _inside_ your workspace with environment variables set +so you can simply execute the test (`pnpm run playwright:test`). + ### Integration Test user interactions like "Click in a button shows a dialog", "Submit the form diff --git a/scripts/remote_playwright.sh b/scripts/remote_playwright.sh new file mode 100755 index 0000000000000..39fdd3701ac56 --- /dev/null +++ b/scripts/remote_playwright.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +set -euo pipefail + +workspace=${1:-} +coder_repo=${2:-.} +port=${3:-3000} + +if [[ -z "${workspace}" ]]; then + echo "Usage: $0 [workspace coder/coder dir] [e2e port]" + exit 1 +fi + +main() { + # Check the Playwright version from the workspace so we have a 1-to-1 match + # between the current branch and what we're going to run locally. This is + # necessary because Playwright uses their own protocol for communicating + # between the server and client, and the protocol changes between versions. + echo "Checking Playwright version from \"${workspace}\"..." + playwright_version="$(ssh "coder.${workspace}" "cat '${coder_repo}'/site/pnpm-lock.yaml | grep '^ /@playwright/test@' | cut -d '@' -f 3 | tr -d ':'")" + + echo "Found Playwright version ${playwright_version}..." + + # Let's store it in cache because, why not, this is ephemeral. + dest=~/.cache/coder-remote-playwright + echo "Initializing Playwright server in ${dest}..." + mkdir -p "${dest}" + cd "${dest}" + echo '{"dependencies":{"@playwright/test":"'"${playwright_version}"'"}}' >package.json + cat <<-EOF >server.mjs + import { chromium } from "@playwright/test"; + + const server = await chromium.launchServer({ headless: false }); + console.log(server.wsEndpoint()); + EOF + + npm_cmd=npm + if command -v pnpm >/dev/null; then + npm_cmd=pnpm + fi + echo "Running \"${npm_cmd} install\" to ensure local and remote are up-to-date..." + "${npm_cmd}" install + + echo "Running \"${npm_cmd} exec playwright install\" for browser binaries..." + "${npm_cmd}" exec playwright install + + playwright_out="$(mktemp -t playwright_server_out.XXXXXX)" + + rm "${playwright_out}" + mkfifo "${playwright_out}" + exec 3<>"${playwright_out}" + + echo "Starting Playwright server..." + ${npm_cmd} exec node server.mjs 1>&3 & + playwright_pid=$! + + trap ' + kill -INT ${playwright_pid} + exec 3>&- + rm "${playwright_out}" + wait ${playwright_pid} + ' EXIT + + echo "Waiting for Playwright to start..." + read -r ws_endpoint <&3 + if [[ ${ws_endpoint} != ws://* ]]; then + echo "Playwright failed to start." + echo "${ws_endpoint}" + cat "${playwright_out}" + exit 1 + fi + echo "Playwright started at ${ws_endpoint}" + + ws_port=${ws_endpoint##*:} + ws_port=${ws_port%/*} + + echo + echo "Starting SSH tunnel, run test via \"pnpm run playwright:test\"..." + ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d '${coder_repo}/site' ]] && cd '${coder_repo}/site'; exec \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -i -l" +} + +main diff --git a/site/e2e/server/run_with_forward.sh b/site/e2e/server/run_with_forward.sh deleted file mode 100755 index 19119278aaf77..0000000000000 --- a/site/e2e/server/run_with_forward.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -workspace=${1:-} -port=${2:-3000} - -if [[ -z "${workspace}" ]]; then - echo "Usage: $0 [port]" - exit 1 -fi - -# Go to site. -cd "$(dirname "$0")/../.." - -echo "Running \"pnpm install\" to ensure local and remote are up-to-date..." -pnpm install - -echo "Running \"playwright install\" for browser binaries..." -pnpm exec playwright install - -playwright_out="$(mktemp -t playwright_server_out.XXXXXX)" - -rm "$playwright_out" -mkfifo "$playwright_out" -exec 3<>"$playwright_out" - -echo "Starting Playwright server..." -exec pnpm --silent exec node ./e2e/server/server.mjs 1>&3 & -playwright_pid=$! - -trap ' -kill $playwright_pid -exec 3>&- -rm "$playwright_out" -' EXIT - -echo "Waiting for Playwright to start..." -read -r ws_endpoint <&3 -if [[ ${ws_endpoint} != ws://* ]]; then - echo "Playwright failed to start." - echo "${ws_endpoint}" - cat "$playwright_out" - exit 1 -fi -echo "Playwright started at ${ws_endpoint}" - -ws_port=${ws_endpoint##*:} -ws_port=${ws_port%/*} - -echo "Starting SSH tunnel, run test via \"pnpm run playwright:test\"..." - -ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d site ]] && cd site; exec \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -l" diff --git a/site/e2e/server/server.mjs b/site/e2e/server/server.mjs deleted file mode 100644 index 952cdb92628fe..0000000000000 --- a/site/e2e/server/server.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { chromium } from "@playwright/test"; - -const server = await chromium.launchServer({ headless: false }); -console.log(server.wsEndpoint()); From 67c70b1c989901557ea26e7d6c4755847e8d9330 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 8 Nov 2023 19:45:36 +0200 Subject: [PATCH 5/6] remove remote --- site/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/site/package.json b/site/package.json index 0060781fabce8..1b9943b56e040 100644 --- a/site/package.json +++ b/site/package.json @@ -18,7 +18,6 @@ "lint:types": "tsc --noEmit", "playwright:install": "playwright install --with-deps chromium", "playwright:test": "playwright test --config=e2e/playwright.config.ts", - "playwright:remote": "./e2e/server/run_with_forward.sh", "gen:provisioner": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./e2e/ --ts_proto_opt=outputJsonMethods=false,outputEncodeMethods=encode-no-creation,outputClientImpl=false,nestJs=false,outputPartialMethods=false,fileSuffix=Generated,suffix=hey -I ../provisionersdk/proto ../provisionersdk/proto/provisioner.proto && pnpm exec prettier --ignore-path '/dev/null' --cache --write './e2e/provisionerGenerated.ts'", "storybook": "STORYBOOK=true storybook dev -p 6006", "storybook:build": "storybook build", From 4aff413b7677c1d1ae10259588138c24311a5a43 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 8 Nov 2023 20:09:27 +0200 Subject: [PATCH 6/6] shellcheck --- scripts/remote_playwright.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/remote_playwright.sh b/scripts/remote_playwright.sh index 39fdd3701ac56..57f3a4d49f27e 100755 --- a/scripts/remote_playwright.sh +++ b/scripts/remote_playwright.sh @@ -16,6 +16,7 @@ main() { # necessary because Playwright uses their own protocol for communicating # between the server and client, and the protocol changes between versions. echo "Checking Playwright version from \"${workspace}\"..." + # shellcheck disable=SC2029 # This is intended to expand client-side. playwright_version="$(ssh "coder.${workspace}" "cat '${coder_repo}'/site/pnpm-lock.yaml | grep '^ /@playwright/test@' | cut -d '@' -f 3 | tr -d ':'")" echo "Found Playwright version ${playwright_version}..." @@ -75,6 +76,7 @@ main() { echo echo "Starting SSH tunnel, run test via \"pnpm run playwright:test\"..." + # shellcheck disable=SC2029 # This is intended to expand client-side. ssh -t -R "${ws_port}:127.0.0.1:${ws_port}" -L "${port}:127.0.0.1:${port}" coder."${workspace}" "export CODER_E2E_PORT='${port}'; export CODER_E2E_WS_ENDPOINT='${ws_endpoint}'; [[ -d '${coder_repo}/site' ]] && cd '${coder_repo}/site'; exec \"\$(grep \"\${USER}\": /etc/passwd | cut -d: -f7)\" -i -l" }