Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Playwright host configurable #4010

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Browser/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ def __init__( # noqa: PLR0915
external_browser_executable: Optional[dict[SupportedBrowsers, str]] = None,
jsextension: Union[list[str], str, None] = None,
language: Optional[str] = None,
playwright_process_host: Optional[str] = None,
playwright_process_port: Optional[int] = None,
plugins: Union[list[str], str, None] = None,
retry_assertions_for: timedelta = timedelta(seconds=1),
Expand All @@ -824,6 +825,7 @@ def __init__( # noqa: PLR0915
| ``external_browser_executable`` | Dict mapping name of browser to path of executable of a browser. Will make opening new browsers of the given type use the set executablePath. Currently only configuring of `chromium` to a separate executable (chrome, chromium and Edge executables all work with recent versions) works. |
| ``jsextension`` | Path to Javascript modules exposed as extra keywords. The modules must be in CommonJS. It can either be a single path, a comma-separated lists of path or a real list of strings |
| ``language`` | Defines language which is used to translate keyword names and documentation. |
| ``playwright_process_host`` | Hostname / Host address which should be used when spawning the Playwright process. Defaults to 127.0.0.1. |
| ``playwright_process_port`` | Experimental reusing of playwright process. ``playwright_process_port`` is preferred over environment variable ``ROBOT_FRAMEWORK_BROWSER_NODE_PORT``. See `Experimental: Re-using same node process` for more details. |
| ``plugins`` | Allows extending the Browser library with external Python classes. Can either be a single class/module, a comma-separated list or a real list of strings |
| ``retry_assertions_for`` | Timeout for retrying assertions on keywords before failing the keywords. This timeout starts counting from the first failure. Global ``timeout`` will still be in effect. This allows stopping execution faster to assertion failure when element is found fast. |
Expand Down Expand Up @@ -865,6 +867,7 @@ def __init__( # noqa: PLR0915
WebAppState(self),
]
self.enable_playwright_debug = enable_playwright_debug
self.playwright_process_host = playwright_process_host
self.playwright_process_port = playwright_process_port
if self.enable_playwright_debug is True:
self.enable_playwright_debug = PlaywrightLogTypes.playwright
Expand Down Expand Up @@ -935,6 +938,7 @@ def playwright(self) -> Playwright:
self._playwright = Playwright(
self,
self.enable_playwright_debug,
self.playwright_process_host,
self.playwright_process_port,
self._playwright_log,
)
Expand Down
17 changes: 11 additions & 6 deletions Browser/playwright.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ def __init__(
self,
library: "Browser",
enable_playwright_debug: PlaywrightLogTypes,
host: Optional[str] = None,
port: Optional[int] = None,
playwright_log: Union[Path, None] = Path(Path.cwd()),
):
LibraryComponent.__init__(self, library)
self.enable_playwright_debug = enable_playwright_debug
self.ensure_node_dependencies()
self.host = str(host) if host else None
self.port = str(port) if port else None
self.playwright_log = playwright_log

Expand Down Expand Up @@ -107,10 +109,12 @@ def start_playwright(self) -> Optional[Popen]:
logfile = self.playwright_log.open("w")
else:
logfile = Path(os.devnull).open("w") # noqa: SIM115
host = str(self.host) if self.host is not None else "127.0.0.1"
port = str(find_free_port())
if self.enable_playwright_debug == PlaywrightLogTypes.playwright:
os.environ["DEBUG"] = "pw:api"
logger.info(f"Starting Browser process {playwright_script} using port {port}")
logger.info(f"Starting Browser process {playwright_script} using at {host}:{port}")
self.host = host
self.port = port
node_args = ["node"]
node_debug_options = os.environ.get(
Expand All @@ -119,6 +123,7 @@ def start_playwright(self) -> Optional[Popen]:
if node_debug_options:
node_args.extend(node_debug_options.split(","))
node_args.append(str(playwright_script))
node_args.append(host)
node_args.append(port)
if not os.environ.get("PLAYWRIGHT_BROWSERS_PATH"):
os.environ["PLAYWRIGHT_BROWSERS_PATH"] = "0"
Expand All @@ -133,25 +138,25 @@ def start_playwright(self) -> Optional[Popen]:
)

def wait_until_server_up(self):
for _ in range(150): # About 15 seconds
with grpc.insecure_channel(f"127.0.0.1:{self.port}") as channel:
for _ in range(150):
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
try:
stub = playwright_pb2_grpc.PlaywrightStub(channel)
response = stub.Health(Request().Empty())
logger.debug(
f"Connected to the playwright process at port {self.port}: {response}"
f"Connected to the playwright process at {self.host}:{self.port}: {response}"
)
return
except grpc.RpcError as err:
logger.debug(err)
time.sleep(0.1)
raise RuntimeError(
f"Could not connect to the playwright process at port {self.port}."
f"Could not connect to the playwright process at {self.host}:{self.port}."
)

@cached_property
def _channel(self):
return grpc.insecure_channel(f"127.0.0.1:{self.port}")
return grpc.insecure_channel(f"{self.host}:{self.port}")

@contextlib.contextmanager
def grpc_channel(self, original_error=False):
Expand Down
18 changes: 14 additions & 4 deletions node/playwright-wrapper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,26 @@ import { PlaywrightService } from './generated/playwright_grpc_pb';
import { pino } from 'pino';
const logger = pino({ timestamp: pino.stdTimeFunctions.isoTime });

const port = process.argv.slice(2);
if (Object.keys(port).length == 0) {
const args = process.argv.slice(2);

const host = args[0];
const port = args[1];

if (!host) {
throw new Error(`No host defined`);
}

if (!port) {
throw new Error(`No port defined`);
}

const server = new Server();
server.addService(
PlaywrightService as unknown as ServiceDefinition<UntypedServiceImplementation>,
new PlaywrightServer() as unknown as UntypedServiceImplementation,
);
server.bindAsync(`127.0.0.1:${port}`, ServerCredentials.createInsecure(), () => {
logger.info(`Listening on ${port}`);

server.bindAsync(`${host}:${port}`, ServerCredentials.createInsecure(), () => {
logger.info(`Listening on ${host}:${port}`);
server.start();
});
Loading