From cb357bfbe879ea7d91cb6c6b2b5e003934f04392 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:00:20 +0300 Subject: [PATCH 01/26] First implementation --- dotnet/src/webdriver/DriverOptions.cs | 87 ++++++++++++++++++++------ dotnet/test/common/BiDi/BiDiFixture.cs | 2 +- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 483ceb03130a4..345eb7b40837a 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -26,6 +26,27 @@ namespace OpenQA.Selenium; +public abstract record UnhandledPromptBehaviorOption +{ + public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior? value) + => new UnhandledPromptBehaviorSingleOption(value); +} + +public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior? Value) : UnhandledPromptBehaviorOption; + +public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption +{ + public UnhandledPromptBehavior? Alert { get; set; } + + public UnhandledPromptBehavior? Confirm { get; set; } + + public UnhandledPromptBehavior? Prompt { get; set; } + + public UnhandledPromptBehavior? BeforeUnload { get; set; } + + public UnhandledPromptBehavior? Default { get; set; } +} + /// /// Specifies the behavior of handling unexpected alerts in the IE driver. /// @@ -164,7 +185,7 @@ protected DriverOptions() /// Gets or sets the value for describing how unexpected alerts are to be handled in the browser. /// Defaults to . /// - public UnhandledPromptBehavior UnhandledPromptBehavior { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehaviorOption? UnhandledPromptBehavior { get; set; } /// /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. @@ -303,7 +324,7 @@ public virtual DriverOptionsMergeResult GetMergeResult(DriverOptions other) return result; } - if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default && other.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) + if (this.UnhandledPromptBehavior is not null && other.UnhandledPromptBehavior is not null) { result.IsMergeConflict = true; result.MergeConflictOptionName = "UnhandledPromptBehavior"; @@ -508,29 +529,59 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) + [return: NotNullIfNotNull(nameof(behavior))] + static string? UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch { - string unhandledPropmtBehaviorSetting = "ignore"; - switch (this.UnhandledPromptBehavior) + Selenium.UnhandledPromptBehavior.Ignore => "ignore", + Selenium.UnhandledPromptBehavior.Accept => "accept", + Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", + Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", + Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", + _ => null + }; + + if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption) + { + var stringValue = UnhandledPromptBehaviorToString(singleOption.Value); + + if (stringValue is not null) { - case UnhandledPromptBehavior.Accept: - unhandledPropmtBehaviorSetting = "accept"; - break; + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); + } + } + else if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorMultiOption multiOption) + { + Dictionary multiOptionDictionary = []; - case UnhandledPromptBehavior.Dismiss: - unhandledPropmtBehaviorSetting = "dismiss"; - break; + if (multiOption.Alert is not null) + { + multiOptionDictionary["alert"] = UnhandledPromptBehaviorToString(multiOption.Alert); + } - case UnhandledPromptBehavior.AcceptAndNotify: - unhandledPropmtBehaviorSetting = "accept and notify"; - break; + if (multiOption.Confirm is not null) + { + multiOptionDictionary["confirm"] = UnhandledPromptBehaviorToString(multiOption.Confirm); + } - case UnhandledPromptBehavior.DismissAndNotify: - unhandledPropmtBehaviorSetting = "dismiss and notify"; - break; + if (multiOption.Prompt is not null) + { + multiOptionDictionary["prompt"] = UnhandledPromptBehaviorToString(multiOption.Prompt); } - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, unhandledPropmtBehaviorSetting); + if (multiOption.BeforeUnload is not null) + { + multiOptionDictionary["beforeUnload"] = UnhandledPromptBehaviorToString(multiOption.BeforeUnload); + } + + if (multiOption.Default is not null) + { + multiOptionDictionary["default"] = UnhandledPromptBehaviorToString(multiOption.Default); + } + + if (multiOptionDictionary.Count != 0) + { + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, multiOptionDictionary); + } } if (this.Proxy != null) diff --git a/dotnet/test/common/BiDi/BiDiFixture.cs b/dotnet/test/common/BiDi/BiDiFixture.cs index 88fe223a71d1c..72b69b10795b8 100644 --- a/dotnet/test/common/BiDi/BiDiFixture.cs +++ b/dotnet/test/common/BiDi/BiDiFixture.cs @@ -39,7 +39,7 @@ public async Task BiDiSetUp() var options = new BiDiEnabledDriverOptions() { UseWebSocketUrl = true, - UnhandledPromptBehavior = UnhandledPromptBehavior.Ignore, + UnhandledPromptBehavior = new UnhandledPromptBehaviorMultiOption() { Default = UnhandledPromptBehavior.Ignore }, }; driver = EnvironmentManager.Instance.CreateDriverInstance(options); From 5a2cdd834c4fb260d8ab188fab1879d37ede9d92 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:05:20 +0300 Subject: [PATCH 02/26] Don't change tests to emphasize backward compatibility --- dotnet/test/common/BiDi/BiDiFixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/test/common/BiDi/BiDiFixture.cs b/dotnet/test/common/BiDi/BiDiFixture.cs index 72b69b10795b8..88fe223a71d1c 100644 --- a/dotnet/test/common/BiDi/BiDiFixture.cs +++ b/dotnet/test/common/BiDi/BiDiFixture.cs @@ -39,7 +39,7 @@ public async Task BiDiSetUp() var options = new BiDiEnabledDriverOptions() { UseWebSocketUrl = true, - UnhandledPromptBehavior = new UnhandledPromptBehaviorMultiOption() { Default = UnhandledPromptBehavior.Ignore }, + UnhandledPromptBehavior = UnhandledPromptBehavior.Ignore, }; driver = EnvironmentManager.Instance.CreateDriverInstance(options); From 5c53619ab1da824593e594f8b97b8696863a2e41 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:12:53 +0300 Subject: [PATCH 03/26] Static factory? --- dotnet/src/webdriver/DriverOptions.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 345eb7b40837a..56518ef6f1008 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -29,7 +29,17 @@ namespace OpenQA.Selenium; public abstract record UnhandledPromptBehaviorOption { public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior? value) + => Single(value); + + public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior? value) => new UnhandledPromptBehaviorSingleOption(value); + + public static UnhandledPromptBehaviorOption Multi(UnhandledPromptBehavior? alert = null, + UnhandledPromptBehavior? confirm = null, + UnhandledPromptBehavior? prompt = null, + UnhandledPromptBehavior? beforeUnload = null, + UnhandledPromptBehavior? @default = null) + => new UnhandledPromptBehaviorMultiOption() with { Alert = alert, Confirm = confirm, Prompt = prompt, BeforeUnload = beforeUnload, Default = @default }; } public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior? Value) : UnhandledPromptBehaviorOption; From fc98a7829d422a1f52d6364d31f3f9079e5088b0 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:53:06 +0300 Subject: [PATCH 04/26] Throw exception if enum is not branched --- dotnet/src/webdriver/DriverOptions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 56518ef6f1008..4688281491ade 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -539,15 +539,14 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - [return: NotNullIfNotNull(nameof(behavior))] - static string? UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch + static string UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch { Selenium.UnhandledPromptBehavior.Ignore => "ignore", Selenium.UnhandledPromptBehavior.Accept => "accept", Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", - _ => null + _ => throw new WebDriverException($"'{behavior}' unhandled prompt behavior value is not recognized."), }; if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption) From 75fc654173edb19b78a70dbcf8e262639d9a1431 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:55:23 +0300 Subject: [PATCH 05/26] Revert "Throw exception if enum is not branched" This reverts commit fc98a7829d422a1f52d6364d31f3f9079e5088b0. --- dotnet/src/webdriver/DriverOptions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 4688281491ade..56518ef6f1008 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -539,14 +539,15 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - static string UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch + [return: NotNullIfNotNull(nameof(behavior))] + static string? UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch { Selenium.UnhandledPromptBehavior.Ignore => "ignore", Selenium.UnhandledPromptBehavior.Accept => "accept", Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", - _ => throw new WebDriverException($"'{behavior}' unhandled prompt behavior value is not recognized."), + _ => null }; if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption) From 50275d2f1b30b7e40ebfc4b6dd472e66ce350ce8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 6 Nov 2025 22:33:52 +0300 Subject: [PATCH 06/26] Throw exception if enum is not branched --- dotnet/src/webdriver/DriverOptions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 56518ef6f1008..e52a79a1eadae 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -547,7 +547,8 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", - _ => null + null or Selenium.UnhandledPromptBehavior.Default => null, + _ => throw new ArgumentOutOfRangeException(nameof(behavior), $"UnhandledPromptBehavior value '{behavior}' is not recognized."), }; if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption) From 9accc2bd124a802f30a89f744df6474c7ec14556 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 7 Nov 2025 22:00:07 +0300 Subject: [PATCH 07/26] Non nullable enum values --- dotnet/src/webdriver/DriverOptions.cs | 45 +++++++++++---------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index e52a79a1eadae..4e14a6f1a6c13 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -28,33 +28,29 @@ namespace OpenQA.Selenium; public abstract record UnhandledPromptBehaviorOption { - public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior? value) + public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior value) => Single(value); - public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior? value) + public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior value) => new UnhandledPromptBehaviorSingleOption(value); - public static UnhandledPromptBehaviorOption Multi(UnhandledPromptBehavior? alert = null, - UnhandledPromptBehavior? confirm = null, - UnhandledPromptBehavior? prompt = null, - UnhandledPromptBehavior? beforeUnload = null, - UnhandledPromptBehavior? @default = null) - => new UnhandledPromptBehaviorMultiOption() with { Alert = alert, Confirm = confirm, Prompt = prompt, BeforeUnload = beforeUnload, Default = @default }; + public static UnhandledPromptBehaviorOption Multi() + => new UnhandledPromptBehaviorMultiOption(); } -public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior? Value) : UnhandledPromptBehaviorOption; +public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption; public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption { - public UnhandledPromptBehavior? Alert { get; set; } + public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; - public UnhandledPromptBehavior? Confirm { get; set; } + public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; - public UnhandledPromptBehavior? Prompt { get; set; } + public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; - public UnhandledPromptBehavior? BeforeUnload { get; set; } + public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; - public UnhandledPromptBehavior? Default { get; set; } + public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; } /// @@ -539,52 +535,47 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - [return: NotNullIfNotNull(nameof(behavior))] - static string? UnhandledPromptBehaviorToString(UnhandledPromptBehavior? behavior) => behavior switch + static string UnhandledPromptBehaviorToString(UnhandledPromptBehavior behavior) => behavior switch { Selenium.UnhandledPromptBehavior.Ignore => "ignore", Selenium.UnhandledPromptBehavior.Accept => "accept", Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", - null or Selenium.UnhandledPromptBehavior.Default => null, _ => throw new ArgumentOutOfRangeException(nameof(behavior), $"UnhandledPromptBehavior value '{behavior}' is not recognized."), }; - if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption) + if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption && singleOption.Value != Selenium.UnhandledPromptBehavior.Default) { var stringValue = UnhandledPromptBehaviorToString(singleOption.Value); - if (stringValue is not null) - { - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); - } + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); } else if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorMultiOption multiOption) { Dictionary multiOptionDictionary = []; - if (multiOption.Alert is not null) + if (multiOption.Alert is not Selenium.UnhandledPromptBehavior.Default) { multiOptionDictionary["alert"] = UnhandledPromptBehaviorToString(multiOption.Alert); } - if (multiOption.Confirm is not null) + if (multiOption.Confirm is not Selenium.UnhandledPromptBehavior.Default) { multiOptionDictionary["confirm"] = UnhandledPromptBehaviorToString(multiOption.Confirm); } - if (multiOption.Prompt is not null) + if (multiOption.Prompt is not Selenium.UnhandledPromptBehavior.Default) { multiOptionDictionary["prompt"] = UnhandledPromptBehaviorToString(multiOption.Prompt); } - if (multiOption.BeforeUnload is not null) + if (multiOption.BeforeUnload is not Selenium.UnhandledPromptBehavior.Default) { multiOptionDictionary["beforeUnload"] = UnhandledPromptBehaviorToString(multiOption.BeforeUnload); } - if (multiOption.Default is not null) + if (multiOption.Default is not Selenium.UnhandledPromptBehavior.Default) { multiOptionDictionary["default"] = UnhandledPromptBehaviorToString(multiOption.Default); } From 727a5b3c10da51bd81f71f4215c08ff03841eff1 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:51:17 +0300 Subject: [PATCH 08/26] Move to new UnhandledPromptBehaviorOption file --- dotnet/src/webdriver/DriverOptions.cs | 65 -------------- .../UnhandledPromptBehaviorOption.cs | 85 +++++++++++++++++++ 2 files changed, 85 insertions(+), 65 deletions(-) create mode 100644 dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 4e14a6f1a6c13..555a78d7a3a51 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -26,71 +26,6 @@ namespace OpenQA.Selenium; -public abstract record UnhandledPromptBehaviorOption -{ - public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior value) - => Single(value); - - public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior value) - => new UnhandledPromptBehaviorSingleOption(value); - - public static UnhandledPromptBehaviorOption Multi() - => new UnhandledPromptBehaviorMultiOption(); -} - -public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption; - -public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption -{ - public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; - - public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; - - public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; - - public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; - - public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; -} - -/// -/// Specifies the behavior of handling unexpected alerts in the IE driver. -/// -public enum UnhandledPromptBehavior -{ - /// - /// Indicates the behavior is not set. - /// - Default, - - /// - /// Ignore unexpected alerts, such that the user must handle them. - /// - Ignore, - - /// - /// Accept unexpected alerts. - /// - Accept, - - /// - /// Dismiss unexpected alerts. - /// - Dismiss, - - /// - /// Accepts unexpected alerts and notifies the user that the alert has - /// been accepted by throwing an - /// - AcceptAndNotify, - - /// - /// Dismisses unexpected alerts and notifies the user that the alert has - /// been dismissed by throwing an - /// - DismissAndNotify -} - /// /// Specifies the behavior of waiting for page loads in the driver. /// diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs new file mode 100644 index 0000000000000..8366ef08966e5 --- /dev/null +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -0,0 +1,85 @@ +// +// 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. +// + +namespace OpenQA.Selenium; + +public abstract record UnhandledPromptBehaviorOption +{ + public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior value) + => Single(value); + + public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior value) + => new UnhandledPromptBehaviorSingleOption(value); + + public static UnhandledPromptBehaviorOption Multi() + => new UnhandledPromptBehaviorMultiOption(); +} + +public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption; + +public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption +{ + public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; + + public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; + + public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; + + public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; + + public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; +} + +/// +/// Specifies the behavior of handling unexpected alerts in the IE driver. +/// +public enum UnhandledPromptBehavior +{ + /// + /// Indicates the behavior is not set. + /// + Default, + + /// + /// Ignore unexpected alerts, such that the user must handle them. + /// + Ignore, + + /// + /// Accept unexpected alerts. + /// + Accept, + + /// + /// Dismiss unexpected alerts. + /// + Dismiss, + + /// + /// Accepts unexpected alerts and notifies the user that the alert has + /// been accepted by throwing an + /// + AcceptAndNotify, + + /// + /// Dismisses unexpected alerts and notifies the user that the alert has + /// been dismissed by throwing an + /// + DismissAndNotify +} From 96cd5cd8592d623509a1da2f5a8773de87868752 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:58:53 +0300 Subject: [PATCH 09/26] Docs --- .../UnhandledPromptBehaviorOption.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index 8366ef08966e5..fa4ebc318a08b 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -19,30 +19,86 @@ namespace OpenQA.Selenium; +/// +/// Represents a configuration option that determines how unhandled prompts are managed during automated browser +/// interactions. +/// +/// Use this type to specify whether a single unhandled prompt behavior or multiple behaviors should be +/// applied. The static methods provide convenient ways to create either a single-behavior or multi-behavior option. +/// This abstraction is typically used in scenarios where browser automation frameworks need to control the handling of +/// unexpected dialogs or prompts. public abstract record UnhandledPromptBehaviorOption { + /// + /// Converts a value of type to an instance. + /// + /// The value to convert. public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior value) => Single(value); + /// + /// Creates an representing a single value. + /// + /// The to apply for all prompt types. + /// An wrapping the provided behavior. public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior value) => new UnhandledPromptBehaviorSingleOption(value); + /// + /// Creates an allowing individual values per prompt type. + /// + /// An with per-prompt configurable behaviors. public static UnhandledPromptBehaviorOption Multi() => new UnhandledPromptBehaviorMultiOption(); } +/// +/// Represents an option that specifies a single unhandled prompt behavior to use when interacting with browser dialogs. +/// +/// The unhandled prompt behavior to apply. Specifies how unexpected browser prompts are handled during automation. public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption; +/// +/// Represents a set of options that specify how unhandled browser prompts are handled for different prompt types. +/// +/// Use this class to configure distinct behaviors for alert, confirm, prompt, and beforeunload 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. public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption { + /// + /// Gets or sets the behavior to use when an unexpected alert is encountered during automation. + /// public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; + /// + /// Gets or sets the behavior to use when a confirmation prompt is encountered. + /// + /// Set this property to specify how the system should respond to confirmation dialogs, such as + /// JavaScript confirm boxes, during automated operations. The default value is , which applies the standard handling defined by the + /// environment. public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; + /// + /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation. + /// + /// Set this property to control how the system responds to unhandled prompts, such as alerts or + /// confirmation dialogs, that appear unexpectedly. The default behavior is determined by the value of + /// . public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; + /// + /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered. + /// + /// Use this property to specify how the application should respond to beforeunload dialogs that + /// appear unexpectedly during automated browser interactions. This setting determines whether such dialogs are + /// automatically accepted, dismissed, or cause an error. public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; + /// + /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. + /// public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; } From 83b5218dcf01782ff3f23c0c74603679cf38e339 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 26 Nov 2025 23:06:58 +0300 Subject: [PATCH 10/26] Docs for possible types --- .../webdriver/UnhandledPromptBehaviorOption.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index fa4ebc318a08b..451b826b85d93 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -23,10 +23,19 @@ namespace OpenQA.Selenium; /// Represents a configuration option that determines how unhandled prompts are managed during automated browser /// interactions. /// -/// Use this type to specify whether a single unhandled prompt behavior or multiple behaviors should be -/// applied. The static methods provide convenient ways to create either a single-behavior or multi-behavior option. +/// +/// Use this type to specify whether a single unhandled prompt behavior or multiple behaviors should be applied. +/// The static methods provide convenient ways to create either a single-behavior or multi-behavior option. /// This abstraction is typically used in scenarios where browser automation frameworks need to control the handling of -/// unexpected dialogs or prompts. +/// unexpected dialogs or prompts. +/// +/// Derived types: +/// +/// - Wraps a single value applied to all prompt types. Create via the implicit conversion from or by calling . +/// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, Default). Create via . +/// +/// +/// public abstract record UnhandledPromptBehaviorOption { /// From f5e5845d2fa1aac6c82938f0b9dfa664e86181c1 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 26 Nov 2025 23:08:36 +0300 Subject: [PATCH 11/26] Clear as options --- dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index 451b826b85d93..7e0dc35988243 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -29,7 +29,7 @@ namespace OpenQA.Selenium; /// This abstraction is typically used in scenarios where browser automation frameworks need to control the handling of /// unexpected dialogs or prompts. /// -/// Derived types: +/// Available options: /// /// - Wraps a single value applied to all prompt types. Create via the implicit conversion from or by calling . /// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, Default). Create via . From baf2939b0c972cd22cdcdba413208fe780f863ff Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:48:39 +0300 Subject: [PATCH 12/26] Fix static factory --- .../src/webdriver/UnhandledPromptBehaviorOption.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index 7e0dc35988243..11ae1cde78188 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -46,18 +46,18 @@ public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBeh => Single(value); /// - /// Creates an representing a single value. + /// Creates an representing a single value. /// /// The to apply for all prompt types. - /// An wrapping the provided behavior. - public static UnhandledPromptBehaviorOption Single(UnhandledPromptBehavior value) + /// An wrapping the provided behavior. + public static UnhandledPromptBehaviorSingleOption Single(UnhandledPromptBehavior value) => new UnhandledPromptBehaviorSingleOption(value); /// - /// Creates an allowing individual values per prompt type. + /// Creates an allowing individual values per prompt type. /// - /// An with per-prompt configurable behaviors. - public static UnhandledPromptBehaviorOption Multi() + /// An with per-prompt configurable behaviors. + public static UnhandledPromptBehaviorMultiOption Multi() => new UnhandledPromptBehaviorMultiOption(); } From bfd55e9d2d116efa9c48d73f6a954b4123f83e9c Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:59:36 +0300 Subject: [PATCH 13/26] Consider the property in MergeResult --- dotnet/src/webdriver/DriverOptions.cs | 4 ++-- dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 555a78d7a3a51..2721acdfc42dd 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -126,7 +126,7 @@ protected DriverOptions() /// Gets or sets the value for describing how unexpected alerts are to be handled in the browser. /// Defaults to . /// - public UnhandledPromptBehaviorOption? UnhandledPromptBehavior { get; set; } + public UnhandledPromptBehaviorOption? UnhandledPromptBehavior { get; set; } = Selenium.UnhandledPromptBehavior.Default; /// /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. @@ -265,7 +265,7 @@ public virtual DriverOptionsMergeResult GetMergeResult(DriverOptions other) return result; } - if (this.UnhandledPromptBehavior is not null && other.UnhandledPromptBehavior is not null) + if (this.UnhandledPromptBehavior != other.UnhandledPromptBehavior) { result.IsMergeConflict = true; result.MergeConflictOptionName = "UnhandledPromptBehavior"; diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index 11ae1cde78188..e8f7b8fd405c1 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -51,14 +51,14 @@ public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBeh /// The to apply for all prompt types. /// An wrapping the provided behavior. public static UnhandledPromptBehaviorSingleOption Single(UnhandledPromptBehavior value) - => new UnhandledPromptBehaviorSingleOption(value); + => new(value); /// /// Creates an allowing individual values per prompt type. /// /// An with per-prompt configurable behaviors. public static UnhandledPromptBehaviorMultiOption Multi() - => new UnhandledPromptBehaviorMultiOption(); + => new(); } /// From c94cc5de70472886fd297a3bc32b0cf38c0d149e Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 9 Dec 2025 18:45:14 +0300 Subject: [PATCH 14/26] Refactor serialization --- dotnet/src/webdriver/DriverOptions.cs | 62 +++++-------------- .../UnhandledPromptBehaviorOption.cs | 46 ++++++++++++++ 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 2721acdfc42dd..4b2f7006b2df9 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -470,55 +470,21 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - static string UnhandledPromptBehaviorToString(UnhandledPromptBehavior behavior) => behavior switch + switch (this.UnhandledPromptBehavior) { - Selenium.UnhandledPromptBehavior.Ignore => "ignore", - Selenium.UnhandledPromptBehavior.Accept => "accept", - Selenium.UnhandledPromptBehavior.Dismiss => "dismiss", - Selenium.UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", - Selenium.UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", - _ => throw new ArgumentOutOfRangeException(nameof(behavior), $"UnhandledPromptBehavior value '{behavior}' is not recognized."), - }; - - if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorSingleOption singleOption && singleOption.Value != Selenium.UnhandledPromptBehavior.Default) - { - var stringValue = UnhandledPromptBehaviorToString(singleOption.Value); - - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); - } - else if (this.UnhandledPromptBehavior is UnhandledPromptBehaviorMultiOption multiOption) - { - Dictionary multiOptionDictionary = []; - - if (multiOption.Alert is not Selenium.UnhandledPromptBehavior.Default) - { - multiOptionDictionary["alert"] = UnhandledPromptBehaviorToString(multiOption.Alert); - } - - if (multiOption.Confirm is not Selenium.UnhandledPromptBehavior.Default) - { - multiOptionDictionary["confirm"] = UnhandledPromptBehaviorToString(multiOption.Confirm); - } - - if (multiOption.Prompt is not Selenium.UnhandledPromptBehavior.Default) - { - multiOptionDictionary["prompt"] = UnhandledPromptBehaviorToString(multiOption.Prompt); - } - - if (multiOption.BeforeUnload is not Selenium.UnhandledPromptBehavior.Default) - { - multiOptionDictionary["beforeUnload"] = UnhandledPromptBehaviorToString(multiOption.BeforeUnload); - } - - if (multiOption.Default is not Selenium.UnhandledPromptBehavior.Default) - { - multiOptionDictionary["default"] = UnhandledPromptBehaviorToString(multiOption.Default); - } - - if (multiOptionDictionary.Count != 0) - { - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, multiOptionDictionary); - } + case UnhandledPromptBehaviorSingleOption singleOption: + if (singleOption != default) + { + var stringValue = UnhandledPromptBehaviorOption.ConvertBehaviorToString(singleOption.Value); + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); + } + break; + case UnhandledPromptBehaviorMultiOption multiOption: + if (multiOption != default) + { + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, multiOption.ToCapabilities()); + } + break; } if (this.Proxy != null) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index e8f7b8fd405c1..a07f797a19c3f 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -17,6 +17,9 @@ // under the License. // +using System; +using System.Collections.Generic; + namespace OpenQA.Selenium; /// @@ -59,6 +62,17 @@ public static UnhandledPromptBehaviorSingleOption Single(UnhandledPromptBehavior /// An with per-prompt configurable behaviors. public static UnhandledPromptBehaviorMultiOption Multi() => new(); + + internal 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 ArgumentOutOfRangeException(nameof(behavior), $"UnhandledPromptBehavior value '{behavior}' is not recognized."), + }; } /// @@ -109,6 +123,38 @@ public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehavio /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. /// public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; + + internal Dictionary ToCapabilities() + { + Dictionary capabilities = []; + + if (Alert != default) + { + capabilities["alert"] = ConvertBehaviorToString(Alert); + } + + if (Confirm != default) + { + capabilities["confirm"] = ConvertBehaviorToString(Confirm); + } + + if (Prompt != default) + { + capabilities["prompt"] = ConvertBehaviorToString(Prompt); + } + + if (BeforeUnload != default) + { + capabilities["beforeUnload"] = ConvertBehaviorToString(BeforeUnload); + } + + if (Default != default) + { + capabilities["default"] = ConvertBehaviorToString(Default); + } + + return capabilities; + } } /// From 4f609270a24248789b12d9557376e86d39aa4dc5 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:11:57 +0300 Subject: [PATCH 15/26] Fully delegate to ToCapabilities --- dotnet/src/webdriver/DriverOptions.cs | 18 ++++----------- .../UnhandledPromptBehaviorOption.cs | 22 +++++++++++++++++-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 4b2f7006b2df9..84942ea73b22f 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -470,21 +470,11 @@ protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecification capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); } - switch (this.UnhandledPromptBehavior) + var unhandledPromptBehaviorCapability = this.UnhandledPromptBehavior?.ToCapabilities(); + + if (unhandledPromptBehaviorCapability != null) { - case UnhandledPromptBehaviorSingleOption singleOption: - if (singleOption != default) - { - var stringValue = UnhandledPromptBehaviorOption.ConvertBehaviorToString(singleOption.Value); - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, stringValue); - } - break; - case UnhandledPromptBehaviorMultiOption multiOption: - if (multiOption != default) - { - capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, multiOption.ToCapabilities()); - } - break; + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, unhandledPromptBehaviorCapability); } if (this.Proxy != null) diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs index a07f797a19c3f..29e0768fab934 100644 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs @@ -63,6 +63,8 @@ public static UnhandledPromptBehaviorSingleOption Single(UnhandledPromptBehavior public static UnhandledPromptBehaviorMultiOption Multi() => new(); + internal abstract object? ToCapabilities(); + internal static string ConvertBehaviorToString(UnhandledPromptBehavior behavior) => behavior switch { @@ -79,7 +81,18 @@ internal static string ConvertBehaviorToString(UnhandledPromptBehavior behavior) /// Represents an option that specifies a single unhandled prompt behavior to use when interacting with browser dialogs. /// /// The unhandled prompt behavior to apply. Specifies how unexpected browser prompts are handled during automation. -public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption; +public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption +{ + internal override object? ToCapabilities() + { + if (Value == UnhandledPromptBehavior.Default) + { + return null; + } + + return ConvertBehaviorToString(Value); + } +} /// /// Represents a set of options that specify how unhandled browser prompts are handled for different prompt types. @@ -124,8 +137,13 @@ public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehavio /// public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; - internal Dictionary ToCapabilities() + internal override object? ToCapabilities() { + if (this == new UnhandledPromptBehaviorMultiOption()) + { + return null; + } + Dictionary capabilities = []; if (Alert != default) From aeb98ae20008d29da9bb21d448710b121fb92ca8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:53:36 +0300 Subject: [PATCH 16/26] Test --- dotnet/test/common/AlertsTest.cs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/dotnet/test/common/AlertsTest.cs b/dotnet/test/common/AlertsTest.cs index f2255c1964a66..f281b28af4a72 100644 --- a/dotnet/test/common/AlertsTest.cs +++ b/dotnet/test/common/AlertsTest.cs @@ -37,6 +37,22 @@ public void ShouldBeAbleToOverrideTheWindowAlertMethod() driver.FindElement(By.Id("alert")).Click(); } + [Test] + public void ShouldBeAbleToDismissTheWindowAlert() + { + using var driver = EnvironmentManager.Instance.CreateDriverInstance(new CustomAlertDriverOptions + { + UnhandledPromptBehavior = new UnhandledPromptBehaviorMultiOption { Default = UnhandledPromptBehavior.Dismiss } + }); + + driver.Url = CreateAlertPage("cheese"); + + driver.FindElement(By.Id("alert")).Click(); + + // If we can perform any action again, we're good to go + driver.FindElement(By.Id("alert")).Click(); + } + [Test] public void ShouldAllowUsersToAcceptAnAlertManually() { @@ -559,4 +575,16 @@ private Func WindowHandleCountToBe(int count) }; } + class CustomAlertDriverOptions : DriverOptions + { + public CustomAlertDriverOptions() + { + + } + + public override ICapabilities ToCapabilities() + { + return null; + } + } } From 1e53b6b31852d310bd4869a2d265083d3e235875 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:00:50 +0300 Subject: [PATCH 17/26] UserPromptHandler - spec defined type --- dotnet/src/webdriver/DriverOptions.cs | 2 +- .../UnhandledPromptBehaviorOption.cs | 214 ------------------ dotnet/src/webdriver/UserPromptHandler.cs | 201 ++++++++++++++++ dotnet/test/webdriver/AlertsTests.cs | 2 +- 4 files changed, 203 insertions(+), 216 deletions(-) delete mode 100644 dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs create mode 100644 dotnet/src/webdriver/UserPromptHandler.cs diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index c20477873036b..39eb3b7925f1e 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -124,7 +124,7 @@ protected DriverOptions() /// Gets or sets the value for describing how unexpected alerts are to be handled in the browser. /// Defaults to . /// - public UnhandledPromptBehaviorOption? UnhandledPromptBehavior { get; set; } = Selenium.UnhandledPromptBehavior.Default; + public UserPromptHandler? UnhandledPromptBehavior { get; set; } = Selenium.UnhandledPromptBehavior.Default; /// /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. diff --git a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs b/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs deleted file mode 100644 index 29e0768fab934..0000000000000 --- a/dotnet/src/webdriver/UnhandledPromptBehaviorOption.cs +++ /dev/null @@ -1,214 +0,0 @@ -// -// 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. -// - -using System; -using System.Collections.Generic; - -namespace OpenQA.Selenium; - -/// -/// Represents a configuration option that determines how unhandled prompts are managed during automated browser -/// interactions. -/// -/// -/// Use this type to specify whether a single unhandled prompt behavior or multiple behaviors should be applied. -/// The static methods provide convenient ways to create either a single-behavior or multi-behavior option. -/// This abstraction is typically used in scenarios where browser automation frameworks need to control the handling of -/// unexpected dialogs or prompts. -/// -/// Available options: -/// -/// - Wraps a single value applied to all prompt types. Create via the implicit conversion from or by calling . -/// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, Default). Create via . -/// -/// -/// -public abstract record UnhandledPromptBehaviorOption -{ - /// - /// Converts a value of type to an instance. - /// - /// The value to convert. - public static implicit operator UnhandledPromptBehaviorOption(UnhandledPromptBehavior value) - => Single(value); - - /// - /// Creates an representing a single value. - /// - /// The to apply for all prompt types. - /// An wrapping the provided behavior. - public static UnhandledPromptBehaviorSingleOption Single(UnhandledPromptBehavior value) - => new(value); - - /// - /// Creates an allowing individual values per prompt type. - /// - /// An with per-prompt configurable behaviors. - public static UnhandledPromptBehaviorMultiOption Multi() - => new(); - - internal abstract object? ToCapabilities(); - - internal 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 ArgumentOutOfRangeException(nameof(behavior), $"UnhandledPromptBehavior value '{behavior}' is not recognized."), - }; -} - -/// -/// Represents an option that specifies a single unhandled prompt behavior to use when interacting with browser dialogs. -/// -/// The unhandled prompt behavior to apply. Specifies how unexpected browser prompts are handled during automation. -public sealed record UnhandledPromptBehaviorSingleOption(UnhandledPromptBehavior Value) : UnhandledPromptBehaviorOption -{ - internal override object? ToCapabilities() - { - if (Value == UnhandledPromptBehavior.Default) - { - return null; - } - - return ConvertBehaviorToString(Value); - } -} - -/// -/// Represents a set of options that specify how unhandled browser prompts are handled for different prompt types. -/// -/// Use this class to configure distinct behaviors for alert, confirm, prompt, and beforeunload 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. -public sealed record UnhandledPromptBehaviorMultiOption : UnhandledPromptBehaviorOption -{ - /// - /// Gets or sets the behavior to use when an unexpected alert is encountered during automation. - /// - public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; - - /// - /// Gets or sets the behavior to use when a confirmation prompt is encountered. - /// - /// Set this property to specify how the system should respond to confirmation dialogs, such as - /// JavaScript confirm boxes, during automated operations. The default value is , which applies the standard handling defined by the - /// environment. - public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; - - /// - /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation. - /// - /// Set this property to control how the system responds to unhandled prompts, such as alerts or - /// confirmation dialogs, that appear unexpectedly. The default behavior is determined by the value of - /// . - public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; - - /// - /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered. - /// - /// Use this property to specify how the application should respond to beforeunload dialogs that - /// appear unexpectedly during automated browser interactions. This setting determines whether such dialogs are - /// automatically accepted, dismissed, or cause an error. - public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; - - /// - /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. - /// - public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; - - internal override object? ToCapabilities() - { - if (this == new UnhandledPromptBehaviorMultiOption()) - { - return null; - } - - Dictionary capabilities = []; - - if (Alert != default) - { - capabilities["alert"] = ConvertBehaviorToString(Alert); - } - - if (Confirm != default) - { - capabilities["confirm"] = ConvertBehaviorToString(Confirm); - } - - if (Prompt != default) - { - capabilities["prompt"] = ConvertBehaviorToString(Prompt); - } - - if (BeforeUnload != default) - { - capabilities["beforeUnload"] = ConvertBehaviorToString(BeforeUnload); - } - - if (Default != default) - { - capabilities["default"] = ConvertBehaviorToString(Default); - } - - return capabilities; - } -} - -/// -/// Specifies the behavior of handling unexpected alerts in the IE driver. -/// -public enum UnhandledPromptBehavior -{ - /// - /// Indicates the behavior is not set. - /// - Default, - - /// - /// Ignore unexpected alerts, such that the user must handle them. - /// - Ignore, - - /// - /// Accept unexpected alerts. - /// - Accept, - - /// - /// Dismiss unexpected alerts. - /// - Dismiss, - - /// - /// Accepts unexpected alerts and notifies the user that the alert has - /// been accepted by throwing an - /// - AcceptAndNotify, - - /// - /// Dismisses unexpected alerts and notifies the user that the alert has - /// been dismissed by throwing an - /// - DismissAndNotify -} diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs new file mode 100644 index 0000000000000..7a981220b43a3 --- /dev/null +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -0,0 +1,201 @@ +// +// 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. +// + +using System; +using System.Collections.Generic; + +namespace OpenQA.Selenium; + +/// +/// Represents a WebDriver session's user prompt handler, which defines how unhandled browser prompts +/// (alerts, confirms, prompts, beforeunload dialogs) are managed during automation. +/// +/// +/// This corresponds to the W3C WebDriver unhandledPromptBehavior capability, which may be expressed +/// either as a single string applied to all prompt types, or as a per-prompt-type map. +/// +/// Available variants: +/// +/// - Wraps a single value applied to all prompt types. Create via the implicit conversion from or by constructing directly. +/// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, Default). +/// +/// +/// +public abstract record UserPromptHandler +{ + private UserPromptHandler() { } + + /// + /// Converts a value of type to a instance. + /// + /// The value to convert. + public static implicit operator UserPromptHandler(UnhandledPromptBehavior value) + => new Uniform(value); + + 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."), + }; + + /// + /// Represents a user prompt handler that applies a single value + /// as the fallback default for all prompt types. + /// + /// The unhandled prompt behavior to apply. Specifies how unexpected browser prompts are handled during automation. + public sealed record Uniform(UnhandledPromptBehavior Value) : UserPromptHandler + { + internal override object? ToCapabilities() + { + if (Value == UnhandledPromptBehavior.Default) + { + return null; + } + + return ConvertBehaviorToString(Value); + } + } + + /// + /// Represents a user prompt handler that specifies distinct values + /// for individual prompt types (alert, confirm, prompt, beforeunload), with a fallback default. + /// + /// Use this variant to configure distinct behaviors for alert, confirm, prompt, and beforeunload 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. + public sealed record PerPromptType : UserPromptHandler + { + /// + /// Gets or sets the behavior to use when an unexpected alert is encountered during automation. + /// + public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; + + /// + /// Gets or sets the behavior to use when a confirmation prompt is encountered. + /// + /// Set this property to specify how the system should respond to confirmation dialogs, such as + /// JavaScript confirm boxes, during automated operations. The default value is , which applies the standard handling defined by the + /// environment. + public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; + + /// + /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation. + /// + /// Set this property to control how the system responds to unhandled prompts, such as alerts or + /// confirmation dialogs, that appear unexpectedly. The default behavior is determined by the value of + /// . + public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; + + /// + /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered. + /// + /// Use this property to specify how the application should respond to beforeunload dialogs that + /// appear unexpectedly during automated browser interactions. This setting determines whether such dialogs are + /// automatically accepted, dismissed, or cause an error. + public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; + + /// + /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. + /// + public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; + + internal override object? ToCapabilities() + { + if (this == new PerPromptType()) + { + return null; + } + + Dictionary capabilities = []; + + if (Alert != default) + { + capabilities["alert"] = ConvertBehaviorToString(Alert); + } + + if (Confirm != default) + { + capabilities["confirm"] = ConvertBehaviorToString(Confirm); + } + + if (Prompt != default) + { + capabilities["prompt"] = ConvertBehaviorToString(Prompt); + } + + if (BeforeUnload != default) + { + capabilities["beforeUnload"] = ConvertBehaviorToString(BeforeUnload); + } + + if (Default != default) + { + capabilities["default"] = ConvertBehaviorToString(Default); + } + + return capabilities; + } + } +} + +/// +/// Specifies the behavior of handling unexpected alerts in the IE driver. +/// +public enum UnhandledPromptBehavior +{ + /// + /// Indicates the behavior is not set. + /// + Default, + + /// + /// Ignore unexpected alerts, such that the user must handle them. + /// + Ignore, + + /// + /// Accept unexpected alerts. + /// + Accept, + + /// + /// Dismiss unexpected alerts. + /// + Dismiss, + + /// + /// Accepts unexpected alerts and notifies the user that the alert has + /// been accepted by throwing an + /// + AcceptAndNotify, + + /// + /// Dismisses unexpected alerts and notifies the user that the alert has + /// been dismissed by throwing an + /// + DismissAndNotify +} diff --git a/dotnet/test/webdriver/AlertsTests.cs b/dotnet/test/webdriver/AlertsTests.cs index 3d756b126b666..03adf54bb1237 100644 --- a/dotnet/test/webdriver/AlertsTests.cs +++ b/dotnet/test/webdriver/AlertsTests.cs @@ -39,7 +39,7 @@ public void ShouldBeAbleToDismissTheWindowAlert() { using var driver = EnvironmentManager.Instance.CreateDriverInstance(new CustomAlertDriverOptions { - UnhandledPromptBehavior = new UnhandledPromptBehaviorMultiOption { Default = UnhandledPromptBehavior.Dismiss } + UnhandledPromptBehavior = new UserPromptHandler.PerPromptType { Default = UnhandledPromptBehavior.Dismiss } }); driver.Url = CreateAlertPage("cheese"); From 3225204d9deef2d419df1b272fcf73865668532b Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:02:26 +0300 Subject: [PATCH 18/26] Support File handler --- dotnet/src/webdriver/UserPromptHandler.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index 7a981220b43a3..21ad794617638 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -118,6 +118,12 @@ public sealed record PerPromptType : UserPromptHandler /// automatically accepted, dismissed, or cause an error. public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; + /// + /// Gets or sets the behavior to use when an unexpected file selection dialog is encountered. + /// + /// The "file" prompt type is respected only in WebDriver BiDi sessions. + public UnhandledPromptBehavior File { get; set; } = UnhandledPromptBehavior.Default; + /// /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. /// @@ -152,6 +158,11 @@ public sealed record PerPromptType : UserPromptHandler capabilities["beforeUnload"] = ConvertBehaviorToString(BeforeUnload); } + if (File != default) + { + capabilities["file"] = ConvertBehaviorToString(File); + } + if (Default != default) { capabilities["default"] = ConvertBehaviorToString(Default); From b984c9fd13bf854525cae3a2034f12a5d2947500 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:02:55 +0300 Subject: [PATCH 19/26] Clean --- dotnet/src/webdriver/UserPromptHandler.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index 21ad794617638..f914367fd24c4 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -17,9 +17,6 @@ // under the License. // -using System; -using System.Collections.Generic; - namespace OpenQA.Selenium; /// From 832d645e8127494870f80b91ea67c82fe4034bdb Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:28:59 +0300 Subject: [PATCH 20/26] Nullable instead of default, obsoleting Default --- dotnet/src/webdriver/DriverOptions.cs | 4 +- dotnet/src/webdriver/UserPromptHandler.cs | 101 ++++++++---------- .../webdriver/UnexpectedAlertBehaviorTests.cs | 11 +- 3 files changed, 49 insertions(+), 67 deletions(-) diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index 39eb3b7925f1e..2de9dd2cbb4c9 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -122,9 +122,9 @@ protected DriverOptions() /// /// Gets or sets the value for describing how unexpected alerts are to be handled in the browser. - /// Defaults to . + /// Defaults to , leaving the behavior unset. /// - public UserPromptHandler? UnhandledPromptBehavior { get; set; } = Selenium.UnhandledPromptBehavior.Default; + public UserPromptHandler? UnhandledPromptBehavior { get; set; } /// /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index f914367fd24c4..b0bd801fb3ae2 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -39,11 +39,12 @@ public abstract record UserPromptHandler private UserPromptHandler() { } /// - /// Converts a value of type to a instance. + /// Converts a nullable to a instance, + /// or when is . /// /// The value to convert. - public static implicit operator UserPromptHandler(UnhandledPromptBehavior value) - => new Uniform(value); + public static implicit operator UserPromptHandler?(UnhandledPromptBehavior? value) + => value is { } v ? new Uniform(v) : null; internal abstract object? ToCapabilities(); @@ -55,6 +56,9 @@ private static string ConvertBehaviorToString(UnhandledPromptBehavior behavior) UnhandledPromptBehavior.Dismiss => "dismiss", UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", +#pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete + UnhandledPromptBehavior.Default => throw new InvalidOperationException("UnhandledPromptBehavior.Default has no wire representation; pass null instead."), +#pragma warning restore CS0618 _ => throw new InvalidOperationException($"UnhandledPromptBehavior value '{behavior}' is not recognized."), }; @@ -67,10 +71,12 @@ 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); } @@ -86,86 +92,62 @@ public sealed record Uniform(UnhandledPromptBehavior Value) : UserPromptHandler public sealed record PerPromptType : UserPromptHandler { /// - /// Gets or sets the behavior to use when an unexpected alert is encountered during automation. + /// Gets or sets the behavior to use when an unexpected alert is encountered during automation, + /// or to leave it unset. /// - public UnhandledPromptBehavior Alert { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? Alert { get; set; } /// - /// Gets or sets the behavior to use when a confirmation prompt is encountered. + /// Gets or sets the behavior to use when a confirmation prompt is encountered, + /// or to leave it unset. /// - /// Set this property to specify how the system should respond to confirmation dialogs, such as - /// JavaScript confirm boxes, during automated operations. The default value is , which applies the standard handling defined by the - /// environment. - public UnhandledPromptBehavior Confirm { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? Confirm { get; set; } /// - /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation. + /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation, + /// or to leave it unset. /// - /// Set this property to control how the system responds to unhandled prompts, such as alerts or - /// confirmation dialogs, that appear unexpectedly. The default behavior is determined by the value of - /// . - public UnhandledPromptBehavior Prompt { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? Prompt { get; set; } /// - /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered. + /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered, + /// or to leave it unset. /// - /// Use this property to specify how the application should respond to beforeunload dialogs that - /// appear unexpectedly during automated browser interactions. This setting determines whether such dialogs are - /// automatically accepted, dismissed, or cause an error. - public UnhandledPromptBehavior BeforeUnload { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? BeforeUnload { get; set; } /// - /// Gets or sets the behavior to use when an unexpected file selection dialog is encountered. + /// Gets or sets the behavior to use when an unexpected file selection dialog is encountered, + /// or to leave it unset. /// /// The "file" prompt type is respected only in WebDriver BiDi sessions. - public UnhandledPromptBehavior File { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? File { get; set; } /// - /// Gets or sets the default behavior to use when an unexpected browser prompt is encountered. + /// Gets or sets the fallback behavior to use when no specific handler is defined for a given prompt type, + /// or to leave it unset. /// - public UnhandledPromptBehavior Default { get; set; } = UnhandledPromptBehavior.Default; + public UnhandledPromptBehavior? Default { get; set; } internal override object? ToCapabilities() { - if (this == new PerPromptType()) - { - return null; - } - Dictionary 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.Count == 0 ? null : capabilities; + } - if (Alert != default) - { - capabilities["alert"] = ConvertBehaviorToString(Alert); - } - - if (Confirm != default) - { - capabilities["confirm"] = ConvertBehaviorToString(Confirm); - } - - if (Prompt != default) - { - capabilities["prompt"] = ConvertBehaviorToString(Prompt); - } - - if (BeforeUnload != default) - { - capabilities["beforeUnload"] = ConvertBehaviorToString(BeforeUnload); - } - - if (File != default) - { - capabilities["file"] = ConvertBehaviorToString(File); - } - - if (Default != default) + private static void AddIfSet(Dictionary capabilities, string key, UnhandledPromptBehavior? value) + { +#pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete + if (value is { } v && v != UnhandledPromptBehavior.Default) +#pragma warning restore CS0618 { - capabilities["default"] = ConvertBehaviorToString(Default); + capabilities[key] = ConvertBehaviorToString(v); } - - return capabilities; } } } @@ -178,6 +160,7 @@ public enum UnhandledPromptBehavior /// /// Indicates the behavior is not set. /// + [Obsolete("Use a nullable UnhandledPromptBehavior? and pass null to leave the behavior unset. This member will be removed in v4.46.")] Default, /// diff --git a/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs b/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs index f93e1f54a113a..b572f8ab254d3 100644 --- a/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs +++ b/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs @@ -71,7 +71,7 @@ public void CanSilentlyDismissUnhandledAlert() [Test] public void CanDismissUnhandledAlertsByDefault() { - ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior.Default, "null"); + ExecuteTestWithUnhandledPrompt(null, "null"); } [Test] @@ -82,14 +82,13 @@ public void CanIgnoreUnhandledAlert() localDriver.SwitchTo().Alert().Dismiss(); } - private void ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior behavior, string expectedAlertText) + private void ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior? 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, + }; localDriver = EnvironmentManager.Instance.CreateDriverInstance(options); localDriver.Url = alertsPage; From 8186fb76f42fb237efb800a67c264477d95455a8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:37:17 +0300 Subject: [PATCH 21/26] Init --- dotnet/src/webdriver/UserPromptHandler.cs | 31 ++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index b0bd801fb3ae2..37383bc638852 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -56,9 +56,6 @@ private static string ConvertBehaviorToString(UnhandledPromptBehavior behavior) UnhandledPromptBehavior.Dismiss => "dismiss", UnhandledPromptBehavior.AcceptAndNotify => "accept and notify", UnhandledPromptBehavior.DismissAndNotify => "dismiss and notify", -#pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete - UnhandledPromptBehavior.Default => throw new InvalidOperationException("UnhandledPromptBehavior.Default has no wire representation; pass null instead."), -#pragma warning restore CS0618 _ => throw new InvalidOperationException($"UnhandledPromptBehavior value '{behavior}' is not recognized."), }; @@ -92,41 +89,41 @@ public sealed record Uniform(UnhandledPromptBehavior Value) : UserPromptHandler public sealed record PerPromptType : UserPromptHandler { /// - /// Gets or sets the behavior to use when an unexpected alert is encountered during automation, + /// Gets the behavior to use when an unexpected alert is encountered during automation, /// or to leave it unset. /// - public UnhandledPromptBehavior? Alert { get; set; } + public UnhandledPromptBehavior? Alert { get; init; } /// - /// Gets or sets the behavior to use when a confirmation prompt is encountered, + /// Gets the behavior to use when a confirmation prompt is encountered, /// or to leave it unset. /// - public UnhandledPromptBehavior? Confirm { get; set; } + public UnhandledPromptBehavior? Confirm { get; init; } /// - /// Gets or sets the behavior to use when an unexpected prompt is encountered during automation, + /// Gets the behavior to use when an unexpected prompt is encountered during automation, /// or to leave it unset. /// - public UnhandledPromptBehavior? Prompt { get; set; } + public UnhandledPromptBehavior? Prompt { get; init; } /// - /// Gets or sets the behavior to use when an unexpected beforeunload dialog is encountered, + /// Gets the behavior to use when an unexpected beforeunload dialog is encountered, /// or to leave it unset. /// - public UnhandledPromptBehavior? BeforeUnload { get; set; } + public UnhandledPromptBehavior? BeforeUnload { get; init; } /// - /// Gets or sets the behavior to use when an unexpected file selection dialog is encountered, + /// Gets the behavior to use when an unexpected file selection dialog is encountered, /// or to leave it unset. /// /// The "file" prompt type is respected only in WebDriver BiDi sessions. - public UnhandledPromptBehavior? File { get; set; } + public UnhandledPromptBehavior? File { get; init; } /// - /// Gets or sets the fallback behavior to use when no specific handler is defined for a given prompt type, + /// Gets the fallback behavior to use when no specific handler is defined for a given prompt type, /// or to leave it unset. /// - public UnhandledPromptBehavior? Default { get; set; } + public UnhandledPromptBehavior? Default { get; init; } internal override object? ToCapabilities() { @@ -144,10 +141,10 @@ private static void AddIfSet(Dictionary capabilities, string key { #pragma warning disable CS0618 // UnhandledPromptBehavior.Default is obsolete if (value is { } v && v != UnhandledPromptBehavior.Default) -#pragma warning restore CS0618 { capabilities[key] = ConvertBehaviorToString(v); } +#pragma warning restore CS0618 } } } @@ -158,7 +155,7 @@ private static void AddIfSet(Dictionary capabilities, string key public enum UnhandledPromptBehavior { /// - /// Indicates the behavior is not set. + /// Sentinel value meaning "behavior not set". Not part of the W3C WebDriver spec. /// [Obsolete("Use a nullable UnhandledPromptBehavior? and pass null to leave the behavior unset. This member will be removed in v4.46.")] Default, From 9894ffcf0d984ff3c31191f43b71a8c6d9a18a4d Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:51:28 +0300 Subject: [PATCH 22/26] Docs UnhandledPromptBehavior --- dotnet/src/webdriver/UserPromptHandler.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index 37383bc638852..3bb5db141d4be 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -150,8 +150,14 @@ private static void AddIfSet(Dictionary capabilities, string key } /// -/// Specifies the behavior of handling unexpected alerts in the IE driver. +/// Specifies how a WebDriver session handles an unhandled user prompt. /// +/// +/// Corresponds to the handler values defined for the W3C WebDriver +/// unhandledPromptBehavior capability: +/// dismiss, accept, dismiss and notify, accept and notify, and ignore. +/// When no handler is configured, the spec's implicit default is . +/// public enum UnhandledPromptBehavior { /// From 0f1836f294d74d22442181cc36b49a20b7fd3b22 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:53:06 +0300 Subject: [PATCH 23/26] Update UserPromptHandler.cs --- dotnet/src/webdriver/UserPromptHandler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index 3bb5db141d4be..4fadc2bb9fa38 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -21,7 +21,7 @@ namespace OpenQA.Selenium; /// /// Represents a WebDriver session's user prompt handler, which defines how unhandled browser prompts -/// (alerts, confirms, prompts, beforeunload dialogs) are managed during automation. +/// (alerts, confirms, prompts, beforeunload dialogs, file selection dialogs) are managed during automation. /// /// /// This corresponds to the W3C WebDriver unhandledPromptBehavior capability, which may be expressed @@ -30,7 +30,7 @@ namespace OpenQA.Selenium; /// Available variants: /// /// - Wraps a single value applied to all prompt types. Create via the implicit conversion from or by constructing directly. -/// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, Default). +/// - Allows configuring per-prompt behaviors (Alert, Confirm, Prompt, BeforeUnload, File, Default). /// /// /// @@ -81,11 +81,11 @@ public sealed record Uniform(UnhandledPromptBehavior Value) : UserPromptHandler /// /// Represents a user prompt handler that specifies distinct values - /// for individual prompt types (alert, confirm, prompt, beforeunload), with a fallback default. + /// for individual prompt types (alert, confirm, prompt, beforeunload, file), with a fallback default. /// - /// Use this variant to configure distinct behaviors for alert, confirm, prompt, and beforeunload 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. + /// 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. public sealed record PerPromptType : UserPromptHandler { /// From f15b022e54c8b03e282213b7111bfe1f0efea00f Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:38:54 +0300 Subject: [PATCH 24/26] Thows.Nothing in assertion --- dotnet/test/webdriver/AlertsTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dotnet/test/webdriver/AlertsTests.cs b/dotnet/test/webdriver/AlertsTests.cs index 03adf54bb1237..681d976a07e11 100644 --- a/dotnet/test/webdriver/AlertsTests.cs +++ b/dotnet/test/webdriver/AlertsTests.cs @@ -46,8 +46,9 @@ public void ShouldBeAbleToDismissTheWindowAlert() driver.FindElement(By.Id("alert")).Click(); - // If we can perform any action again, we're good to go - driver.FindElement(By.Id("alert")).Click(); + Assert.That( + () => driver.FindElement(By.Id("alert")).Click(), + Throws.Nothing, "Unexpected alert was not dismissed as expected"); } [Test] From b46633ccdb9d09c8f7beb16fc016950786608c40 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:42:55 +0300 Subject: [PATCH 25/26] Move test to proper fixture --- dotnet/test/webdriver/AlertsTests.cs | 30 ------------ .../webdriver/UnexpectedAlertBehaviorTests.cs | 46 ++++++++----------- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/dotnet/test/webdriver/AlertsTests.cs b/dotnet/test/webdriver/AlertsTests.cs index 681d976a07e11..49486304d0c5d 100644 --- a/dotnet/test/webdriver/AlertsTests.cs +++ b/dotnet/test/webdriver/AlertsTests.cs @@ -34,23 +34,6 @@ public void ShouldBeAbleToOverrideTheWindowAlertMethod() driver.FindElement(By.Id("alert")).Click(); } - [Test] - public void ShouldBeAbleToDismissTheWindowAlert() - { - using var driver = EnvironmentManager.Instance.CreateDriverInstance(new CustomAlertDriverOptions - { - UnhandledPromptBehavior = new UserPromptHandler.PerPromptType { Default = UnhandledPromptBehavior.Dismiss } - }); - - driver.Url = CreateAlertPage("cheese"); - - driver.FindElement(By.Id("alert")).Click(); - - Assert.That( - () => driver.FindElement(By.Id("alert")).Click(), - Throws.Nothing, "Unexpected alert was not dismissed as expected"); - } - [Test] public void ShouldAllowUsersToAcceptAnAlertManually() { @@ -572,17 +555,4 @@ private Func WindowHandleCountToBe(int count) return driver.WindowHandles.Count == count; }; } - - class CustomAlertDriverOptions : DriverOptions - { - public CustomAlertDriverOptions() - { - - } - - public override ICapabilities ToCapabilities() - { - return null; - } - } } diff --git a/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs b/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs index b572f8ab254d3..45e875c1a4129 100644 --- a/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs +++ b/dotnet/test/webdriver/UnexpectedAlertBehaviorTests.cs @@ -74,17 +74,33 @@ public void CanDismissUnhandledAlertsByDefault() 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().With.InnerException.InstanceOf()); + Assert.That( + () => ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior.Ignore, "Text ignored"), + Throws.InstanceOf().With.InnerException.InstanceOf()); 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() { UnhandledPromptBehavior = behavior, @@ -95,31 +111,9 @@ private void ExecuteTestWithUnhandledPrompt(UnhandledPromptBehavior? behavior, s 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"); } - private Func 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 { From aa0ccefcba449db64e911f2690a253e96941d736 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:50:16 +0300 Subject: [PATCH 26/26] Serialize empty --- dotnet/src/webdriver/UserPromptHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/UserPromptHandler.cs b/dotnet/src/webdriver/UserPromptHandler.cs index 4fadc2bb9fa38..e226c79aab1fa 100644 --- a/dotnet/src/webdriver/UserPromptHandler.cs +++ b/dotnet/src/webdriver/UserPromptHandler.cs @@ -134,7 +134,7 @@ public sealed record PerPromptType : UserPromptHandler AddIfSet(capabilities, "beforeUnload", BeforeUnload); AddIfSet(capabilities, "file", File); AddIfSet(capabilities, "default", Default); - return capabilities.Count == 0 ? null : capabilities; + return capabilities; } private static void AddIfSet(Dictionary capabilities, string key, UnhandledPromptBehavior? value)