Skip to content

[grid] Session can be deleted via Grid UI #15808

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

Merged
merged 13 commits into from
Jun 23, 2025
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
8 changes: 8 additions & 0 deletions java/src/org/openqa/selenium/grid/node/config/NodeFlags.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import static org.openqa.selenium.grid.config.StandardGridRoles.NODE_ROLE;
import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_CONNECTION_LIMIT;
import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_DELETE_SESSION_ON_UI;
import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_DETECT_DRIVERS;
import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_DRAIN_AFTER_SESSION_COUNT;
import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_ENABLE_BIDI;
Expand Down Expand Up @@ -241,6 +242,13 @@ public class NodeFlags implements HasRoles {
@ConfigValue(section = NODE_SECTION, name = "drain-after-session-count", example = "1")
public int drainAfterSessionCount = DEFAULT_DRAIN_AFTER_SESSION_COUNT;

@Parameter(
names = {"--delete-session-on-ui"},
arity = 1,
description = "Enable capability to support deleting session on Grid UI. False by default")
@ConfigValue(section = NODE_SECTION, name = "delete-session-on-ui", example = "true")
public Boolean deleteSessionOnUi = DEFAULT_DELETE_SESSION_ON_UI;

@Parameter(
names = {"--enable-cdp"},
arity = 1,
Expand Down
12 changes: 12 additions & 0 deletions java/src/org/openqa/selenium/grid/node/config/NodeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class NodeOptions {
public static final int DEFAULT_SESSION_TIMEOUT = 300;
public static final int DEFAULT_DRAIN_AFTER_SESSION_COUNT = 0;
public static final int DEFAULT_CONNECTION_LIMIT = 10;
public static final boolean DEFAULT_DELETE_SESSION_ON_UI = false;
public static final boolean DEFAULT_ENABLE_CDP = true;
public static final boolean DEFAULT_ENABLE_BIDI = true;
static final String NODE_SECTION = "node";
Expand Down Expand Up @@ -303,6 +304,13 @@ public int getDrainAfterSessionCount() {
DEFAULT_DRAIN_AFTER_SESSION_COUNT);
}

@VisibleForTesting
boolean isSessionDeletedOnUi() {
return config
.getBool(NODE_SECTION, "delete-session-on-ui")
.orElse(DEFAULT_DELETE_SESSION_ON_UI);
}

@VisibleForTesting
boolean isVncEnabled() {
List<String> vncEnvVars = DEFAULT_VNC_ENV_VARS;
Expand Down Expand Up @@ -750,6 +758,10 @@ public Capabilities enhanceStereotype(Capabilities capabilities) {
.setCapability("se:vncEnabled", true)
.setCapability("se:noVncPort", noVncPort());
}
if (isSessionDeletedOnUi()) {
capabilities =
new PersistentCapabilities(capabilities).setCapability("se:deleteSessionOnUi", true);
}
if (isManagedDownloadsEnabled() && canConfigureDownloadsDir(capabilities)) {
capabilities = new PersistentCapabilities(capabilities).setCapability(ENABLE_DOWNLOADS, true);
}
Expand Down
104 changes: 104 additions & 0 deletions java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,110 @@ void testIsVncEnabledAcceptSingleEnvVar() {
assertThat(nodeOptionsEnabled.isVncEnabled()).isFalse();
}

@Test
void deleteSessionOnUiIsEnabledByDefault() {
Config config = new MapConfig(singletonMap("node", singletonMap("detect-drivers", "false")));
NodeOptions nodeOptions = new NodeOptions(config);
assertThat(nodeOptions.isSessionDeletedOnUi()).isFalse();
}

@Test
void deleteSessionOnUiCanBeEnabledExplicitly() {
Config config =
new MapConfig(
singletonMap(
"node",
ImmutableMap.of("detect-drivers", "false", "delete-session-on-ui", "true")));
NodeOptions nodeOptions = new NodeOptions(config);
assertThat(nodeOptions.isSessionDeletedOnUi()).isTrue();
}

@Test
void deleteSessionOnUiCanBeDisabled() {
Config config =
new MapConfig(
singletonMap(
"node",
ImmutableMap.of("detect-drivers", "false", "delete-session-on-ui", "false")));
NodeOptions nodeOptions = new NodeOptions(config);
assertThat(nodeOptions.isSessionDeletedOnUi()).isFalse();
}

@Test
void deleteSessionOnUiCapabilityIsAddedWhenEnabled() {
assumeTrue(
new ChromeDriverInfo().isPresent() || new GeckoDriverInfo().isPresent(),
"A driver needs to be available");

Config config =
new MapConfig(
singletonMap(
"node", ImmutableMap.of("detect-drivers", "true", "delete-session-on-ui", "true")));

List<Capabilities> reported = new ArrayList<>();
new NodeOptions(config)
.getSessionFactories(
caps -> {
reported.add(caps);
return Collections.singleton(HelperFactory.create(config, caps));
});

assertThat(reported)
.filteredOn(capabilities -> capabilities.getCapability("se:deleteSessionOnUi") != null)
.hasSize(reported.size());

assertThat(reported)
.allMatch(
capabilities ->
Boolean.TRUE.equals(capabilities.getCapability("se:deleteSessionOnUi")));
}

@Test
void deleteSessionOnUiCapabilityIsNotAddedWhenDisabled() {
assumeTrue(
new ChromeDriverInfo().isPresent() || new GeckoDriverInfo().isPresent(),
"A driver needs to be available");

Config config =
new MapConfig(
singletonMap(
"node",
ImmutableMap.of("detect-drivers", "true", "delete-session-on-ui", "false")));

List<Capabilities> reported = new ArrayList<>();
new NodeOptions(config)
.getSessionFactories(
caps -> {
reported.add(caps);
return Collections.singleton(HelperFactory.create(config, caps));
});

assertThat(reported)
.filteredOn(capabilities -> capabilities.getCapability("se:deleteSessionOnUi") == null)
.hasSize(reported.size());
}

@Test
void deleteSessionOnUiCapabilityIsAddedByDefault() {
assumeTrue(
new ChromeDriverInfo().isPresent() || new GeckoDriverInfo().isPresent(),
"A driver needs to be available");

Config config = new MapConfig(singletonMap("node", singletonMap("detect-drivers", "true")));

List<Capabilities> reported = new ArrayList<>();
new NodeOptions(config)
.getSessionFactories(
caps -> {
reported.add(caps);
return Collections.singleton(HelperFactory.create(config, caps));
});

assertThat(reported)
.filteredOn(capabilities -> capabilities.getCapability("se:deleteSessionOnUi") == null)
.hasSize(reported.size());
}

private Condition<? super List<? extends Capabilities>> supporting(String name) {
return new Condition<>(
caps -> caps.stream().anyMatch(cap -> name.equals(cap.getBrowserName())),
Expand Down
Loading