From a9c3e67d8d498843cc47d3c4fec7e5a2b30b2416 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Sun, 28 Dec 2025 20:53:10 +0100 Subject: [PATCH 1/4] feat: make Harness WS port configurable --- .nx/version-plans/version-plan-1766951346332.md | 5 +++++ packages/config/src/types.ts | 1 + packages/jest/src/harness.ts | 2 +- packages/metro/src/manifest.ts | 5 ++++- packages/platform-android/src/adb.ts | 5 +++-- packages/platform-android/src/runner.ts | 1 - packages/runtime/src/client/getWSServer.ts | 3 ++- packages/runtime/src/globals.ts | 1 + 8 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .nx/version-plans/version-plan-1766951346332.md diff --git a/.nx/version-plans/version-plan-1766951346332.md b/.nx/version-plans/version-plan-1766951346332.md new file mode 100644 index 0000000..f5e1957 --- /dev/null +++ b/.nx/version-plans/version-plan-1766951346332.md @@ -0,0 +1,5 @@ +--- +__default__: prerelease +--- + +Added `webSocketPort` option to `rn-harness.config` (default 3001). This allows configuring the Bridge Server port, enabling usage of custom ports without rebuilding the application. \ No newline at end of file diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 26547eb..272a4b4 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -8,6 +8,7 @@ export const ConfigSchema = z .min(1, 'App registry component name is required'), runners: z.array(z.any()).min(1, 'At least one runner is required'), defaultRunner: z.string().optional(), + webSocketPort: z.number().optional().default(3001), bridgeTimeout: z .number() .min(1000, 'Bridge timeout must be at least 1 second') diff --git a/packages/jest/src/harness.ts b/packages/jest/src/harness.ts index 235751e..0f1ae46 100644 --- a/packages/jest/src/harness.ts +++ b/packages/jest/src/harness.ts @@ -24,7 +24,7 @@ const getHarnessInternal = async ( getMetroInstance({ projectRoot, harnessConfig: config }, signal), import(platform.runner).then((module) => module.default(platform.config)), getBridgeServer({ - port: 3001, + port: config.webSocketPort, timeout: config.bridgeTimeout, }), ]); diff --git a/packages/metro/src/manifest.ts b/packages/metro/src/manifest.ts index 42c87ba..5c32141 100644 --- a/packages/metro/src/manifest.ts +++ b/packages/metro/src/manifest.ts @@ -3,7 +3,10 @@ import fs from 'node:fs'; import { Config as HarnessConfig } from '@react-native-harness/config'; const getManifestContent = (harnessConfig: HarnessConfig): string => { - return `global.RN_HARNESS = { appRegistryComponentName: '${harnessConfig.appRegistryComponentName}' };`; + return `global.RN_HARNESS = { + appRegistryComponentName: '${harnessConfig.appRegistryComponentName}', + webSocketPort: ${harnessConfig.webSocketPort} + };`; }; export const getHarnessManifest = (harnessConfig: HarnessConfig): string => { diff --git a/packages/platform-android/src/adb.ts b/packages/platform-android/src/adb.ts index 1a04bcc..4b9e6f1 100644 --- a/packages/platform-android/src/adb.ts +++ b/packages/platform-android/src/adb.ts @@ -18,9 +18,10 @@ export const isAppInstalled = async ( export const reversePort = async ( adbId: string, - port: number + port: number, + hostPort: number = port ): Promise => { - await spawn('adb', ['-s', adbId, 'reverse', `tcp:${port}`, `tcp:${port}`]); + await spawn('adb', ['-s', adbId, 'reverse', `tcp:${port}`, `tcp:${hostPort}`]); }; export const stopApp = async ( diff --git a/packages/platform-android/src/runner.ts b/packages/platform-android/src/runner.ts index 4b4775e..edd9a21 100644 --- a/packages/platform-android/src/runner.ts +++ b/packages/platform-android/src/runner.ts @@ -33,7 +33,6 @@ const getAndroidRunner = async ( await Promise.all([ adb.reversePort(adbId, 8081), adb.reversePort(adbId, 8080), - adb.reversePort(adbId, 3001), ]); return { diff --git a/packages/runtime/src/client/getWSServer.ts b/packages/runtime/src/client/getWSServer.ts index d81baf9..8cf0a2f 100644 --- a/packages/runtime/src/client/getWSServer.ts +++ b/packages/runtime/src/client/getWSServer.ts @@ -4,6 +4,7 @@ import { WS_SERVER_PORT } from '../constants.js'; export const getWSServer = (): string => { const devServerUrl = getDevServerUrl(); const hostname = devServerUrl.split('://')[1].split(':')[0]; + const port = global.RN_HARNESS?.webSocketPort || WS_SERVER_PORT; - return `ws://${hostname}:${WS_SERVER_PORT}`; + return `ws://${hostname}:${port}`; }; diff --git a/packages/runtime/src/globals.ts b/packages/runtime/src/globals.ts index cad4a8a..71d11a8 100644 --- a/packages/runtime/src/globals.ts +++ b/packages/runtime/src/globals.ts @@ -1,5 +1,6 @@ export type HarnessGlobal = { appRegistryComponentName: string; + webSocketPort?: number; }; declare global { From de50096f4895eb6f2a25f9c772ff71758e2bf980 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Sun, 28 Dec 2025 20:54:28 +0100 Subject: [PATCH 2/4] chore: use custom port in the playground app --- apps/playground/rn-harness.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/playground/rn-harness.config.mjs b/apps/playground/rn-harness.config.mjs index 335a2a2..0c49b64 100644 --- a/apps/playground/rn-harness.config.mjs +++ b/apps/playground/rn-harness.config.mjs @@ -51,6 +51,7 @@ const config = { ], defaultRunner: 'android', bridgeTimeout: 120000, + webSocketPort: 3002, resetEnvironmentBetweenTestFiles: true, unstable__skipAlreadyIncludedModules: false, From 44c45f159f472ca90bdf72e5719547203aec8778 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Sun, 28 Dec 2025 21:46:14 +0100 Subject: [PATCH 3/4] fix: reverse port --- packages/jest/src/harness.ts | 4 +++- packages/platform-android/package.json | 1 + packages/platform-android/src/runner.ts | 5 ++++- pnpm-lock.yaml | 6 ++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/jest/src/harness.ts b/packages/jest/src/harness.ts index 0f1ae46..db2a6f4 100644 --- a/packages/jest/src/harness.ts +++ b/packages/jest/src/harness.ts @@ -22,7 +22,9 @@ const getHarnessInternal = async ( ): Promise => { const [metroInstance, platformInstance, serverBridge] = await Promise.all([ getMetroInstance({ projectRoot, harnessConfig: config }, signal), - import(platform.runner).then((module) => module.default(platform.config)), + import(platform.runner).then((module) => + module.default(platform.config, config) + ), getBridgeServer({ port: config.webSocketPort, timeout: config.bridgeTimeout, diff --git a/packages/platform-android/package.json b/packages/platform-android/package.json index c573249..3dac4f3 100644 --- a/packages/platform-android/package.json +++ b/packages/platform-android/package.json @@ -18,6 +18,7 @@ "dependencies": { "@react-native-harness/platforms": "workspace:*", "@react-native-harness/tools": "workspace:*", + "@react-native-harness/config": "workspace:*", "zod": "^3.25.67", "tslib": "^2.3.0" }, diff --git a/packages/platform-android/src/runner.ts b/packages/platform-android/src/runner.ts index edd9a21..419c109 100644 --- a/packages/platform-android/src/runner.ts +++ b/packages/platform-android/src/runner.ts @@ -3,6 +3,7 @@ import { AppNotInstalledError, HarnessPlatformRunner, } from '@react-native-harness/platforms'; +import { Config } from '@react-native-harness/config'; import { AndroidPlatformConfigSchema, type AndroidPlatformConfig, @@ -12,7 +13,8 @@ import * as adb from './adb.js'; import { getDeviceName } from './utils.js'; const getAndroidRunner = async ( - config: AndroidPlatformConfig + config: AndroidPlatformConfig, + harnessConfig: Config ): Promise => { const parsedConfig = AndroidPlatformConfigSchema.parse(config); const adbId = await getAdbId(parsedConfig.device); @@ -33,6 +35,7 @@ const getAndroidRunner = async ( await Promise.all([ adb.reversePort(adbId, 8081), adb.reversePort(adbId, 8080), + adb.reversePort(adbId, harnessConfig.webSocketPort), ]); return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4dc3a35..95fb12b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -352,6 +352,9 @@ importers: packages/platform-android: dependencies: + '@react-native-harness/config': + specifier: workspace:* + version: link:../config '@react-native-harness/platforms': specifier: workspace:* version: link:../platforms @@ -367,6 +370,9 @@ importers: packages/platform-ios: dependencies: + '@react-native-harness/config': + specifier: workspace:* + version: link:../config '@react-native-harness/platforms': specifier: workspace:* version: link:../platforms From 6e8bcfce59fef9fc9beae10a5fa19e4fd8438caf Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Sun, 28 Dec 2025 21:48:21 +0100 Subject: [PATCH 4/4] fix: lockfile --- pnpm-lock.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95fb12b..7c031f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -370,9 +370,6 @@ importers: packages/platform-ios: dependencies: - '@react-native-harness/config': - specifier: workspace:* - version: link:../config '@react-native-harness/platforms': specifier: workspace:* version: link:../platforms