From 0d038c1fcf1cecbb170b2715fcf1f8151c0944bb Mon Sep 17 00:00:00 2001 From: RaghavsBrowserStack Date: Mon, 19 Oct 2026 15:17:59 +0530 Subject: [PATCH] PPLT-6237: Read PERCY_SERVER_ADDRESS and certify Playwright 1.63 Resolve the CLI address as PERCY_CLI_API -> PERCY_SERVER_ADDRESS -> default, so `percy exec --port N` works without also exporting PERCY_CLI_API. Bump the dev/test pin to playwright==1.63.* (Python >= 3.10). The install range in setup.py is unchanged. Co-Authored-By: Claude Opus 5.5 --- percy/screenshot.py | 11 +++++++++-- requirements.txt | 4 +++- tests/test_screenshot.py | 40 ++++++++++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/percy/screenshot.py b/percy/screenshot.py index 2a1c162..666320c 100644 --- a/percy/screenshot.py +++ b/percy/screenshot.py @@ -19,8 +19,15 @@ def _get_bool_env(key): """Get boolean value from environment variable.""" return os.environ.get(key, "").lower() == "true" -# Maybe get the CLI API address from the environment -PERCY_CLI_API = os.environ.get("PERCY_CLI_API") or "http://localhost:5338" +def _get_cli_api_address(): + # `percy exec --port N` exports PERCY_SERVER_ADDRESS, not PERCY_CLI_API + return ( + os.environ.get("PERCY_CLI_API") + or os.environ.get("PERCY_SERVER_ADDRESS") + or "http://localhost:5338" + ) + +PERCY_CLI_API = _get_cli_api_address() PERCY_DEBUG = os.environ.get("PERCY_LOGLEVEL") == "debug" RESPONSIVE_CAPTURE_SLEEP_TIME = os.environ.get("RESPONSIVE_CAPTURE_SLEEP_TIME") PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT = _get_bool_env("PERCY_RESPONSIVE_CAPTURE_MIN_HEIGHT") diff --git a/requirements.txt b/requirements.txt index f4b70ec..fe83f90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ -playwright==1.40.* +# Playwright 1.63 requires Python >= 3.10; the 3.8/3.9 CI jobs keep the previous pin +playwright==1.63.*; python_version >= "3.10" +playwright==1.40.*; python_version < "3.10" requests==2.* diff --git a/tests/test_screenshot.py b/tests/test_screenshot.py index 959f6ee..fc0ead0 100644 --- a/tests/test_screenshot.py +++ b/tests/test_screenshot.py @@ -1,4 +1,5 @@ # pylint: disable=[abstract-class-instantiated, arguments-differ, protected-access, too-many-lines] +import os import json import unittest import platform @@ -35,6 +36,7 @@ _resolve_readiness_config, _wait_for_ready, _deep_merge, + _get_cli_api_address, ) import percy.screenshot as local @@ -93,14 +95,14 @@ def mock_healthcheck( health_body = json.dumps(health_body) httpretty.register_uri( httpretty.GET, - "http://localhost:5338/percy/healthcheck", + f"{local.PERCY_CLI_API}/percy/healthcheck", body=health_body, adding_headers=health_headers, status=health_status, ) httpretty.register_uri( httpretty.GET, - "http://localhost:5338/percy/dom.js", + f"{local.PERCY_CLI_API}/percy/dom.js", body=( "window.PercyDOM = { serialize: () => { return { html: " "document.documentElement.outerHTML } }, waitForResize: () => { " @@ -114,7 +116,7 @@ def mock_healthcheck( def mock_logger(): httpretty.register_uri( httpretty.POST, - "http://localhost:5338/percy/log", + f"{local.PERCY_CLI_API}/percy/log", body=json.dumps({"success": True}), status=200, ) @@ -123,7 +125,7 @@ def mock_logger(): def mock_snapshot(fail=False, data=False): httpretty.register_uri( httpretty.POST, - "http://localhost:5338/percy/snapshot", + f"{local.PERCY_CLI_API}/percy/snapshot", body=json.dumps( { "success": "false" if fail else "true", @@ -1109,7 +1111,7 @@ def test_percy_automate_screenshot(self, mock_is_percy_enabled, mock_post): # Assertions self.assertEqual(result, "screenshot_data") mock_post.assert_called_once_with( - "http://localhost:5338/percy/automateScreenshot", + f"{local.PERCY_CLI_API}/percy/automateScreenshot", json={ "client_info": f"percy-playwright-python/{SDK_VERSION}", "environment_info": [ @@ -1501,7 +1503,7 @@ def test_get_responsive_widths(self, mock_get): ], ) mock_get.assert_called_once_with( - "http://localhost:5338/percy/widths-config?widths=375,1280", + f"{local.PERCY_CLI_API}/percy/widths-config?widths=375,1280", timeout=30, ) @@ -1536,7 +1538,7 @@ def test_get_responsive_widths_with_none(self, mock_get): ], ) mock_get.assert_called_once_with( - "http://localhost:5338/percy/widths-config", + f"{local.PERCY_CLI_API}/percy/widths-config", timeout=30, ) @@ -1558,7 +1560,7 @@ def test_get_responsive_widths_with_empty_list(self, mock_get): ], ) mock_get.assert_called_once_with( - "http://localhost:5338/percy/widths-config", + f"{local.PERCY_CLI_API}/percy/widths-config", timeout=30, ) @@ -2081,5 +2083,27 @@ def test_expose_detach_error_suppressed(self): expose_closed_shadow_roots(page) +class TestCliApiAddress(unittest.TestCase): + def test_prefers_percy_cli_api(self): + with patch.dict("os.environ", { + "PERCY_CLI_API": "http://localhost:1111", + "PERCY_SERVER_ADDRESS": "http://localhost:2222", + }): + self.assertEqual(_get_cli_api_address(), "http://localhost:1111") + + def test_falls_back_to_percy_server_address(self): + with patch.dict("os.environ", {"PERCY_SERVER_ADDRESS": "http://localhost:2222"}): + os.environ.pop("PERCY_CLI_API", None) + self.assertEqual(_get_cli_api_address(), "http://localhost:2222") + + def test_ignores_empty_values(self): + with patch.dict("os.environ", {"PERCY_CLI_API": "", "PERCY_SERVER_ADDRESS": ""}): + self.assertEqual(_get_cli_api_address(), "http://localhost:5338") + + def test_defaults_to_5338(self): + with patch.dict("os.environ", {}, clear=True): + self.assertEqual(_get_cli_api_address(), "http://localhost:5338") + + if __name__ == "__main__": unittest.main()