Skip to content
Merged
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
24 changes: 12 additions & 12 deletions docs/modules/webdriver_containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,22 @@ every test.

The following field in your JUnit UI test class will prepare a container running Chrome:
<!--codeinclude-->
[Chrome](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java) inside_block:junitRule
[Chrome](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeWebDriverContainerTest.java) inside_block:junitRule
<!--/codeinclude-->


Now, instead of instantiating an instance of WebDriver directly, use the following to obtain an instance inside your
test methods:
<!--codeinclude-->
[RemoteWebDriver](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getWebDriver
[RemoteWebDriver](../../modules/selenium/src/test/java/org/testcontainers/selenium/LocalServerWebDriverContainerTest.java) inside_block:getWebDriver
<!--/codeinclude-->

You can then use this driver instance like a regular WebDriver.

Note that, if you want to test a **web application running on the host machine** (the machine the JUnit tests are
running on - which is quite likely), you'll need to use [the host exposing](../features/networking.md#exposing-host-ports-to-the-container) feature of Testcontainers, e.g.:
<!--codeinclude-->
[Open Web Page](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getPage
[Open Web Page](../../modules/selenium/src/test/java/org/testcontainers/selenium/LocalServerWebDriverContainerTest.java) inside_block:getPage
<!--/codeinclude-->


Expand All @@ -48,9 +48,9 @@ running on - which is quite likely), you'll need to use [the host exposing](../f

At the moment, Chrome, Firefox and Edge are supported. To switch, simply change the first parameter to the rule constructor:
<!--codeinclude-->
[Chrome](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java) inside_block:junitRule
[Firefox](../../modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java) inside_block:junitRule
[Edge](../../modules/selenium/src/test/java/org/testcontainers/junit/EdgeWebDriverContainerTest.java) inside_block:junitRule
[Chrome](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeWebDriverContainerTest.java) inside_block:junitRule
[Firefox](../../modules/selenium/src/test/java/org/testcontainers/selenium/FirefoxWebDriverContainerTest.java) inside_block:junitRule
[Edge](../../modules/selenium/src/test/java/org/testcontainers/selenium/EdgeWebDriverContainerTest.java) inside_block:junitRule
<!--/codeinclude-->

### Recording videos
Expand All @@ -59,30 +59,30 @@ By default, no videos will be recorded. However, you can instruct Testcontainers
just for failing tests.

<!--codeinclude-->
[Record all Tests](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordAll
[Record failing Tests](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFailing
[Record all Tests](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:recordAll
[Record failing Tests](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFailing
<!--/codeinclude-->

Note that the second parameter of `withRecordingMode` should be a directory where recordings can be saved.

By default, the video will be recorded in [FLV](https://en.wikipedia.org/wiki/Flash_Video) format, but you can specify it explicitly or change it to [MP4](https://en.wikipedia.org/wiki/MPEG-4_Part_14) using `withRecordingMode` method with `VncRecordingFormat` option:

<!--codeinclude-->
[Video Format in MP4](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordMp4
[Video Format in FLV](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFlv
[Video Format in MP4](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:recordMp4
[Video Format in FLV](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:recordFlv
<!--/codeinclude-->

If you would like to customise the file name of the recording, or provide a different directory at runtime based on the description of the test and/or its success or failure, you may provide a custom recording file factory as follows:
<!--codeinclude-->
[CustomRecordingFileFactory](../../modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java) inside_block:withRecordingFileFactory
[CustomRecordingFileFactory](../../modules/selenium/src/test/java/org/testcontainers/selenium/ChromeRecordingWebDriverContainerTest.java) inside_block:withRecordingFileFactory
<!--/codeinclude-->


Note the factory must implement `org.testcontainers.containers.RecordingFileFactory`.

## More examples

A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/main/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java).
A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/main/modules/selenium/src/test/java/org/testcontainers/selenium/ChromeWebDriverContainerTest.java).

## Adding this module to your project dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@
* {@code selenium/standalone-edge}, {@code selenium/standalone-chrome-debug}, {@code selenium/standalone-firefox-debug}
* <p>
* Exposed ports: 4444
*
* @deprecated use {@link org.testcontainers.selenium.BrowserWebDriverContainer} instead.
*/
@Deprecated
public class BrowserWebDriverContainer<SELF extends BrowserWebDriverContainer<SELF>>
extends GenericContainer<SELF>
implements LinkableContainer, TestLifecycleAware {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package org.testcontainers.selenium;

import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.AccessMode;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.ContainerLaunchException;
import org.testcontainers.containers.DefaultRecordingFileFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.RecordingFileFactory;
import org.testcontainers.containers.VncRecordingContainer;
import org.testcontainers.containers.VncRecordingContainer.VncRecordingFormat;
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.lifecycle.TestDescription;
import org.testcontainers.lifecycle.TestLifecycleAware;
import org.testcontainers.utility.DockerImageName;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;

/**
* A chrome/firefox/custom container based on SeleniumHQ's standalone container sets.
* <p>
* Supported images: {@code selenium/standalone-chrome}, {@code selenium/standalone-firefox},
* {@code selenium/standalone-edge}, {@code selenium/standalone-chrome-debug}, {@code selenium/standalone-firefox-debug}
* <p>
* Exposed ports: 4444
*/
public class BrowserWebDriverContainer
extends GenericContainer<BrowserWebDriverContainer>
implements TestLifecycleAware {

private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome");

private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox");

private static final DockerImageName EDGE_IMAGE = DockerImageName.parse("selenium/standalone-edge");

private static final DockerImageName CHROME_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug");

private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse(
"selenium/standalone-firefox-debug"
);

private static final DockerImageName[] COMPATIBLE_IMAGES = new DockerImageName[] {
CHROME_IMAGE,
FIREFOX_IMAGE,
EDGE_IMAGE,
CHROME_DEBUG_IMAGE,
FIREFOX_DEBUG_IMAGE,
};

private static final String DEFAULT_PASSWORD = "secret";

private static final int SELENIUM_PORT = 4444;

private static final int VNC_PORT = 5900;

private static final String NO_PROXY_KEY = "no_proxy";

private static final String TC_TEMP_DIR_PREFIX = "tc";

private VncRecordingMode recordingMode = VncRecordingMode.RECORD_FAILING;

private VncRecordingFormat recordingFormat;

private RecordingFileFactory recordingFileFactory;

private File vncRecordingDirectory;

private VncRecordingContainer vncRecordingContainer = null;

private static final Logger LOGGER = LoggerFactory.getLogger(BrowserWebDriverContainer.class);

/**
* Constructor taking a specific webdriver container name and tag
* @param dockerImageName Name of the selenium docker image
*/
public BrowserWebDriverContainer(String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

/**
* Constructor taking a specific webdriver container name and tag
* @param dockerImageName Name of the selenium docker image
*/
public BrowserWebDriverContainer(DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(COMPATIBLE_IMAGES);

waitingFor(getDefaultWaitStrategy());

withRecordingFileFactory(new DefaultRecordingFileFactory());
// We have to force SKIP mode for the recording by default because we don't know if the image has VNC or not
recordingMode = VncRecordingMode.SKIP;
}

@NotNull
@Override
protected Set<Integer> getLivenessCheckPorts() {
Integer seleniumPort = getMappedPort(SELENIUM_PORT);
if (recordingMode == VncRecordingMode.SKIP) {
return ImmutableSet.of(seleniumPort);
} else {
return ImmutableSet.of(seleniumPort, getMappedPort(VNC_PORT));
}
}

@Override
protected void configure() {
if (recordingMode != VncRecordingMode.SKIP) {
if (vncRecordingDirectory == null) {
try {
vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile();
} catch (IOException e) {
// should never happen as per javadoc, since we use valid prefix
logger().error("Exception while trying to create temp directory", e);
throw new ContainerLaunchException("Exception while trying to create temp directory", e);
}
}

if (getNetwork() == null) {
withNetwork(Network.SHARED);
}

vncRecordingContainer =
new VncRecordingContainer(this)
.withVncPassword(DEFAULT_PASSWORD)
.withVncPort(VNC_PORT)
.withVideoFormat(recordingFormat);
}

String timeZone = System.getProperty("user.timezone");

if (timeZone == null || timeZone.isEmpty()) {
timeZone = "Etc/UTC";
}

addExposedPorts(SELENIUM_PORT, VNC_PORT);
addEnv("TZ", timeZone);

if (!getEnvMap().containsKey(NO_PROXY_KEY)) {
addEnv(NO_PROXY_KEY, "localhost");
}

setCommand("/opt/bin/entry_point.sh");

if (getShmSize() == null) {
if (SystemUtils.IS_OS_WINDOWS) {
withSharedMemorySize(512 * FileUtils.ONE_MB);
} else {
this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw));
}
}

/*
* Some unreliability of the selenium browser containers has been observed, so allow multiple attempts to start.
*/
setStartupAttempts(3);
}

public URL getSeleniumAddress() {
try {
return new URL("http", getHost(), getMappedPort(SELENIUM_PORT), "/wd/hub");
} catch (MalformedURLException e) {
e.printStackTrace(); // TODO
return null;
}
}

public String getVncAddress() {
return "vnc://vnc:secret@" + getHost() + ":" + getMappedPort(VNC_PORT);
}

@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
if (vncRecordingContainer != null) {
LOGGER.debug("Starting VNC recording");
vncRecordingContainer.start();
}
}

@Override
public void afterTest(TestDescription description, Optional<Throwable> throwable) {
retainRecordingIfNeeded(description.getFilesystemFriendlyName(), !throwable.isPresent());
}

@Override
public void stop() {
if (vncRecordingContainer != null) {
try {
vncRecordingContainer.stop();
} catch (Exception e) {
LOGGER.debug("Failed to stop vncRecordingContainer", e);
}
vncRecordingContainer = null;
}

super.stop();
}

private void retainRecordingIfNeeded(String prefix, boolean succeeded) {
final boolean shouldRecord;
switch (recordingMode) {
case RECORD_ALL:
shouldRecord = true;
break;
case RECORD_FAILING:
shouldRecord = !succeeded;
break;
default:
shouldRecord = false;
break;
}

if (shouldRecord) {
File recordingFile = recordingFileFactory.recordingFileForTest(
vncRecordingDirectory,
prefix,
succeeded,
vncRecordingContainer.getVideoFormat()
);
LOGGER.info("Screen recordings for test {} will be stored at: {}", prefix, recordingFile);

vncRecordingContainer.saveRecordingToFile(recordingFile);
}
}

public BrowserWebDriverContainer withRecordingMode(VncRecordingMode recordingMode, File vncRecordingDirectory) {
return withRecordingMode(recordingMode, vncRecordingDirectory, null);
}

public BrowserWebDriverContainer withRecordingMode(
VncRecordingMode recordingMode,
File vncRecordingDirectory,
VncRecordingFormat recordingFormat
) {
this.recordingMode = recordingMode;
this.vncRecordingDirectory = vncRecordingDirectory;
this.recordingFormat = recordingFormat;
return self();
}

public BrowserWebDriverContainer withRecordingFileFactory(RecordingFileFactory recordingFileFactory) {
this.recordingFileFactory = recordingFileFactory;
return self();
}

private WaitStrategy getDefaultWaitStrategy() {
final WaitStrategy logWaitStrategy = new LogMessageWaitStrategy()
.withRegEx(
".*(RemoteWebDriver instances should connect to|Selenium Server is up and running|Started Selenium Standalone).*\n"
)
.withStartupTimeout(Duration.ofMinutes(1));

return new WaitAllStrategy()
.withStrategy(logWaitStrategy)
.withStrategy(new HostPortWaitStrategy())
.withStartupTimeout(Duration.ofMinutes(1));
}

public enum VncRecordingMode {
SKIP,
RECORD_ALL,
RECORD_FAILING,
}
}
Loading
Loading