From cf51243e7d9b891a43e6ad0af683b8deb048f319 Mon Sep 17 00:00:00 2001 From: Oleksii Sanin Date: Thu, 25 Dec 2025 14:16:55 +0200 Subject: [PATCH 1/2] fix(selenium): respect user-provided network when recording is enabled Previously, SeleniumRecordingContainer always created a new internal network, ignoring any network provided via withNetwork() or withNetworkMode(). This broke scenarios where Selenium needed to communicate with other containers on a shared network. The fix: - Only create an internal network if user didn't provide one - Use the user-provided network for both Selenium and ffmpeg containers - Only stop the network on cleanup if we created it internally Fixes #844 --- .../selenium/src/selenium-container.test.ts | 19 +++++++++++++++++- .../selenium/src/selenium-container.ts | 20 ++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/modules/selenium/src/selenium-container.test.ts b/packages/modules/selenium/src/selenium-container.test.ts index d0bd29d5d..c541e7aeb 100644 --- a/packages/modules/selenium/src/selenium-container.test.ts +++ b/packages/modules/selenium/src/selenium-container.test.ts @@ -1,6 +1,6 @@ import path from "path"; import { Browser, Builder } from "selenium-webdriver"; -import { GenericContainer } from "testcontainers"; +import { GenericContainer, Network } from "testcontainers"; import tmp from "tmp"; import { getImage } from "../../../testcontainers/src/utils/test-helper"; import { SELENIUM_VIDEO_IMAGE, SeleniumContainer } from "./selenium-container"; @@ -45,4 +45,21 @@ describe.for(browsers)("SeleniumContainer", { timeout: 240_000 }, ([browser, ima expect(exitCode).toBe(0); // } }); + + it(`should use provided network when recording for ${browser}`, async () => { + await using network = await new Network().start(); + + await using _webServer = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withNetwork(network) + .withNetworkAliases("webserver") + .withExposedPorts(8080) + .start(); + + const container = await new SeleniumContainer(image).withRecording().withNetwork(network).start(); + + const { exitCode } = await container.exec(["getent", "hosts", "webserver"]); + expect(exitCode).toBe(0); + + await container.stop(); + }); }); diff --git a/packages/modules/selenium/src/selenium-container.ts b/packages/modules/selenium/src/selenium-container.ts index c20c65496..89e78175b 100644 --- a/packages/modules/selenium/src/selenium-container.ts +++ b/packages/modules/selenium/src/selenium-container.ts @@ -77,20 +77,28 @@ export class SeleniumRecordingContainer extends SeleniumContainer { super(image); } - public override async start(): Promise { + private async createNetworkIfNeeded(): Promise { + if (this.networkMode) { + return undefined; + } const network = await new Network().start(); this.withNetwork(network); + return network; + } + + public override async start(): Promise { + const internalNetwork = await this.createNetworkIfNeeded(); this.withNetworkAliases(SELENIUM_NETWORK_ALIAS); const startedSeleniumContainer = await super.start(); const startedFfmpegContainer = await new GenericContainer(SELENIUM_VIDEO_IMAGE) - .withNetwork(network) + .withNetworkMode(this.networkMode!) .withEnvironment({ DISPLAY_CONTAINER_NAME: SELENIUM_NETWORK_ALIAS }) .withWaitStrategy(Wait.forLogMessage(/.*video-recording entered RUNNING state.*/)) .start(); - return new StartedSeleniumRecordingContainer(startedSeleniumContainer, startedFfmpegContainer, network); + return new StartedSeleniumRecordingContainer(startedSeleniumContainer, startedFfmpegContainer, internalNetwork); } } @@ -98,7 +106,7 @@ export class StartedSeleniumRecordingContainer extends StartedSeleniumContainer constructor( startedSeleniumContainer: StartedTestContainer, private readonly startedFfmpegContainer: StartedTestContainer, - private readonly network: StartedNetwork + private readonly internalNetwork?: StartedNetwork ) { super(startedSeleniumContainer); } @@ -106,7 +114,9 @@ export class StartedSeleniumRecordingContainer extends StartedSeleniumContainer override async stop(options?: Partial): Promise { const stoppedSeleniumContainer = await super.stop(options); const stoppedFfmpegContainer = await this.startedFfmpegContainer.stop({ remove: false, timeout: 60_000 }); - await this.network.stop(); + if (this.internalNetwork) { + await this.internalNetwork.stop(); + } return new StoppedSeleniumRecordingContainer(stoppedSeleniumContainer, stoppedFfmpegContainer); } } From 4680d356fb9776bfce86214e547d38117ccdb61d Mon Sep 17 00:00:00 2001 From: Oleksii Sanin Date: Fri, 26 Dec 2025 11:13:26 +0200 Subject: [PATCH 2/2] refactor(selenium): use await using for automatic container cleanup Replace manual container.stop() with await using declaration for consistent resource management pattern across the test file. --- packages/modules/selenium/src/selenium-container.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/modules/selenium/src/selenium-container.test.ts b/packages/modules/selenium/src/selenium-container.test.ts index c541e7aeb..d8acdcd2d 100644 --- a/packages/modules/selenium/src/selenium-container.test.ts +++ b/packages/modules/selenium/src/selenium-container.test.ts @@ -55,11 +55,9 @@ describe.for(browsers)("SeleniumContainer", { timeout: 240_000 }, ([browser, ima .withExposedPorts(8080) .start(); - const container = await new SeleniumContainer(image).withRecording().withNetwork(network).start(); + await using container = await new SeleniumContainer(image).withRecording().withNetwork(network).start(); const { exitCode } = await container.exec(["getent", "hosts", "webserver"]); expect(exitCode).toBe(0); - - await container.stop(); }); });