Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cb357bf
First implementation
nvborisenko Nov 6, 2025
5a2cdd8
Don't change tests to emphasize backward compatibility
nvborisenko Nov 6, 2025
5c53619
Static factory?
nvborisenko Nov 6, 2025
fc98a78
Throw exception if enum is not branched
nvborisenko Nov 6, 2025
75fc654
Revert "Throw exception if enum is not branched"
nvborisenko Nov 6, 2025
50275d2
Throw exception if enum is not branched
nvborisenko Nov 6, 2025
9accc2b
Non nullable enum values
nvborisenko Nov 7, 2025
1acc7de
Merge branch 'trunk' into dotnet-prompt-behavior-dict
nvborisenko Nov 26, 2025
727a5b3
Move to new UnhandledPromptBehaviorOption file
nvborisenko Nov 26, 2025
96cd5cd
Docs
nvborisenko Nov 26, 2025
83b5218
Docs for possible types
nvborisenko Nov 26, 2025
f5e5845
Clear as options
nvborisenko Nov 26, 2025
c83c67c
Merge branch 'trunk' into dotnet-prompt-behavior-dict
nvborisenko Dec 9, 2025
baf2939
Fix static factory
nvborisenko Dec 9, 2025
bfd55e9
Consider the property in MergeResult
nvborisenko Dec 9, 2025
c94cc5d
Refactor serialization
nvborisenko Dec 9, 2025
4f60927
Fully delegate to ToCapabilities
nvborisenko Dec 9, 2025
aeb98ae
Test
nvborisenko Dec 9, 2025
53efd4c
Merge remote-tracking branch 'upstream/trunk' into dotnet-prompt-beha…
nvborisenko Apr 23, 2026
1e53b6b
UserPromptHandler - spec defined type
nvborisenko Apr 23, 2026
3225204
Support File handler
nvborisenko Apr 23, 2026
b984c9f
Clean
nvborisenko Apr 23, 2026
832d645
Nullable instead of default, obsoleting Default
nvborisenko Apr 23, 2026
8186fb7
Init
nvborisenko Apr 23, 2026
9894ffc
Docs UnhandledPromptBehavior
nvborisenko Apr 23, 2026
0f1836f
Update UserPromptHandler.cs
nvborisenko Apr 23, 2026
f15b022
Thows.Nothing in assertion
nvborisenko Apr 24, 2026
b46633c
Move test to proper fixture
nvborisenko Apr 24, 2026
aa0ccef
Serialize empty
nvborisenko Apr 24, 2026
89eeffa
Merge branch 'trunk' into dotnet-prompt-behavior-dict
nvborisenko Apr 24, 2026
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
70 changes: 7 additions & 63 deletions dotnet/src/webdriver/DriverOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,6 @@

namespace OpenQA.Selenium;

/// <summary>
/// Specifies the behavior of handling unexpected alerts in the IE driver.
/// </summary>
public enum UnhandledPromptBehavior
{
/// <summary>
/// Indicates the behavior is not set.
/// </summary>
Default,

/// <summary>
/// Ignore unexpected alerts, such that the user must handle them.
/// </summary>
Ignore,

/// <summary>
/// Accept unexpected alerts.
/// </summary>
Accept,

/// <summary>
/// Dismiss unexpected alerts.
/// </summary>
Dismiss,

/// <summary>
/// Accepts unexpected alerts and notifies the user that the alert has
/// been accepted by throwing an <see cref="UnhandledAlertException"/>
/// </summary>
AcceptAndNotify,

/// <summary>
/// Dismisses unexpected alerts and notifies the user that the alert has
/// been dismissed by throwing an <see cref="UnhandledAlertException"/>
/// </summary>
DismissAndNotify
}

/// <summary>
/// Specifies the behavior of waiting for page loads in the driver.
/// </summary>
Expand Down Expand Up @@ -160,9 +122,9 @@ protected DriverOptions()

/// <summary>
/// Gets or sets the value for describing how unexpected alerts are to be handled in the browser.
/// Defaults to <see cref="UnhandledPromptBehavior.Default"/>.
/// Defaults to <see langword="null"/>, leaving the behavior unset.
/// </summary>
public UnhandledPromptBehavior UnhandledPromptBehavior { get; set; } = UnhandledPromptBehavior.Default;
public UserPromptHandler? UnhandledPromptBehavior { get; set; }
Comment thread
nvborisenko marked this conversation as resolved.

/// <summary>
/// Gets or sets the value for describing how the browser is to wait for pages to load in the browser.
Expand Down Expand Up @@ -298,7 +260,7 @@ public virtual DriverOptionsMergeResult GetMergeResult(DriverOptions other)
return result;
}

if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default && other.UnhandledPromptBehavior != UnhandledPromptBehavior.Default)
if (this.UnhandledPromptBehavior != other.UnhandledPromptBehavior)
{
result.IsMergeConflict = true;
result.MergeConflictOptionName = "UnhandledPromptBehavior";
Expand Down Expand Up @@ -503,29 +465,11 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification
capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting);
}

if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default)
{
string unhandledPropmtBehaviorSetting = "ignore";
switch (this.UnhandledPromptBehavior)
{
case UnhandledPromptBehavior.Accept:
unhandledPropmtBehaviorSetting = "accept";
break;
var unhandledPromptBehaviorCapability = this.UnhandledPromptBehavior?.ToCapabilities();

case UnhandledPromptBehavior.Dismiss:
unhandledPropmtBehaviorSetting = "dismiss";
break;

case UnhandledPromptBehavior.AcceptAndNotify:
unhandledPropmtBehaviorSetting = "accept and notify";
break;

case UnhandledPromptBehavior.DismissAndNotify:
unhandledPropmtBehaviorSetting = "dismiss and notify";
break;
}

capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, unhandledPropmtBehaviorSetting);
if (unhandledPromptBehaviorCapability != null)
{
capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, unhandledPromptBehaviorCapability);
}
Comment thread
nvborisenko marked this conversation as resolved.

if (this.Proxy != null)
Expand Down
195 changes: 195 additions & 0 deletions dotnet/src/webdriver/UserPromptHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// <copyright file="UserPromptHandler.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

namespace OpenQA.Selenium;

/// <summary>
/// Represents a WebDriver session's user prompt handler, which defines how unhandled browser prompts
/// (alerts, confirms, prompts, beforeunload dialogs, file selection dialogs) are managed during automation.
/// </summary>
/// <remarks>
/// This corresponds to the W3C WebDriver <c>unhandledPromptBehavior</c> capability, which may be expressed
/// either as a single string applied to all prompt types, or as a per-prompt-type map.
/// <para>
/// Available variants:
/// <list type="bullet">
/// <item><description><see cref="Uniform"/> - Wraps a single <see cref="UnhandledPromptBehavior"/> value applied to all prompt types. Create via the implicit conversion from <see cref="UnhandledPromptBehavior"/> or by constructing <see cref="Uniform"/> directly.</description></item>
/// <item><description><see cref="PerPromptType"/> - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, File, Default).</description></item>
/// </list>
/// </para>
/// </remarks>
public abstract record UserPromptHandler
{
private UserPromptHandler() { }
Comment thread
nvborisenko marked this conversation as resolved.
Comment thread
nvborisenko marked this conversation as resolved.

/// <summary>
/// Converts a nullable <see cref="UnhandledPromptBehavior"/> to a <see cref="UserPromptHandler"/> instance,
/// or <see langword="null"/> when <paramref name="value"/> is <see langword="null"/>.
/// </summary>
/// <param name="value">The <see cref="UnhandledPromptBehavior"/> value to convert.</param>
public static implicit operator UserPromptHandler?(UnhandledPromptBehavior? value)
=> value is { } v ? new Uniform(v) : null;
Comment thread
nvborisenko marked this conversation as resolved.

internal abstract object? ToCapabilities();

private static string ConvertBehaviorToString(UnhandledPromptBehavior behavior) =>
behavior switch
{
UnhandledPromptBehavior.Ignore => "ignore",
UnhandledPromptBehavior.Accept => "accept",
UnhandledPromptBehavior.Dismiss => "dismiss",
UnhandledPromptBehavior.AcceptAndNotify => "accept and notify",
UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify",
_ => throw new InvalidOperationException($"UnhandledPromptBehavior value '{behavior}' is not recognized."),
};

/// <summary>
/// Represents a user prompt handler that applies a single <see cref="UnhandledPromptBehavior"/> value
/// as the fallback default for all prompt types.
/// </summary>
/// <param name="Value">The unhandled prompt behavior to apply. Specifies how unexpected browser prompts are handled during automation.</param>
public sealed record Uniform(UnhandledPromptBehavior Value) : UserPromptHandler
{
internal override object? ToCapabilities()
{
#pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete
if (Value == UnhandledPromptBehavior.Default)
{
return null;
}
#pragma warning restore CS0618

return ConvertBehaviorToString(Value);
}
}

/// <summary>
/// Represents a user prompt handler that specifies distinct <see cref="UnhandledPromptBehavior"/> values
/// for individual prompt types (alert, confirm, prompt, beforeunload, file), with a fallback default.
/// </summary>
/// <remarks>Use this variant to configure distinct behaviors for alert, confirm, prompt, beforeunload, and file
/// selection dialogs encountered during browser automation. Each property allows you to control the response to a
/// specific type of unhandled prompt, enabling fine-grained handling beyond a single global setting.</remarks>
public sealed record PerPromptType : UserPromptHandler
{
/// <summary>
/// Gets the behavior to use when an unexpected alert is encountered during automation,
/// or <see langword="null"/> to leave it unset.
/// </summary>
public UnhandledPromptBehavior? Alert { get; init; }

/// <summary>
/// Gets the behavior to use when a confirmation prompt is encountered,
/// or <see langword="null"/> to leave it unset.
/// </summary>
public UnhandledPromptBehavior? Confirm { get; init; }

/// <summary>
/// Gets the behavior to use when an unexpected prompt is encountered during automation,
/// or <see langword="null"/> to leave it unset.
/// </summary>
public UnhandledPromptBehavior? Prompt { get; init; }

/// <summary>
/// Gets the behavior to use when an unexpected beforeunload dialog is encountered,
/// or <see langword="null"/> to leave it unset.
/// </summary>
public UnhandledPromptBehavior? BeforeUnload { get; init; }

/// <summary>
/// Gets the behavior to use when an unexpected file selection dialog is encountered,
/// or <see langword="null"/> to leave it unset.
/// </summary>
/// <remarks>The "file" prompt type is respected only in WebDriver BiDi sessions.</remarks>
public UnhandledPromptBehavior? File { get; init; }

/// <summary>
/// Gets the fallback behavior to use when no specific handler is defined for a given prompt type,
/// or <see langword="null"/> to leave it unset.
/// </summary>
public UnhandledPromptBehavior? Default { get; init; }

internal override object? ToCapabilities()
{
Dictionary<string, string> capabilities = [];
AddIfSet(capabilities, "alert", Alert);
AddIfSet(capabilities, "confirm", Confirm);
AddIfSet(capabilities, "prompt", Prompt);
AddIfSet(capabilities, "beforeUnload", BeforeUnload);
AddIfSet(capabilities, "file", File);
AddIfSet(capabilities, "default", Default);
return capabilities;
Comment thread
nvborisenko marked this conversation as resolved.
}

private static void AddIfSet(Dictionary<string, string> capabilities, string key, UnhandledPromptBehavior? value)
{
#pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete
if (value is { } v && v != UnhandledPromptBehavior.Default)
{
capabilities[key] = ConvertBehaviorToString(v);
}
#pragma warning restore CS0618
}
}
}

/// <summary>
/// Specifies how a WebDriver session handles an unhandled user prompt.
/// </summary>
/// <remarks>
/// Corresponds to the handler values defined for the W3C WebDriver
/// <c>unhandledPromptBehavior</c> capability:
/// <c>dismiss</c>, <c>accept</c>, <c>dismiss and notify</c>, <c>accept and notify</c>, and <c>ignore</c>.
/// When no handler is configured, the spec's implicit default is <see cref="DismissAndNotify"/>.
/// </remarks>
public enum UnhandledPromptBehavior
{
/// <summary>
/// Sentinel value meaning "behavior not set". Not part of the W3C WebDriver spec.
/// </summary>
[Obsolete("Use a nullable UnhandledPromptBehavior? and pass null to leave the behavior unset. This member will be removed in v4.46.")]
Default,

/// <summary>
/// Ignore unexpected alerts, such that the user must handle them.
/// </summary>
Ignore,

/// <summary>
/// Accept unexpected alerts.
/// </summary>
Accept,

/// <summary>
/// Dismiss unexpected alerts.
/// </summary>
Dismiss,

/// <summary>
/// Accepts unexpected alerts and notifies the user that the alert has
/// been accepted by throwing an <see cref="UnhandledAlertException"/>
/// </summary>
AcceptAndNotify,

/// <summary>
/// Dismisses unexpected alerts and notifies the user that the alert has
/// been dismissed by throwing an <see cref="UnhandledAlertException"/>
/// </summary>
DismissAndNotify
}
1 change: 0 additions & 1 deletion dotnet/test/webdriver/AlertsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -555,5 +555,4 @@ private Func<bool> WindowHandleCountToBe(int count)
return driver.WindowHandles.Count == count;
};
}

}
55 changes: 24 additions & 31 deletions dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,56 +71,49 @@ public void CanSilentlyDismissUnhandledAlert()
[Test]
public void CanDismissUnhandledAlertsByDefault()
{
ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior.Default, "null");
ExecuteTestWithUnhandledPrompt(null, "null");
}

[Test]
public void CanDismissUnhandledAlertsViaPerType()
{
ExecuteTestWithUnhandledPrompt(new UserPromptHandler.PerPromptType
{
Alert = UnhandledPromptBehavior.Dismiss
}, "null");
}

[Test]
public void CanDismissUnhandledAlertsViaDefaultPerType()
{
ExecuteTestWithUnhandledPrompt(new UserPromptHandler.PerPromptType(), "null");
}

[Test]
[IgnoreBrowser(Browser.Safari, "Test hangs waiting for alert acknowledgement in Safari, but works in Tech Preview")]
public void CanIgnoreUnhandledAlert()
{
Assert.That(() => ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior.Ignore, "Text ignored"), Throws.InstanceOf<WebDriverException>().With.InnerException.InstanceOf<UnhandledAlertException>());
Assert.That(
() => ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior.Ignore, "Text ignored"),
Throws.InstanceOf<WebDriverException>().With.InnerException.InstanceOf<UnhandledAlertException>());
localDriver.SwitchTo().Alert().Dismiss();
}

private void ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior behavior, string expectedAlertText)
private void ExecuteTestWithUnhandledPrompt(UserPromptHandler behavior, string expectedAlertText)
{
bool silentlyHandlePrompt = behavior == UnhandledPromptBehavior.Accept || behavior == UnhandledPromptBehavior.Dismiss;
UnhandledPromptBehaviorOptions options = new UnhandledPromptBehaviorOptions();
if (behavior != UnhandledPromptBehavior.Default)
UnhandledPromptBehaviorOptions options = new()
{
options.UnhandledPromptBehavior = behavior;
}
UnhandledPromptBehavior = behavior,
Comment thread
nvborisenko marked this conversation as resolved.
};

localDriver = EnvironmentManager.Instance.CreateDriverInstance(options);
localDriver.Url = alertsPage;
IWebElement resultElement = localDriver.FindElement(By.Id("text"));
localDriver.FindElement(By.Id("prompt-with-default")).Click();

WaitFor(ElementTextToBeEqual(resultElement, expectedAlertText, silentlyHandlePrompt), "Did not find text");
WaitFor(() => resultElement.Text == expectedAlertText, "Did not find text");
}
Comment thread
nvborisenko marked this conversation as resolved.

private Func<bool> ElementTextToBeEqual(IWebElement resultElement, string expectedAlertText, bool silentlyHandlePrompt)
{
return () =>
{
try
{
return resultElement.Text == expectedAlertText;
}
catch (UnhandledAlertException)
{
if (!silentlyHandlePrompt)
{
throw;
}
}
catch (NoSuchElementException)
{
}

return false;
};
}

public class UnhandledPromptBehaviorOptions : DriverOptions
{
Expand Down
Loading