From 866f81cad25fd898ae0b652753a6a1a311804587 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:50:30 +0300 Subject: [PATCH] [dotnet] Use namespace file scoped --- dotnet/.editorconfig | 2 + .../support/Events/EventFiringWebDriver.cs | 2937 ++++++++--------- .../support/Events/FindElementEventArgs.cs | 77 +- .../support/Events/GetShadowRootEventArgs.cs | 45 +- .../Events/WebDriverExceptionEventArgs.cs | 45 +- .../Events/WebDriverNavigationEventArgs.cs | 63 +- .../Events/WebDriverScriptEventArgs.cs | 45 +- .../src/support/Events/WebElementEventArgs.cs | 45 +- .../Events/WebElementValueEventArgs.cs | 39 +- .../support/Extensions/WebDriverExtensions.cs | 189 +- dotnet/src/support/UI/ILoadableComponent.cs | 47 +- .../support/UI/LoadableComponentException.cs | 63 +- dotnet/src/support/UI/LoadableComponent{T}.cs | 193 +- dotnet/src/support/UI/PopupWindowFinder.cs | 203 +- dotnet/src/support/UI/SelectElement.cs | 643 ++-- .../support/UI/SlowLoadableComponent{T}.cs | 185 +- .../support/UI/UnexpectedTagNameException.cs | 83 +- dotnet/src/webdriver/Alert.cs | 97 +- dotnet/src/webdriver/By.cs | 635 ++-- dotnet/src/webdriver/CapabilityType.cs | 353 +- dotnet/src/webdriver/Chrome/ChromeDriver.cs | 293 +- .../webdriver/Chrome/ChromeDriverService.cs | 111 +- dotnet/src/webdriver/Chrome/ChromeOptions.cs | 125 +- .../Chromium/ChromiumAndroidOptions.cs | 37 +- .../src/webdriver/Chromium/ChromiumDriver.cs | 795 +++-- .../Chromium/ChromiumDriverService.cs | 315 +- .../ChromiumMobileEmulationDeviceSettings.cs | 89 +- .../Chromium/ChromiumNetworkConditions.cs | 173 +- .../src/webdriver/Chromium/ChromiumOptions.cs | 1063 +++--- .../ChromiumPerformanceLoggingPreferences.cs | 141 +- dotnet/src/webdriver/Command.cs | 255 +- dotnet/src/webdriver/CommandInfo.cs | 143 +- dotnet/src/webdriver/CommandInfoRepository.cs | 205 +- dotnet/src/webdriver/Cookie.cs | 587 ++-- dotnet/src/webdriver/CookieJar.cs | 167 +- dotnet/src/webdriver/DefaultFileDetector.cs | 27 +- .../webdriver/DetachedShadowRootException.cs | 63 +- .../DevTools/AuthRequiredEventArgs.cs | 43 +- .../DevTools/BindingCalledEventArgs.cs | 55 +- .../DevTools/CommandResponseException.cs | 65 +- .../DevTools/CommandResponseExtensions.cs | 27 +- .../DevTools/CommandResponseTypeMap.cs | 85 +- .../webdriver/DevTools/ConsoleApiArgument.cs | 45 +- .../DevTools/ConsoleApiCalledEventArgs.cs | 57 +- .../webdriver/DevTools/DevToolsCommandData.cs | 133 +- .../src/webdriver/DevTools/DevToolsDomains.cs | 193 +- .../webdriver/DevTools/DevToolsEventData.cs | 45 +- .../DevTools/DevToolsExtensionMethods.cs | 121 +- .../src/webdriver/DevTools/DevToolsOptions.cs | 29 +- .../src/webdriver/DevTools/DevToolsSession.cs | 1015 +++--- .../DevTools/DevToolsSessionDomains.cs | 37 +- .../DevToolsSessionEventReceivedEventArgs.cs | 55 +- .../DevToolsSessionLogMessageEventArgs.cs | 69 +- .../webdriver/DevTools/DevToolsVersionInfo.cs | 193 +- .../webdriver/DevTools/EntryAddedEventArgs.cs | 33 +- .../DevTools/ExceptionThrownEventArgs.cs | 43 +- dotnet/src/webdriver/DevTools/ICommand.cs | 39 +- dotnet/src/webdriver/DevTools/IDevTools.cs | 79 +- .../webdriver/DevTools/IDevToolsSession.cs | 133 +- dotnet/src/webdriver/DevTools/JavaScript.cs | 215 +- .../DevTools/Json/JsonEnumMemberConverter.cs | 77 +- dotnet/src/webdriver/DevTools/Log.cs | 63 +- dotnet/src/webdriver/DevTools/LogEntry.cs | 43 +- dotnet/src/webdriver/DevTools/Network.cs | 347 +- .../DevTools/RequestPausedEventArgs.cs | 43 +- .../DevTools/ResponsePausedEventArgs.cs | 33 +- dotnet/src/webdriver/DevTools/Target.cs | 127 +- .../DevTools/TargetAttachedEventArgs.cs | 55 +- .../DevTools/TargetDetachedEventArgs.cs | 43 +- dotnet/src/webdriver/DevTools/TargetInfo.cs | 103 +- .../webdriver/DevTools/WebSocketConnection.cs | 447 ++- ...ebSocketConnectionDataReceivedEventArgs.cs | 33 +- .../webdriver/DevTools/v133/V133Domains.cs | 77 +- .../webdriver/DevTools/v133/V133JavaScript.cs | 279 +- dotnet/src/webdriver/DevTools/v133/V133Log.cs | 93 +- .../webdriver/DevTools/v133/V133Network.cs | 555 ++-- .../src/webdriver/DevTools/v133/V133Target.cs | 235 +- .../webdriver/DevTools/v134/V134Domains.cs | 77 +- .../webdriver/DevTools/v134/V134JavaScript.cs | 279 +- dotnet/src/webdriver/DevTools/v134/V134Log.cs | 93 +- .../webdriver/DevTools/v134/V134Network.cs | 555 ++-- .../src/webdriver/DevTools/v134/V134Target.cs | 235 +- .../webdriver/DevTools/v135/V135Domains.cs | 77 +- .../webdriver/DevTools/v135/V135JavaScript.cs | 279 +- dotnet/src/webdriver/DevTools/v135/V135Log.cs | 93 +- .../webdriver/DevTools/v135/V135Network.cs | 555 ++-- .../src/webdriver/DevTools/v135/V135Target.cs | 235 +- dotnet/src/webdriver/DomMutatedEventArgs.cs | 27 +- dotnet/src/webdriver/DomMutationData.cs | 81 +- dotnet/src/webdriver/DriverCommand.cs | 933 +++--- dotnet/src/webdriver/DriverFinder.cs | 229 +- dotnet/src/webdriver/DriverOptions.cs | 901 +++-- .../src/webdriver/DriverOptionsMergeResult.cs | 25 +- .../DriverProcessStartedEventArgs.cs | 73 +- .../DriverProcessStartingEventArgs.cs | 37 +- dotnet/src/webdriver/DriverService.cs | 569 ++-- .../DriverServiceNotFoundException.cs | 63 +- dotnet/src/webdriver/Edge/EdgeDriver.cs | 233 +- .../src/webdriver/Edge/EdgeDriverService.cs | 129 +- dotnet/src/webdriver/Edge/EdgeOptions.cs | 133 +- .../ElementClickInterceptedException.cs | 63 +- dotnet/src/webdriver/ElementCoordinates.cs | 73 +- .../ElementNotInteractableException.cs | 63 +- dotnet/src/webdriver/EncodedFile.cs | 75 +- dotnet/src/webdriver/ErrorResponse.cs | 123 +- .../Firefox/FirefoxAndroidOptions.cs | 65 +- .../Firefox/FirefoxCommandContext.cs | 29 +- dotnet/src/webdriver/Firefox/FirefoxDriver.cs | 691 ++-- .../Firefox/FirefoxDriverLogLevel.cs | 73 +- .../webdriver/Firefox/FirefoxDriverService.cs | 411 ++- .../src/webdriver/Firefox/FirefoxExtension.cs | 265 +- .../src/webdriver/Firefox/FirefoxOptions.cs | 571 ++-- .../src/webdriver/Firefox/FirefoxProfile.cs | 503 ++- .../Firefox/FirefoxProfileManager.cs | 121 +- .../Firefox/Internal/IniFileReader.cs | 173 +- dotnet/src/webdriver/Firefox/Preferences.cs | 281 +- dotnet/src/webdriver/HttpCommandInfo.cs | 183 +- dotnet/src/webdriver/HttpRequestData.cs | 67 +- dotnet/src/webdriver/HttpResponseContent.cs | 73 +- dotnet/src/webdriver/HttpResponseData.cs | 101 +- dotnet/src/webdriver/IActionExecutor.cs | 39 +- dotnet/src/webdriver/IAlert.cs | 45 +- dotnet/src/webdriver/IAllowsFileDetection.cs | 23 +- dotnet/src/webdriver/ICapabilities.cs | 53 +- dotnet/src/webdriver/ICommandExecutor.cs | 51 +- dotnet/src/webdriver/ICookieJar.cs | 77 +- dotnet/src/webdriver/ICredentials.cs | 15 +- .../webdriver/ICustomDriverCommandExecutor.cs | 49 +- .../webdriver/IE/InternetExplorerDriver.cs | 325 +- .../IE/InternetExplorerDriverLogLevel.cs | 57 +- .../IE/InternetExplorerDriverService.cs | 241 +- .../webdriver/IE/InternetExplorerOptions.cs | 639 ++-- dotnet/src/webdriver/IFileDetector.cs | 25 +- dotnet/src/webdriver/IHasCapabilities.cs | 17 +- dotnet/src/webdriver/IHasCommandExecutor.cs | 17 +- dotnet/src/webdriver/IHasDownloads.cs | 39 +- dotnet/src/webdriver/IHasSessionId.cs | 17 +- dotnet/src/webdriver/IJavaScriptEngine.cs | 277 +- dotnet/src/webdriver/IJavascriptExecutor.cs | 169 +- dotnet/src/webdriver/ILocatable.cs | 29 +- dotnet/src/webdriver/ILogs.cs | 33 +- dotnet/src/webdriver/INavigation.cs | 155 +- dotnet/src/webdriver/INetwork.cs | 113 +- dotnet/src/webdriver/IOptions.cs | 55 +- dotnet/src/webdriver/IRotatable.cs | 21 +- dotnet/src/webdriver/ISearchContext.cs | 41 +- dotnet/src/webdriver/ISupportsLogs.cs | 15 +- dotnet/src/webdriver/ISupportsPrint.cs | 23 +- dotnet/src/webdriver/ITakesScreenshot.cs | 19 +- dotnet/src/webdriver/ITargetLocator.cs | 139 +- dotnet/src/webdriver/ITimeouts.cs | 71 +- dotnet/src/webdriver/IWebDriver.cs | 177 +- dotnet/src/webdriver/IWebElement.cs | 383 ++- dotnet/src/webdriver/IWindow.cs | 53 +- dotnet/src/webdriver/IWrapsDriver.cs | 17 +- dotnet/src/webdriver/IWrapsElement.cs | 17 +- dotnet/src/webdriver/IWritableCapabilities.cs | 31 +- dotnet/src/webdriver/InitializationScript.cs | 101 +- .../webdriver/InsecureCertificateException.cs | 63 +- .../webdriver/Interactions/ActionBuilder.cs | 177 +- .../webdriver/Interactions/ActionSequence.cs | 169 +- dotnet/src/webdriver/Interactions/Actions.cs | 1081 +++--- dotnet/src/webdriver/Interactions/IAction.cs | 17 +- .../webdriver/Interactions/ICoordinates.cs | 41 +- .../src/webdriver/Interactions/InputDevice.cs | 127 +- .../webdriver/Interactions/InputDeviceKind.cs | 41 +- .../src/webdriver/Interactions/Interaction.cs | 59 +- .../webdriver/Interactions/KeyInputDevice.cs | 169 +- .../Interactions/PauseInteraction.cs | 101 +- .../Interactions/PointerInputDevice.cs | 909 +++-- .../Interactions/WheelInputDevice.cs | 323 +- .../src/webdriver/Internal/AndroidOptions.cs | 55 +- .../webdriver/Internal/Base64UrlEncoder.cs | 247 +- .../src/webdriver/Internal/FileUtilities.cs | 301 +- .../src/webdriver/Internal/IFindsElement.cs | 37 +- .../Internal/IHasCapabilitiesDictionary.cs | 17 +- .../Internal/IWebDriverObjectReference.cs | 27 +- .../Internal/Logging/ConsoleLogHandler.cs | 15 +- .../Internal/Logging/FileLogHandler.cs | 163 +- .../webdriver/Internal/Logging/ILogContext.cs | 117 +- .../webdriver/Internal/Logging/ILogHandler.cs | 19 +- .../Internal/Logging/ILogHandlerList.cs | 43 +- .../src/webdriver/Internal/Logging/ILogger.cs | 87 +- dotnet/src/webdriver/Internal/Logging/Log.cs | 175 +- .../webdriver/Internal/Logging/LogContext.cs | 195 +- .../Internal/Logging/LogContextManager.cs | 31 +- .../webdriver/Internal/Logging/LogEvent.cs | 69 +- .../Internal/Logging/LogEventLevel.cs | 57 +- .../Internal/Logging/LogHandlerList.cs | 63 +- .../src/webdriver/Internal/Logging/Logger.cs | 81 +- .../Internal/Logging/TextWriterHandler.cs | 27 +- .../webdriver/Internal/NullableAttributes.cs | 257 +- .../src/webdriver/Internal/PortUtilities.cs | 51 +- .../webdriver/Internal/ResourceUtilities.cs | 175 +- .../Internal/ResponseValueJsonConverter.cs | 183 +- .../Internal/ReturnedCapabilities.cs | 173 +- .../src/webdriver/Internal/ReturnedCookie.cs | 109 +- .../webdriver/Internal/TrimmingAttributes.cs | 757 +++-- .../webdriver/InvalidCookieDomainException.cs | 63 +- .../webdriver/InvalidElementStateException.cs | 63 +- .../src/webdriver/InvalidSelectorException.cs | 91 +- .../JavaScriptCallbackExecutedEventArgs.cs | 43 +- .../JavaScriptConsoleApiCalledEventArgs.cs | 55 +- dotnet/src/webdriver/JavaScriptEngine.cs | 689 ++-- dotnet/src/webdriver/JavaScriptException.cs | 63 +- .../JavaScriptExceptionThrownEventArgs.cs | 31 +- dotnet/src/webdriver/Keys.cs | 807 +++-- dotnet/src/webdriver/LogEntry.cs | 113 +- dotnet/src/webdriver/LogLevel.cs | 57 +- dotnet/src/webdriver/LogType.cs | 57 +- dotnet/src/webdriver/Logs.cs | 113 +- .../MoveTargetOutOfBoundsException.cs | 65 +- dotnet/src/webdriver/Navigator.cs | 233 +- .../webdriver/NetworkAuthenticationHandler.cs | 33 +- dotnet/src/webdriver/NetworkManager.cs | 389 ++- dotnet/src/webdriver/NetworkRequestHandler.cs | 45 +- .../webdriver/NetworkRequestSentEventArgs.cs | 79 +- .../src/webdriver/NetworkResponseHandler.cs | 35 +- .../NetworkResponseReceivedEventArgs.cs | 101 +- .../src/webdriver/NoAlertPresentException.cs | 63 +- dotnet/src/webdriver/NoSuchCookieException.cs | 63 +- dotnet/src/webdriver/NoSuchDriverException.cs | 93 +- .../src/webdriver/NoSuchElementException.cs | 93 +- dotnet/src/webdriver/NoSuchFrameException.cs | 63 +- .../webdriver/NoSuchShadowRootException.cs | 63 +- dotnet/src/webdriver/NoSuchWindowException.cs | 63 +- dotnet/src/webdriver/NotFoundException.cs | 63 +- dotnet/src/webdriver/OptionsManager.cs | 73 +- dotnet/src/webdriver/PasswordCredentials.cs | 57 +- dotnet/src/webdriver/PinnedScript.cs | 103 +- dotnet/src/webdriver/Platform.cs | 321 +- dotnet/src/webdriver/PrintDocument.cs | 75 +- dotnet/src/webdriver/PrintOptions.cs | 615 ++-- dotnet/src/webdriver/Proxy.cs | 825 +++-- dotnet/src/webdriver/RelativeBy.cs | 623 ++-- .../webdriver/Remote/DesiredCapabilities.cs | 415 ++- .../Remote/DriverServiceCommandExecutor.cs | 235 +- .../webdriver/Remote/HttpCommandExecutor.cs | 683 ++-- dotnet/src/webdriver/Remote/ICommandServer.cs | 17 +- .../src/webdriver/Remote/LocalFileDetector.cs | 27 +- .../Remote/ReadOnlyDesiredCapabilities.cs | 311 +- .../webdriver/Remote/RemoteSessionSettings.cs | 393 ++- .../src/webdriver/Remote/RemoteWebDriver.cs | 1017 +++--- .../SendingRemoteHttpRequestEventArgs.cs | 115 +- .../W3CWireProtocolCommandInfoRepository.cs | 211 +- dotnet/src/webdriver/Response.cs | 279 +- dotnet/src/webdriver/Safari/SafariDriver.cs | 415 ++- .../webdriver/Safari/SafariDriverService.cs | 171 +- dotnet/src/webdriver/Safari/SafariOptions.cs | 151 +- dotnet/src/webdriver/ScreenOrientation.cs | 25 +- dotnet/src/webdriver/Screenshot.cs | 53 +- dotnet/src/webdriver/SeleniumManager.cs | 371 ++- dotnet/src/webdriver/SessionId.cs | 77 +- dotnet/src/webdriver/ShadowRoot.cs | 179 +- dotnet/src/webdriver/StackTraceElement.cs | 173 +- .../StaleElementReferenceException.cs | 91 +- .../src/webdriver/Support/DefaultWait{T}.cs | 305 +- dotnet/src/webdriver/Support/IClock.cs | 41 +- dotnet/src/webdriver/Support/IWait{T}.cs | 67 +- dotnet/src/webdriver/Support/SystemClock.cs | 49 +- dotnet/src/webdriver/Support/WebDriverWait.cs | 69 +- dotnet/src/webdriver/TargetLocator.cs | 331 +- dotnet/src/webdriver/Timeouts.cs | 205 +- .../webdriver/UnableToSetCookieException.cs | 63 +- .../src/webdriver/UnhandledAlertException.cs | 95 +- dotnet/src/webdriver/UnknownErrorException.cs | 41 +- .../src/webdriver/UnknownMethodException.cs | 37 +- .../UnsupportedOperationException.cs | 39 +- dotnet/src/webdriver/UserAgent.cs | 63 +- .../src/webdriver/VirtualAuth/Credential.cs | 215 +- .../VirtualAuth/IHasVirtualAuthenticator.cs | 111 +- .../VirtualAuthenticatorOptions.cs | 259 +- dotnet/src/webdriver/WebDriver.cs | 1759 +++++----- .../webdriver/WebDriverArgumentException.cs | 63 +- dotnet/src/webdriver/WebDriverError.cs | 377 ++- dotnet/src/webdriver/WebDriverException.cs | 81 +- dotnet/src/webdriver/WebDriverResult.cs | 299 +- .../webdriver/WebDriverTimeoutException.cs | 63 +- dotnet/src/webdriver/WebElement.cs | 1161 ++++--- dotnet/src/webdriver/WebElementFactory.cs | 125 +- dotnet/src/webdriver/Window.cs | 145 +- dotnet/src/webdriver/WindowType.cs | 25 +- dotnet/src/webdriver/XPathLookupException.cs | 63 +- dotnet/test/chrome/ChromeSpecificTests.cs | 17 +- dotnet/test/common/AlertsTest.cs | 869 +++-- dotnet/test/common/AssemblyFixture.cs | 47 +- dotnet/test/common/Browser.cs | 21 +- dotnet/test/common/ChildrenFindingTest.cs | 747 +++-- dotnet/test/common/ClearTest.cs | 363 +- dotnet/test/common/ClickScrollingTest.cs | 421 ++- dotnet/test/common/ClickTest.cs | 547 ++- dotnet/test/common/CommandTests.cs | 23 +- dotnet/test/common/ContentEditableTest.cs | 219 +- .../test/common/CookieImplementationTest.cs | 1479 +++++---- dotnet/test/common/CookieTest.cs | 151 +- dotnet/test/common/CorrectEventFiringTest.cs | 765 +++-- dotnet/test/common/CssValueTest.cs | 65 +- .../DefaultSafariDriver.cs | 27 +- .../DevChannelChromeDriver.cs | 37 +- .../DevChannelEdgeDriver.cs | 37 +- .../EdgeInternetExplorerModeDriver.cs | 43 +- .../NightlyChannelFirefoxDriver.cs | 43 +- .../SafariTechnologyPreviewDriver.cs | 49 +- .../StableChannelChromeDriver.cs | 37 +- .../StableChannelEdgeDriver.cs | 39 +- .../StableChannelFirefoxDriver.cs | 43 +- .../StableChannelRemoteChromeDriver.cs | 13 +- .../IgnoreBrowserAttribute.cs | 165 +- .../IgnorePlatformAttribute.cs | 143 +- .../IgnoreTargetAttribute.cs | 91 +- .../NeedsFreshDriverAttribute.cs | 59 +- .../common/DevTools/DevToolsConsoleTest.cs | 53 +- .../test/common/DevTools/DevToolsLogTest.cs | 61 +- .../common/DevTools/DevToolsNetworkTest.cs | 777 +++-- .../DevTools/DevToolsPerformanceTest.cs | 189 +- .../common/DevTools/DevToolsProfilerTest.cs | 207 +- .../common/DevTools/DevToolsSecurityTest.cs | 89 +- .../test/common/DevTools/DevToolsTabsTest.cs | 49 +- .../common/DevTools/DevToolsTargetTest.cs | 303 +- .../common/DevTools/DevToolsTestFixture.cs | 55 +- dotnet/test/common/DownloadsTest.cs | 143 +- .../test/common/DriverElementFindingTest.cs | 241 +- dotnet/test/common/DriverTestFixture.cs | 311 +- dotnet/test/common/ElementAttributeTest.cs | 693 ++-- .../test/common/ElementElementFindingTest.cs | 297 +- dotnet/test/common/ElementEqualityTest.cs | 83 +- dotnet/test/common/ElementFindingTest.cs | 1677 +++++----- dotnet/test/common/ElementPropertyTest.cs | 43 +- dotnet/test/common/ElementSelectingTest.cs | 499 ++- .../test/common/Environment/DriverConfig.cs | 31 +- .../test/common/Environment/DriverFactory.cs | 331 +- .../Environment/DriverStartingEventArgs.cs | 25 +- .../common/Environment/EnvironmentManager.cs | 411 ++- dotnet/test/common/Environment/InlinePage.cs | 137 +- .../Environment/RemoteSeleniumServer.cs | 121 +- .../common/Environment/TestEnvironment.cs | 41 +- .../test/common/Environment/TestWebServer.cs | 191 +- .../common/Environment/TestWebServerConfig.cs | 23 +- dotnet/test/common/Environment/UrlBuilder.cs | 193 +- .../test/common/Environment/WebsiteConfig.cs | 29 +- dotnet/test/common/ErrorsTest.cs | 33 +- .../common/ExecutingAsyncJavascriptTest.cs | 591 ++-- dotnet/test/common/ExecutingJavascriptTest.cs | 1479 +++++---- dotnet/test/common/FormHandlingTests.cs | 701 ++-- dotnet/test/common/FrameSwitchingTest.cs | 981 +++--- dotnet/test/common/GetLogsTest.cs | 137 +- .../test/common/GetMultipleAttributeTest.cs | 75 +- dotnet/test/common/I18Test.cs | 121 +- dotnet/test/common/ImplicitWaitTest.cs | 215 +- .../common/Interactions/ActionBuilderTest.cs | 93 +- .../BasicKeyboardInterfaceTest.cs | 511 ++- .../Interactions/BasicMouseInterfaceTest.cs | 727 ++-- .../Interactions/BasicWheelInterfaceTest.cs | 261 +- .../Interactions/CombinedInputActionsTest.cs | 665 ++-- .../common/Interactions/DragAndDropTest.cs | 393 ++- .../Internal/Logging/FileLogHandlerTest.cs | 175 +- .../test/common/Internal/Logging/LogTest.cs | 345 +- .../common/JavascriptEnabledBrowserTest.cs | 331 +- dotnet/test/common/MiscTest.cs | 169 +- dotnet/test/common/NavigationTest.cs | 303 +- .../test/common/NetworkInterceptionTests.cs | 135 +- .../test/common/ObjectStateAssumptionsTest.cs | 77 +- dotnet/test/common/PageLoadingTest.cs | 741 +++-- .../test/common/PartialLinkTextMatchTest.cs | 113 +- dotnet/test/common/PositionAndSizeTest.cs | 339 +- dotnet/test/common/PrintTest.cs | 217 +- dotnet/test/common/ProxySettingTest.cs | 563 ++-- dotnet/test/common/ProxyTest.cs | 537 ++- dotnet/test/common/RelativeLocatorTest.cs | 361 +- .../test/common/SelectElementHandlingTest.cs | 253 +- dotnet/test/common/SessionHandlingTest.cs | 231 +- dotnet/test/common/ShadowRootHandlingTest.cs | 99 +- dotnet/test/common/SlowLoadingPageTest.cs | 79 +- .../test/common/StaleElementReferenceTest.cs | 93 +- dotnet/test/common/StubDriver.cs | 143 +- dotnet/test/common/SvgDocumentTest.cs | 55 +- dotnet/test/common/SvgElementTest.cs | 93 +- dotnet/test/common/TagNameTest.cs | 19 +- dotnet/test/common/TakesScreenshotTest.cs | 635 ++-- dotnet/test/common/TargetLocatorTest.cs | 223 +- dotnet/test/common/TestUtilities.cs | 147 +- dotnet/test/common/TextHandlingTest.cs | 801 +++-- dotnet/test/common/TextPagesTest.cs | 65 +- .../test/common/TimeoutDriverOptionsTest.cs | 177 +- dotnet/test/common/TypingTest.cs | 1305 ++++---- .../common/UnexpectedAlertBehaviorTest.cs | 173 +- dotnet/test/common/UploadTest.cs | 193 +- .../VirtualAuthn/VirtualAuthenticatorTest.cs | 881 +++-- dotnet/test/common/VisibilityTest.cs | 475 ++- dotnet/test/common/WebElementTest.cs | 289 +- dotnet/test/common/WindowSwitchingTest.cs | 699 ++-- dotnet/test/common/WindowTest.cs | 407 ++- dotnet/test/edge/AssemblyTeardown.cs | 29 +- dotnet/test/edge/EdgeSpecificTests.cs | 9 +- dotnet/test/firefox/AssemblyTeardown.cs | 29 +- dotnet/test/firefox/FirefoxDriverTest.cs | 543 ++- .../test/firefox/FirefoxProfileManagerTest.cs | 75 +- dotnet/test/firefox/FirefoxProfileTests.cs | 125 +- dotnet/test/ie/AssemblyTeardown.cs | 29 +- dotnet/test/ie/IeSpecificTests.cs | 617 ++-- dotnet/test/remote/AssemblyTeardown.cs | 37 +- dotnet/test/remote/ChromeRemoteWebDriver.cs | 15 +- dotnet/test/remote/EdgeRemoteWebDriver.cs | 15 +- dotnet/test/remote/FirefoxRemoteWebDriver.cs | 15 +- .../test/remote/RemoteSessionCreationTests.cs | 195 +- .../remote/RemoteWebDriverSpecificTests.cs | 109 +- .../TestInternetExplorerRemoteWebDriver.cs | 17 +- dotnet/test/safari/SafariSpecificTests.cs | 9 +- .../Events/EventFiringWebDriverElementTest.cs | 31 +- .../Events/EventFiringWebDriverTest.cs | 505 ++- .../Extensions/ExecuteJavaScriptTest.cs | 235 +- dotnet/test/support/UI/DefaultWaitTest.cs | 263 +- dotnet/test/support/UI/HandCrankClock.cs | 33 +- .../test/support/UI/LoadableComponentTests.cs | 175 +- .../test/support/UI/PopupWindowFinderTest.cs | 179 +- dotnet/test/support/UI/SelectBrowserTests.cs | 789 +++-- dotnet/test/support/UI/SelectTests.cs | 893 +++-- .../support/UI/SlowLoadableComponentTest.cs | 251 +- dotnet/test/support/UI/WebDriverWaitTest.cs | 241 +- 419 files changed, 46153 insertions(+), 46569 deletions(-) create mode 100644 dotnet/.editorconfig diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig new file mode 100644 index 0000000000000..e25f2c3ede71c --- /dev/null +++ b/dotnet/.editorconfig @@ -0,0 +1,2 @@ +[*.cs] +csharp_style_namespace_declarations=file_scoped:suggestion diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs index 9f0355cc204cd..a23c4a04bf9ed 100644 --- a/dotnet/src/support/Events/EventFiringWebDriver.cs +++ b/dotnet/src/support/Events/EventFiringWebDriver.cs @@ -23,269 +23,154 @@ using System.Drawing; using System.Threading.Tasks; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// A wrapper around an arbitrary WebDriver instance which supports registering for +/// events, e.g. for logging purposes. +/// +public class EventFiringWebDriver : IWebDriver, IJavaScriptExecutor, ITakesScreenshot, IWrapsDriver { /// - /// A wrapper around an arbitrary WebDriver instance which supports registering for - /// events, e.g. for logging purposes. + /// Initializes a new instance of the class. /// - public class EventFiringWebDriver : IWebDriver, IJavaScriptExecutor, ITakesScreenshot, IWrapsDriver + /// The driver to register events for. + /// If is . + public EventFiringWebDriver(IWebDriver parentDriver) { - /// - /// Initializes a new instance of the class. - /// - /// The driver to register events for. - /// If is . - public EventFiringWebDriver(IWebDriver parentDriver) - { - this.WrappedDriver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); - } - - /// - /// Fires before the driver begins navigation. - /// - public event EventHandler? Navigating; - - /// - /// Fires after the driver completes navigation - /// - public event EventHandler? Navigated; - - /// - /// Fires before the driver begins navigation back one entry in the browser history list. - /// - public event EventHandler? NavigatingBack; - - /// - /// Fires after the driver completes navigation back one entry in the browser history list. - /// - public event EventHandler? NavigatedBack; - - /// - /// Fires before the driver begins navigation forward one entry in the browser history list. - /// - public event EventHandler? NavigatingForward; + this.WrappedDriver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + } - /// - /// Fires after the driver completes navigation forward one entry in the browser history list. - /// - public event EventHandler? NavigatedForward; + /// + /// Fires before the driver begins navigation. + /// + public event EventHandler? Navigating; - /// - /// Fires before the driver clicks on an element. - /// - public event EventHandler? ElementClicking; + /// + /// Fires after the driver completes navigation + /// + public event EventHandler? Navigated; - /// - /// Fires after the driver has clicked on an element. - /// - public event EventHandler? ElementClicked; + /// + /// Fires before the driver begins navigation back one entry in the browser history list. + /// + public event EventHandler? NavigatingBack; - /// - /// Fires before the driver changes the value of an element via Clear(), SendKeys() or Toggle(). - /// - public event EventHandler? ElementValueChanging; + /// + /// Fires after the driver completes navigation back one entry in the browser history list. + /// + public event EventHandler? NavigatedBack; - /// - /// Fires after the driver has changed the value of an element via Clear(), SendKeys() or Toggle(). - /// - public event EventHandler? ElementValueChanged; + /// + /// Fires before the driver begins navigation forward one entry in the browser history list. + /// + public event EventHandler? NavigatingForward; - /// - /// Fires before the driver starts to find an element. - /// - public event EventHandler? FindingElement; + /// + /// Fires after the driver completes navigation forward one entry in the browser history list. + /// + public event EventHandler? NavigatedForward; - /// - /// Fires after the driver completes finding an element. - /// - public event EventHandler? FindElementCompleted; + /// + /// Fires before the driver clicks on an element. + /// + public event EventHandler? ElementClicking; - /// - /// Fires before the driver starts to get a shadow root. - /// - public event EventHandler? GettingShadowRoot; + /// + /// Fires after the driver has clicked on an element. + /// + public event EventHandler? ElementClicked; - /// - /// Fires after the driver completes getting a shadow root. - /// - public event EventHandler? GetShadowRootCompleted; + /// + /// Fires before the driver changes the value of an element via Clear(), SendKeys() or Toggle(). + /// + public event EventHandler? ElementValueChanging; - /// - /// Fires before a script is executed. - /// - public event EventHandler? ScriptExecuting; + /// + /// Fires after the driver has changed the value of an element via Clear(), SendKeys() or Toggle(). + /// + public event EventHandler? ElementValueChanged; - /// - /// Fires after a script is executed. - /// - public event EventHandler? ScriptExecuted; + /// + /// Fires before the driver starts to find an element. + /// + public event EventHandler? FindingElement; - /// - /// Fires when an exception is thrown. - /// - public event EventHandler? ExceptionThrown; + /// + /// Fires after the driver completes finding an element. + /// + public event EventHandler? FindElementCompleted; - /// - /// Gets the wrapped by this EventsFiringWebDriver instance. - /// - public IWebDriver WrappedDriver { get; } + /// + /// Fires before the driver starts to get a shadow root. + /// + public event EventHandler? GettingShadowRoot; - /// - /// Gets or sets the URL the browser is currently displaying. - /// - /// - /// Setting the property will load a new web page in the current browser window. - /// This is done using an HTTP GET operation, and the method will block until the - /// load is complete. This will follow redirects issued either by the server or - /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" - /// for any duration of time, it is best to wait until this timeout is over, since - /// should the underlying page change while your test is executing the results of - /// future calls against this interface will be against the freshly loaded page. - /// - /// - /// - public string Url - { - get - { - string url; - try - { - url = this.WrappedDriver.Url; - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } + /// + /// Fires after the driver completes getting a shadow root. + /// + public event EventHandler? GetShadowRootCompleted; - return url; - } + /// + /// Fires before a script is executed. + /// + public event EventHandler? ScriptExecuting; - set - { - try - { - WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.WrappedDriver, value); - this.OnNavigating(e); - this.WrappedDriver.Url = value; - this.OnNavigated(e); - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } - } - } + /// + /// Fires after a script is executed. + /// + public event EventHandler? ScriptExecuted; - /// - /// Gets the title of the current browser window. - /// - public string Title - { - get - { - string title; - try - { - title = this.WrappedDriver.Title; - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } + /// + /// Fires when an exception is thrown. + /// + public event EventHandler? ExceptionThrown; - return title; - } - } + /// + /// Gets the wrapped by this EventsFiringWebDriver instance. + /// + public IWebDriver WrappedDriver { get; } - /// - /// Gets the source of the page last loaded by the browser. - /// - /// - /// If the page has been modified after loading (for example, by JavaScript) - /// there is no guarantee that the returned text is that of the modified page. - /// Please consult the documentation of the particular driver being used to - /// determine whether the returned text reflects the current state of the page - /// or the text last sent by the web server. The page source returned is a - /// representation of the underlying DOM: do not expect it to be formatted - /// or escaped in the same way as the response sent from the web server. - /// - public string PageSource + /// + /// Gets or sets the URL the browser is currently displaying. + /// + /// + /// Setting the property will load a new web page in the current browser window. + /// This is done using an HTTP GET operation, and the method will block until the + /// load is complete. This will follow redirects issued either by the server or + /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" + /// for any duration of time, it is best to wait until this timeout is over, since + /// should the underlying page change while your test is executing the results of + /// future calls against this interface will be against the freshly loaded page. + /// + /// + /// + public string Url + { + get { - get + string url; + try { - string source; - try - { - source = this.WrappedDriver.PageSource; - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } - - return source; + url = this.WrappedDriver.Url; } - } - - /// - /// Gets the current window handle, which is an opaque handle to this - /// window that uniquely identifies it within this driver instance. - /// - public string CurrentWindowHandle - { - get + catch (Exception ex) { - string handle; - try - { - handle = this.WrappedDriver.CurrentWindowHandle; - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } - - return handle; + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; } - } - - /// - /// Gets the window handles of open browser windows. - /// - public ReadOnlyCollection WindowHandles - { - get - { - ReadOnlyCollection handles; - try - { - handles = this.WrappedDriver.WindowHandles; - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } - return handles; - } + return url; } - /// - /// Close the current window, quitting the browser if it is the last window currently open. - /// - public void Close() + set { try { - this.WrappedDriver.Close(); + WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.WrappedDriver, value); + this.OnNavigating(e); + this.WrappedDriver.Url = value; + this.OnNavigated(e); } catch (Exception ex) { @@ -293,69 +178,50 @@ public void Close() throw; } } + } - /// - /// Quits this driver, closing every associated window. - /// - public void Quit() + /// + /// Gets the title of the current browser window. + /// + public string Title + { + get { + string title; try { - this.WrappedDriver.Quit(); + title = this.WrappedDriver.Title; } catch (Exception ex) { this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); throw; } - } - - /// - /// Instructs the driver to change its settings. - /// - /// An object allowing the user to change - /// the settings of the driver. - public IOptions Manage() - { - return new EventFiringOptions(this); - } - - /// - /// Instructs the driver to navigate the browser to another location. - /// - /// An object allowing the user to access - /// the browser's history and to navigate to a given URL. - public INavigation Navigate() - { - return new EventFiringNavigation(this); - } - /// - /// Instructs the driver to send future commands to a different frame or window. - /// - /// An object which can be used to select - /// a frame or window. - public ITargetLocator SwitchTo() - { - return new EventFiringTargetLocator(this); + return title; } + } - /// - /// Find the first using the given method. - /// - /// The locating mechanism to use. - /// The first matching on the current context. - /// If no element matches the criteria. - public IWebElement FindElement(By by) + /// + /// Gets the source of the page last loaded by the browser. + /// + /// + /// If the page has been modified after loading (for example, by JavaScript) + /// there is no guarantee that the returned text is that of the modified page. + /// Please consult the documentation of the particular driver being used to + /// determine whether the returned text reflects the current state of the page + /// or the text last sent by the web server. The page source returned is a + /// representation of the underlying DOM: do not expect it to be formatted + /// or escaped in the same way as the response sent from the web server. + /// + public string PageSource + { + get { - IWebElement wrappedElement; + string source; try { - FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); - this.OnFindingElement(e); - IWebElement element = this.WrappedDriver.FindElement(by); - this.OnFindElementCompleted(e); - wrappedElement = this.WrapElement(element); + source = this.WrappedDriver.PageSource; } catch (Exception ex) { @@ -363,32 +229,22 @@ public IWebElement FindElement(By by) throw; } - return wrappedElement; + return source; } + } - /// - /// Find all IWebElements within the current context - /// using the given mechanism. - /// - /// The locating mechanism to use. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - public ReadOnlyCollection FindElements(By by) + /// + /// Gets the current window handle, which is an opaque handle to this + /// window that uniquely identifies it within this driver instance. + /// + public string CurrentWindowHandle + { + get { + string handle; try { - FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); - this.OnFindingElement(e); - ReadOnlyCollection elements = this.WrappedDriver.FindElements(by); - this.OnFindElementCompleted(e); - - List wrappedElementList = new List(elements.Count); - foreach (IWebElement element in elements) - { - IWebElement wrappedElement = this.WrapElement(element); - wrappedElementList.Add(wrappedElement); - } - return wrappedElementList.AsReadOnly(); + handle = this.WrappedDriver.CurrentWindowHandle; } catch (Exception ex) { @@ -396,68 +252,21 @@ public ReadOnlyCollection FindElements(By by) throw; } + return handle; } + } - /// - /// Frees all managed and unmanaged resources used by this instance. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Executes JavaScript in the context of the currently selected frame or window. - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// - /// - /// The ExecuteScript method executes JavaScript in the context of - /// the currently selected frame or window. This means that "document" will refer - /// to the current document. If the script has a return value, then the following - /// steps will be taken: - /// - /// - /// - /// For an HTML element, this method returns a - /// For a number, a is returned - /// For a boolean, a is returned - /// For all other cases a is returned. - /// For an array,we check the first element, and attempt to return a - /// of that type, following the rules above. Nested lists are not - /// supported. - /// If the value is null or there is no return value, - /// is returned. - /// - /// - /// - /// Arguments must be a number (which will be converted to a ), - /// a , a or a , - /// or a . - /// An exception will be thrown if the arguments do not meet these criteria. - /// The arguments will be made available to the JavaScript via the "arguments" magic - /// variable, as if the function were called via "Function.apply" - /// - /// - public object? ExecuteScript(string script, params object?[] args) + /// + /// Gets the window handles of open browser windows. + /// + public ReadOnlyCollection WindowHandles + { + get { - if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) - { - throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); - } - - object? scriptResult; + ReadOnlyCollection handles; try { - object?[] unwrappedArgs = UnwrapElementArguments(args); - - WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); - this.OnScriptExecuting(e); - scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); - this.OnScriptExecuted(e); + handles = this.WrappedDriver.WindowHandles; } catch (Exception ex) { @@ -465,1198 +274,1154 @@ public void Dispose() throw; } - return scriptResult; + return handles; } + } - - /// - /// Executes JavaScript in the context of the currently selected frame or window. - /// - /// A object containing the code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// If is . - /// - /// - /// The ExecuteScript method executes JavaScript in the context of - /// the currently selected frame or window. This means that "document" will refer - /// to the current document. If the script has a return value, then the following - /// steps will be taken: - /// - /// - /// - /// For an HTML element, this method returns a - /// For a number, a is returned - /// For a boolean, a is returned - /// For all other cases a is returned. - /// For an array,we check the first element, and attempt to return a - /// of that type, following the rules above. Nested lists are not - /// supported. - /// If the value is null or there is no return value, - /// is returned. - /// - /// - /// - /// Arguments must be a number (which will be converted to a ), - /// a , a or a , - /// or a . - /// An exception will be thrown if the arguments do not meet these criteria. - /// The arguments will be made available to the JavaScript via the "arguments" magic - /// variable, as if the function were called via "Function.apply" - /// - /// - public object? ExecuteScript(PinnedScript script, params object?[] args) + /// + /// Close the current window, quitting the browser if it is the last window currently open. + /// + public void Close() + { + try { - if (script == null) - { - throw new ArgumentNullException(nameof(script)); - } + this.WrappedDriver.Close(); + } + catch (Exception ex) + { + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; + } + } - if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) - { - throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); - } + /// + /// Quits this driver, closing every associated window. + /// + public void Quit() + { + try + { + this.WrappedDriver.Quit(); + } + catch (Exception ex) + { + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; + } + } - object? scriptResult; - try - { - object?[] unwrappedArgs = UnwrapElementArguments(args); + /// + /// Instructs the driver to change its settings. + /// + /// An object allowing the user to change + /// the settings of the driver. + public IOptions Manage() + { + return new EventFiringOptions(this); + } - WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script.Source); - this.OnScriptExecuting(e); - scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); - this.OnScriptExecuted(e); - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } + /// + /// Instructs the driver to navigate the browser to another location. + /// + /// An object allowing the user to access + /// the browser's history and to navigate to a given URL. + public INavigation Navigate() + { + return new EventFiringNavigation(this); + } + + /// + /// Instructs the driver to send future commands to a different frame or window. + /// + /// An object which can be used to select + /// a frame or window. + public ITargetLocator SwitchTo() + { + return new EventFiringTargetLocator(this); + } - return scriptResult; + /// + /// Find the first using the given method. + /// + /// The locating mechanism to use. + /// The first matching on the current context. + /// If no element matches the criteria. + public IWebElement FindElement(By by) + { + IWebElement wrappedElement; + try + { + FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); + this.OnFindingElement(e); + IWebElement element = this.WrappedDriver.FindElement(by); + this.OnFindElementCompleted(e); + wrappedElement = this.WrapElement(element); + } + catch (Exception ex) + { + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; } - /// - /// Executes JavaScript asynchronously in the context of the currently selected frame or window. - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - public object? ExecuteAsyncScript(string script, params object?[] args) + return wrappedElement; + } + + /// + /// Find all IWebElements within the current context + /// using the given mechanism. + /// + /// The locating mechanism to use. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + public ReadOnlyCollection FindElements(By by) + { + try { - if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) + FindElementEventArgs e = new FindElementEventArgs(this.WrappedDriver, by); + this.OnFindingElement(e); + ReadOnlyCollection elements = this.WrappedDriver.FindElements(by); + this.OnFindElementCompleted(e); + + List wrappedElementList = new List(elements.Count); + foreach (IWebElement element in elements) { - throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); + IWebElement wrappedElement = this.WrapElement(element); + wrappedElementList.Add(wrappedElement); } + return wrappedElementList.AsReadOnly(); + } + catch (Exception ex) + { + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; + } - object? scriptResult; - try - { - object?[] unwrappedArgs = UnwrapElementArguments(args); + } - WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); - this.OnScriptExecuting(e); - scriptResult = javascriptDriver.ExecuteAsyncScript(script, unwrappedArgs); - this.OnScriptExecuted(e); - } - catch (Exception ex) - { - this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); - throw; - } + /// + /// Frees all managed and unmanaged resources used by this instance. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } - return scriptResult; + /// + /// Executes JavaScript in the context of the currently selected frame or window. + /// + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// + /// + /// The ExecuteScript method executes JavaScript in the context of + /// the currently selected frame or window. This means that "document" will refer + /// to the current document. If the script has a return value, then the following + /// steps will be taken: + /// + /// + /// + /// For an HTML element, this method returns a + /// For a number, a is returned + /// For a boolean, a is returned + /// For all other cases a is returned. + /// For an array,we check the first element, and attempt to return a + /// of that type, following the rules above. Nested lists are not + /// supported. + /// If the value is null or there is no return value, + /// is returned. + /// + /// + /// + /// Arguments must be a number (which will be converted to a ), + /// a , a or a , + /// or a . + /// An exception will be thrown if the arguments do not meet these criteria. + /// The arguments will be made available to the JavaScript via the "arguments" magic + /// variable, as if the function were called via "Function.apply" + /// + /// + public object? ExecuteScript(string script, params object?[] args) + { + if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) + { + throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); } - /// - /// Gets a object representing the image of the page on the screen. - /// - /// A object containing the image. - public Screenshot GetScreenshot() + object? scriptResult; + try { - if (this.WrappedDriver is not ITakesScreenshot screenshotDriver) - { - throw new NotSupportedException("Underlying driver instance does not support taking screenshots"); - } + object?[] unwrappedArgs = UnwrapElementArguments(args); - return screenshotDriver.GetScreenshot(); + WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); + this.OnScriptExecuting(e); + scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); + this.OnScriptExecuted(e); } - - /// - /// Frees all managed and, optionally, unmanaged resources used by this instance. - /// - /// to dispose of only managed resources; - /// to dispose of managed and unmanaged resources. - protected virtual void Dispose(bool disposing) + catch (Exception ex) { - if (disposing) - { - this.WrappedDriver.Dispose(); - } + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigating(WebDriverNavigationEventArgs e) + return scriptResult; + } + + + /// + /// Executes JavaScript in the context of the currently selected frame or window. + /// + /// A object containing the code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// If is . + /// + /// + /// The ExecuteScript method executes JavaScript in the context of + /// the currently selected frame or window. This means that "document" will refer + /// to the current document. If the script has a return value, then the following + /// steps will be taken: + /// + /// + /// + /// For an HTML element, this method returns a + /// For a number, a is returned + /// For a boolean, a is returned + /// For all other cases a is returned. + /// For an array,we check the first element, and attempt to return a + /// of that type, following the rules above. Nested lists are not + /// supported. + /// If the value is null or there is no return value, + /// is returned. + /// + /// + /// + /// Arguments must be a number (which will be converted to a ), + /// a , a or a , + /// or a . + /// An exception will be thrown if the arguments do not meet these criteria. + /// The arguments will be made available to the JavaScript via the "arguments" magic + /// variable, as if the function were called via "Function.apply" + /// + /// + public object? ExecuteScript(PinnedScript script, params object?[] args) + { + if (script == null) { - if (this.Navigating != null) - { - this.Navigating(this, e); - } + throw new ArgumentNullException(nameof(script)); } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigated(WebDriverNavigationEventArgs e) + if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) { - if (this.Navigated != null) - { - this.Navigated(this, e); - } + throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigatingBack(WebDriverNavigationEventArgs e) + object? scriptResult; + try { - if (this.NavigatingBack != null) - { - this.NavigatingBack(this, e); - } - } + object?[] unwrappedArgs = UnwrapElementArguments(args); - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigatedBack(WebDriverNavigationEventArgs e) + WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script.Source); + this.OnScriptExecuting(e); + scriptResult = javascriptDriver.ExecuteScript(script, unwrappedArgs); + this.OnScriptExecuted(e); + } + catch (Exception ex) { - if (this.NavigatedBack != null) - { - this.NavigatedBack(this, e); - } + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigatingForward(WebDriverNavigationEventArgs e) + return scriptResult; + } + + /// + /// Executes JavaScript asynchronously in the context of the currently selected frame or window. + /// + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + public object? ExecuteAsyncScript(string script, params object?[] args) + { + if (this.WrappedDriver is not IJavaScriptExecutor javascriptDriver) { - if (this.NavigatingForward != null) - { - this.NavigatingForward(this, e); - } + throw new NotSupportedException("Underlying driver instance does not support executing JavaScript"); } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnNavigatedForward(WebDriverNavigationEventArgs e) + object? scriptResult; + try { - if (this.NavigatedForward != null) - { - this.NavigatedForward(this, e); - } + object?[] unwrappedArgs = UnwrapElementArguments(args); + + WebDriverScriptEventArgs e = new WebDriverScriptEventArgs(this.WrappedDriver, script); + this.OnScriptExecuting(e); + scriptResult = javascriptDriver.ExecuteAsyncScript(script, unwrappedArgs); + this.OnScriptExecuted(e); + } + catch (Exception ex) + { + this.OnException(new WebDriverExceptionEventArgs(this.WrappedDriver, ex)); + throw; } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnElementClicking(WebElementEventArgs e) + return scriptResult; + } + + /// + /// Gets a object representing the image of the page on the screen. + /// + /// A object containing the image. + public Screenshot GetScreenshot() + { + if (this.WrappedDriver is not ITakesScreenshot screenshotDriver) { - if (this.ElementClicking != null) - { - this.ElementClicking(this, e); - } + throw new NotSupportedException("Underlying driver instance does not support taking screenshots"); } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnElementClicked(WebElementEventArgs e) + return screenshotDriver.GetScreenshot(); + } + + /// + /// Frees all managed and, optionally, unmanaged resources used by this instance. + /// + /// to dispose of only managed resources; + /// to dispose of managed and unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (this.ElementClicked != null) - { - this.ElementClicked(this, e); - } + this.WrappedDriver.Dispose(); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnElementValueChanging(WebElementValueEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigating(WebDriverNavigationEventArgs e) + { + if (this.Navigating != null) { - if (this.ElementValueChanging != null) - { - this.ElementValueChanging(this, e); - } + this.Navigating(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnElementValueChanged(WebElementValueEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigated(WebDriverNavigationEventArgs e) + { + if (this.Navigated != null) { - if (this.ElementValueChanged != null) - { - this.ElementValueChanged(this, e); - } + this.Navigated(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnFindingElement(FindElementEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigatingBack(WebDriverNavigationEventArgs e) + { + if (this.NavigatingBack != null) { - if (this.FindingElement != null) - { - this.FindingElement(this, e); - } + this.NavigatingBack(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnFindElementCompleted(FindElementEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigatedBack(WebDriverNavigationEventArgs e) + { + if (this.NavigatedBack != null) { - if (this.FindElementCompleted != null) - { - this.FindElementCompleted(this, e); - } + this.NavigatedBack(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnGettingShadowRoot(GetShadowRootEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigatingForward(WebDriverNavigationEventArgs e) + { + if (this.NavigatingForward != null) { - if (this.GettingShadowRoot != null) - { - this.GettingShadowRoot(this, e); - } + this.NavigatingForward(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnGetShadowRootCompleted(GetShadowRootEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnNavigatedForward(WebDriverNavigationEventArgs e) + { + if (this.NavigatedForward != null) { - if (this.GetShadowRootCompleted != null) - { - this.GetShadowRootCompleted(this, e); - } + this.NavigatedForward(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnScriptExecuting(WebDriverScriptEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnElementClicking(WebElementEventArgs e) + { + if (this.ElementClicking != null) { - if (this.ScriptExecuting != null) - { - this.ScriptExecuting(this, e); - } + this.ElementClicking(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnScriptExecuted(WebDriverScriptEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnElementClicked(WebElementEventArgs e) + { + if (this.ElementClicked != null) { - if (this.ScriptExecuted != null) - { - this.ScriptExecuted(this, e); - } + this.ElementClicked(this, e); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnException(WebDriverExceptionEventArgs e) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnElementValueChanging(WebElementValueEventArgs e) + { + if (this.ElementValueChanging != null) { - if (this.ExceptionThrown != null) - { - this.ExceptionThrown(this, e); - } + this.ElementValueChanging(this, e); } + } - private static object?[] UnwrapElementArguments(object?[] args) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnElementValueChanged(WebElementValueEventArgs e) + { + if (this.ElementValueChanged != null) { - if (args is null) - { - throw new InvalidOperationException("Cannot unwrap null args"); - } + this.ElementValueChanged(this, e); + } + } - // Walk the args: the various drivers expect unwrapped versions of the elements - List unwrappedArgs = new List(args.Length); - foreach (object? arg in args) - { - if (arg is IWrapsElement eventElementArg) - { - unwrappedArgs.Add(eventElementArg.WrappedElement); - } - else - { - unwrappedArgs.Add(arg); - } - } + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnFindingElement(FindElementEventArgs e) + { + if (this.FindingElement != null) + { + this.FindingElement(this, e); + } + } - return unwrappedArgs.ToArray(); + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnFindElementCompleted(FindElementEventArgs e) + { + if (this.FindElementCompleted != null) + { + this.FindElementCompleted(this, e); } + } - private IWebElement WrapElement(IWebElement underlyingElement) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnGettingShadowRoot(GetShadowRootEventArgs e) + { + if (this.GettingShadowRoot != null) { - return new EventFiringWebElement(this, underlyingElement); + this.GettingShadowRoot(this, e); } + } - /// - /// Provides a mechanism for Navigating with the driver. - /// - private class EventFiringNavigation : INavigation + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnGetShadowRootCompleted(GetShadowRootEventArgs e) + { + if (this.GetShadowRootCompleted != null) { - private readonly EventFiringWebDriver parentDriver; - private readonly INavigation wrappedNavigation; + this.GetShadowRootCompleted(this, e); + } + } - /// - /// Initializes a new instance of the class - /// - /// Driver in use - public EventFiringNavigation(EventFiringWebDriver driver) - { - this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.wrappedNavigation = this.parentDriver.WrappedDriver.Navigate(); - } + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnScriptExecuting(WebDriverScriptEventArgs e) + { + if (this.ScriptExecuting != null) + { + this.ScriptExecuting(this, e); + } + } - /// - /// Move the browser back - /// - public void Back() - { - Task.Run(async delegate - { - await this.BackAsync(); - }).GetAwaiter().GetResult(); - } + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnScriptExecuted(WebDriverScriptEventArgs e) + { + if (this.ScriptExecuted != null) + { + this.ScriptExecuted(this, e); + } + } - /// - /// Move the browser back as an asynchronous task. - /// - /// A task object representing the asynchronous operation - public async Task BackAsync() - { - try - { - WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnException(WebDriverExceptionEventArgs e) + { + if (this.ExceptionThrown != null) + { + this.ExceptionThrown(this, e); + } + } - this.parentDriver.OnNavigatingBack(e); - await this.wrappedNavigation.BackAsync().ConfigureAwait(false); - this.parentDriver.OnNavigatedBack(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - } + private static object?[] UnwrapElementArguments(object?[] args) + { + if (args is null) + { + throw new InvalidOperationException("Cannot unwrap null args"); + } - /// - /// Move a single "item" forward in the browser's history. - /// - public void Forward() + // Walk the args: the various drivers expect unwrapped versions of the elements + List unwrappedArgs = new List(args.Length); + foreach (object? arg in args) + { + if (arg is IWrapsElement eventElementArg) { - Task.Run(async delegate - { - await this.ForwardAsync(); - }).GetAwaiter().GetResult(); + unwrappedArgs.Add(eventElementArg.WrappedElement); } - - /// - /// Move a single "item" forward in the browser's history as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - public async Task ForwardAsync() + else { - try - { - WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); - this.parentDriver.OnNavigatingForward(e); - await this.wrappedNavigation.ForwardAsync().ConfigureAwait(false); - this.parentDriver.OnNavigatedForward(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + unwrappedArgs.Add(arg); } + } - /// - /// Navigate to a url. - /// - /// String of where you want the browser to go to - public void GoToUrl(string url) - { - Task.Run(async delegate - { - await this.GoToUrlAsync(url); - }).GetAwaiter().GetResult(); - } + return unwrappedArgs.ToArray(); + } - /// - /// Navigate to a url as an asynchronous task. - /// - /// String of where you want the browser to go. - /// A task object representing the asynchronous operation. - public async Task GoToUrlAsync(string url) - { - if (url == null) - { - throw new ArgumentNullException(nameof(url), "url cannot be null"); - } + private IWebElement WrapElement(IWebElement underlyingElement) + { + return new EventFiringWebElement(this, underlyingElement); + } - try - { - WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver, url); - this.parentDriver.OnNavigating(e); - await this.wrappedNavigation.GoToUrlAsync(url).ConfigureAwait(false); - this.parentDriver.OnNavigated(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - } + /// + /// Provides a mechanism for Navigating with the driver. + /// + private class EventFiringNavigation : INavigation + { + private readonly EventFiringWebDriver parentDriver; + private readonly INavigation wrappedNavigation; + + /// + /// Initializes a new instance of the class + /// + /// Driver in use + public EventFiringNavigation(EventFiringWebDriver driver) + { + this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.wrappedNavigation = this.parentDriver.WrappedDriver.Navigate(); + } - /// - /// Navigate to a url. - /// - /// Uri object of where you want the browser to go to - public void GoToUrl(Uri url) + /// + /// Move the browser back + /// + public void Back() + { + Task.Run(async delegate { - Task.Run(async delegate - { - await this.GoToUrlAsync(url); - }).GetAwaiter().GetResult(); - } + await this.BackAsync(); + }).GetAwaiter().GetResult(); + } - /// - /// Navigate to a url as an asynchronous task. - /// - /// Uri object of where you want the browser to go. - /// A task object representing the asynchronous operation. - public async Task GoToUrlAsync(Uri url) + /// + /// Move the browser back as an asynchronous task. + /// + /// A task object representing the asynchronous operation + public async Task BackAsync() + { + try { - if (url == null) - { - throw new ArgumentNullException(nameof(url), "url cannot be null"); - } + WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); - await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false); + this.parentDriver.OnNavigatingBack(e); + await this.wrappedNavigation.BackAsync().ConfigureAwait(false); + this.parentDriver.OnNavigatedBack(e); } - - /// - /// Reload the current page. - /// - public void Refresh() + catch (Exception ex) { - Task.Run(async delegate - { - await this.RefreshAsync(); - }).GetAwaiter().GetResult(); + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Reload the current page as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - public async Task RefreshAsync() + /// + /// Move a single "item" forward in the browser's history. + /// + public void Forward() + { + Task.Run(async delegate { - try - { - await this.wrappedNavigation.RefreshAsync().ConfigureAwait(false); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - } + await this.ForwardAsync(); + }).GetAwaiter().GetResult(); } /// - /// Provides a mechanism for setting options needed for the driver during the test. + /// Move a single "item" forward in the browser's history as an asynchronous task. /// - private class EventFiringOptions : IOptions + /// A task object representing the asynchronous operation. + public async Task ForwardAsync() { - private readonly IOptions wrappedOptions; - - /// - /// Initializes a new instance of the class - /// - /// Instance of the driver currently in use - public EventFiringOptions(EventFiringWebDriver driver) + try { - this.wrappedOptions = driver.WrappedDriver.Manage(); + WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver); + this.parentDriver.OnNavigatingForward(e); + await this.wrappedNavigation.ForwardAsync().ConfigureAwait(false); + this.parentDriver.OnNavigatedForward(e); } - - /// - /// Gets an object allowing the user to manipulate cookies on the page. - /// - public ICookieJar Cookies => this.wrappedOptions.Cookies; - - /// - /// Gets an object allowing the user to manipulate the currently-focused browser window. - /// - /// "Currently-focused" is defined as the browser window having the window handle - /// returned when IWebDriver.CurrentWindowHandle is called. - public IWindow Window => this.wrappedOptions.Window; - - public ILogs Logs => this.wrappedOptions.Logs; - - public INetwork Network => this.wrappedOptions.Network; - - /// - /// Provides access to the timeouts defined for this driver. - /// - /// An object implementing the interface. - public ITimeouts Timeouts() + catch (Exception ex) { - return new EventFiringTimeouts(this.wrappedOptions); + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } } /// - /// Provides a mechanism for finding elements on the page with locators. + /// Navigate to a url. /// - private class EventFiringTargetLocator : ITargetLocator + /// String of where you want the browser to go to + public void GoToUrl(string url) { - private readonly ITargetLocator wrappedLocator; - private readonly EventFiringWebDriver parentDriver; - - /// - /// Initializes a new instance of the class - /// - /// The driver that is currently in use - public EventFiringTargetLocator(EventFiringWebDriver driver) + Task.Run(async delegate { - this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.wrappedLocator = this.parentDriver.WrappedDriver.SwitchTo(); - } + await this.GoToUrlAsync(url); + }).GetAwaiter().GetResult(); + } - /// - /// Move to a different frame using its index - /// - /// The index of the - /// A WebDriver instance that is currently in use - public IWebDriver Frame(int frameIndex) + /// + /// Navigate to a url as an asynchronous task. + /// + /// String of where you want the browser to go. + /// A task object representing the asynchronous operation. + public async Task GoToUrlAsync(string url) + { + if (url == null) { - IWebDriver driver; - try - { - driver = this.wrappedLocator.Frame(frameIndex); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return driver; + throw new ArgumentNullException(nameof(url), "url cannot be null"); } - /// - /// Move to different frame using its name - /// - /// name of the frame - /// A WebDriver instance that is currently in use - public IWebDriver Frame(string frameName) + try { - IWebDriver driver; - try - { - driver = this.wrappedLocator.Frame(frameName); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return driver; + WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver, url); + this.parentDriver.OnNavigating(e); + await this.wrappedNavigation.GoToUrlAsync(url).ConfigureAwait(false); + this.parentDriver.OnNavigated(e); } - - /// - /// Move to a frame element. - /// - /// a previously found FRAME or IFRAME element. - /// A WebDriver instance that is currently in use. - public IWebDriver Frame(IWebElement frameElement) + catch (Exception ex) { - IWebDriver driver; - try - { - IWrapsElement wrapper = (IWrapsElement)frameElement; - driver = this.wrappedLocator.Frame(wrapper.WrappedElement); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return driver; + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Select the parent frame of the currently selected frame. - /// - /// An instance focused on the specified frame. - public IWebDriver ParentFrame() + /// + /// Navigate to a url. + /// + /// Uri object of where you want the browser to go to + public void GoToUrl(Uri url) + { + Task.Run(async delegate { - IWebDriver driver; - try - { - driver = this.wrappedLocator.ParentFrame(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + await this.GoToUrlAsync(url); + }).GetAwaiter().GetResult(); + } - return driver; + /// + /// Navigate to a url as an asynchronous task. + /// + /// Uri object of where you want the browser to go. + /// A task object representing the asynchronous operation. + public async Task GoToUrlAsync(Uri url) + { + if (url == null) + { + throw new ArgumentNullException(nameof(url), "url cannot be null"); } - /// - /// Change to the Window by passing in the name - /// - /// name of the window that you wish to move to - /// A WebDriver instance that is currently in use - public IWebDriver Window(string windowName) + await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false); + } + + /// + /// Reload the current page. + /// + public void Refresh() + { + Task.Run(async delegate { - IWebDriver driver; - try - { - driver = this.wrappedLocator.Window(windowName); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + await this.RefreshAsync(); + }).GetAwaiter().GetResult(); + } - return driver; + /// + /// Reload the current page as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + public async Task RefreshAsync() + { + try + { + await this.wrappedNavigation.RefreshAsync().ConfigureAwait(false); } - - /// - /// Creates a new browser window and switches the focus for future commands - /// of this driver to the new window. - /// - /// The type of new browser window to be created. - /// The created window is not guaranteed to be of the requested type; if - /// the driver does not support the requested type, a new browser window - /// will be created of whatever type the driver does support. - /// An instance focused on the new browser. - public IWebDriver NewWindow(WindowType typeHint) + catch (Exception ex) { - IWebDriver driver; - try - { - driver = this.wrappedLocator.NewWindow(typeHint); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return driver; + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } + } - /// - /// Change the active frame to the default - /// - /// Element of the default - public IWebDriver DefaultContent() - { - IWebDriver driver; - try - { - driver = this.wrappedLocator.DefaultContent(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + /// + /// Provides a mechanism for setting options needed for the driver during the test. + /// + private class EventFiringOptions : IOptions + { + private readonly IOptions wrappedOptions; - return driver; - } + /// + /// Initializes a new instance of the class + /// + /// Instance of the driver currently in use + public EventFiringOptions(EventFiringWebDriver driver) + { + this.wrappedOptions = driver.WrappedDriver.Manage(); + } - /// - /// Finds the active element on the page and returns it - /// - /// Element that is active - public IWebElement ActiveElement() - { - IWebElement element; - try - { - element = this.wrappedLocator.ActiveElement(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + /// + /// Gets an object allowing the user to manipulate cookies on the page. + /// + public ICookieJar Cookies => this.wrappedOptions.Cookies; - return element; - } + /// + /// Gets an object allowing the user to manipulate the currently-focused browser window. + /// + /// "Currently-focused" is defined as the browser window having the window handle + /// returned when IWebDriver.CurrentWindowHandle is called. + public IWindow Window => this.wrappedOptions.Window; - /// - /// Switches to the currently active modal dialog for this particular driver instance. - /// - /// A handle to the dialog. - public IAlert Alert() - { - IAlert alert; - try - { - alert = this.wrappedLocator.Alert(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + public ILogs Logs => this.wrappedOptions.Logs; - return alert; - } + public INetwork Network => this.wrappedOptions.Network; + + /// + /// Provides access to the timeouts defined for this driver. + /// + /// An object implementing the interface. + public ITimeouts Timeouts() + { + return new EventFiringTimeouts(this.wrappedOptions); } + } + + /// + /// Provides a mechanism for finding elements on the page with locators. + /// + private class EventFiringTargetLocator : ITargetLocator + { + private readonly ITargetLocator wrappedLocator; + private readonly EventFiringWebDriver parentDriver; /// - /// Defines the interface through which the user can define timeouts. + /// Initializes a new instance of the class /// - private class EventFiringTimeouts : ITimeouts + /// The driver that is currently in use + public EventFiringTargetLocator(EventFiringWebDriver driver) { - private readonly ITimeouts wrappedTimeouts; + this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.wrappedLocator = this.parentDriver.WrappedDriver.SwitchTo(); + } - /// - /// Initializes a new instance of the class - /// - /// The object to wrap. - public EventFiringTimeouts(IOptions options) + /// + /// Move to a different frame using its index + /// + /// The index of the + /// A WebDriver instance that is currently in use + public IWebDriver Frame(int frameIndex) + { + IWebDriver driver; + try { - this.wrappedTimeouts = options.Timeouts(); + driver = this.wrappedLocator.Frame(frameIndex); } - - /// - /// Gets or sets the implicit wait timeout, which is the amount of time the - /// driver should wait when searching for an element if it is not immediately - /// present. - /// - /// - /// When searching for a single element, the driver should poll the page - /// until the element has been found, or this timeout expires before throwing - /// a . When searching for multiple elements, - /// the driver should poll the page until at least one element has been found - /// or this timeout has expired. - /// - /// Increasing the implicit wait timeout should be used judiciously as it - /// will have an adverse effect on test run time, especially when used with - /// slower location strategies like XPath. - /// - /// - public TimeSpan ImplicitWait + catch (Exception ex) { - get => this.wrappedTimeouts.ImplicitWait; - set => this.wrappedTimeouts.ImplicitWait = value; + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets or sets the asynchronous script timeout, which is the amount - /// of time the driver should wait when executing JavaScript asynchronously. - /// This timeout only affects the - /// method. - /// - public TimeSpan AsynchronousJavaScript + return driver; + } + + /// + /// Move to different frame using its name + /// + /// name of the frame + /// A WebDriver instance that is currently in use + public IWebDriver Frame(string frameName) + { + IWebDriver driver; + try { - get => this.wrappedTimeouts.AsynchronousJavaScript; - set => this.wrappedTimeouts.AsynchronousJavaScript = value; + driver = this.wrappedLocator.Frame(frameName); } - - /// - /// Gets or sets the page load timeout, which is the amount of time the driver - /// should wait for a page to load when setting the - /// property. - /// - public TimeSpan PageLoad + catch (Exception ex) { - get => this.wrappedTimeouts.PageLoad; - set => this.wrappedTimeouts.PageLoad = value; + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + + return driver; } /// - /// EventFiringWebElement allows you to have access to specific items that are found on the page + /// Move to a frame element. /// - private class EventFiringWebElement : ITakesScreenshot, IWebElement, IWrapsElement, IWrapsDriver + /// a previously found FRAME or IFRAME element. + /// A WebDriver instance that is currently in use. + public IWebDriver Frame(IWebElement frameElement) { - private readonly EventFiringWebDriver parentDriver; - - /// - /// Initializes a new instance of the class. - /// - /// The instance hosting this element. - /// The to wrap for event firing. - public EventFiringWebElement(EventFiringWebDriver driver, IWebElement element) + IWebDriver driver; + try { - this.WrappedElement = element ?? throw new ArgumentNullException(nameof(element)); - this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); + IWrapsElement wrapper = (IWrapsElement)frameElement; + driver = this.wrappedLocator.Frame(wrapper.WrappedElement); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets the underlying wrapped . - /// - public IWebElement WrappedElement { get; } - - /// - /// Gets the underlying parent wrapped - /// - public IWebDriver WrappedDriver => this.parentDriver; + return driver; + } - /// - /// Gets the DOM Tag of element - /// - public string TagName + /// + /// Select the parent frame of the currently selected frame. + /// + /// An instance focused on the specified frame. + public IWebDriver ParentFrame() + { + IWebDriver driver; + try { - get - { - string tagName; - try - { - tagName = this.WrappedElement.TagName; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return tagName; - } + driver = this.wrappedLocator.ParentFrame(); } - - /// - /// Gets the text from the element - /// - public string Text + catch (Exception ex) { - get - { - string text; - try - { - text = this.WrappedElement.Text; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return text; - } + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets a value indicating whether an element is currently enabled - /// - public bool Enabled + return driver; + } + + /// + /// Change to the Window by passing in the name + /// + /// name of the window that you wish to move to + /// A WebDriver instance that is currently in use + public IWebDriver Window(string windowName) + { + IWebDriver driver; + try { - get - { - bool enabled; - try - { - enabled = this.WrappedElement.Enabled; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return enabled; - } + driver = this.wrappedLocator.Window(windowName); } - - /// - /// Gets a value indicating whether this element is selected or not. This operation only applies to input elements such as checkboxes, options in a select and radio buttons. - /// - public bool Selected + catch (Exception ex) { - get - { - bool selected; - try - { - selected = this.WrappedElement.Selected; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return selected; - } + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets the Location of an element and returns a Point object - /// - public Point Location + return driver; + } + + /// + /// Creates a new browser window and switches the focus for future commands + /// of this driver to the new window. + /// + /// The type of new browser window to be created. + /// The created window is not guaranteed to be of the requested type; if + /// the driver does not support the requested type, a new browser window + /// will be created of whatever type the driver does support. + /// An instance focused on the new browser. + public IWebDriver NewWindow(WindowType typeHint) + { + IWebDriver driver; + try { - get - { - Point location; - try - { - location = this.WrappedElement.Location; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return location; - } + driver = this.wrappedLocator.NewWindow(typeHint); } - - /// - /// Gets the of the element on the page - /// - public Size Size + catch (Exception ex) { - get - { - Size size; - try - { - size = this.WrappedElement.Size; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return size; - } + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets a value indicating whether the element is currently being displayed - /// - public bool Displayed + return driver; + } + + /// + /// Change the active frame to the default + /// + /// Element of the default + public IWebDriver DefaultContent() + { + IWebDriver driver; + try { - get - { - bool displayed; - try - { - displayed = this.WrappedElement.Displayed; - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } - - return displayed; - } + driver = this.wrappedLocator.DefaultContent(); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Gets the underlying EventFiringWebDriver for this element. - /// - protected EventFiringWebDriver ParentDriver => this.parentDriver; + return driver; + } - /// - /// Method to clear the text out of an Input element - /// - public void Clear() + /// + /// Finds the active element on the page and returns it + /// + /// Element that is active + public IWebElement ActiveElement() + { + IWebElement element; + try { - try - { - WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, null); - this.parentDriver.OnElementValueChanging(e); - this.WrappedElement.Clear(); - this.parentDriver.OnElementValueChanged(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + element = this.wrappedLocator.ActiveElement(); } - - /// - /// Method for sending native key strokes to the browser - /// - /// String containing what you would like to type onto the screen - public void SendKeys(string text) + catch (Exception ex) { - try - { - WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, text); - this.parentDriver.OnElementValueChanging(e); - this.WrappedElement.SendKeys(text); - this.parentDriver.OnElementValueChanged(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. - /// If this causes the current page to change, then this method will block until the new page is loaded. - /// - public void Submit() + return element; + } + + /// + /// Switches to the currently active modal dialog for this particular driver instance. + /// + /// A handle to the dialog. + public IAlert Alert() + { + IAlert alert; + try { - try - { - this.WrappedElement.Submit(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + alert = this.wrappedLocator.Alert(); } - - /// - /// Click this element. If this causes a new page to load, this method will block until - /// the page has loaded. At this point, you should discard all references to this element - /// and any further operations performed on this element will have undefined behavior unless - /// you know that the element and the page will still be present. If this element is not - /// clickable, then this operation is a no-op since it's pretty common for someone to - /// accidentally miss the target when clicking in Real Life - /// - public void Click() + catch (Exception ex) { - try - { - WebElementEventArgs e = new WebElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); - this.parentDriver.OnElementClicking(e); - this.WrappedElement.Click(); - this.parentDriver.OnElementClicked(e); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. If this causes the current page to change, then this method will block until the new page is loaded. - /// - /// Attribute you wish to get details of - /// The attribute's current value or null if the value is not set. - public string? GetAttribute(string attributeName) + return alert; + } + } + + /// + /// Defines the interface through which the user can define timeouts. + /// + private class EventFiringTimeouts : ITimeouts + { + private readonly ITimeouts wrappedTimeouts; + + /// + /// Initializes a new instance of the class + /// + /// The object to wrap. + public EventFiringTimeouts(IOptions options) + { + this.wrappedTimeouts = options.Timeouts(); + } + + /// + /// Gets or sets the implicit wait timeout, which is the amount of time the + /// driver should wait when searching for an element if it is not immediately + /// present. + /// + /// + /// When searching for a single element, the driver should poll the page + /// until the element has been found, or this timeout expires before throwing + /// a . When searching for multiple elements, + /// the driver should poll the page until at least one element has been found + /// or this timeout has expired. + /// + /// Increasing the implicit wait timeout should be used judiciously as it + /// will have an adverse effect on test run time, especially when used with + /// slower location strategies like XPath. + /// + /// + public TimeSpan ImplicitWait + { + get => this.wrappedTimeouts.ImplicitWait; + set => this.wrappedTimeouts.ImplicitWait = value; + } + + /// + /// Gets or sets the asynchronous script timeout, which is the amount + /// of time the driver should wait when executing JavaScript asynchronously. + /// This timeout only affects the + /// method. + /// + public TimeSpan AsynchronousJavaScript + { + get => this.wrappedTimeouts.AsynchronousJavaScript; + set => this.wrappedTimeouts.AsynchronousJavaScript = value; + } + + /// + /// Gets or sets the page load timeout, which is the amount of time the driver + /// should wait for a page to load when setting the + /// property. + /// + public TimeSpan PageLoad + { + get => this.wrappedTimeouts.PageLoad; + set => this.wrappedTimeouts.PageLoad = value; + } + } + + /// + /// EventFiringWebElement allows you to have access to specific items that are found on the page + /// + private class EventFiringWebElement : ITakesScreenshot, IWebElement, IWrapsElement, IWrapsDriver + { + private readonly EventFiringWebDriver parentDriver; + + /// + /// Initializes a new instance of the class. + /// + /// The instance hosting this element. + /// The to wrap for event firing. + public EventFiringWebElement(EventFiringWebDriver driver, IWebElement element) + { + this.WrappedElement = element ?? throw new ArgumentNullException(nameof(element)); + this.parentDriver = driver ?? throw new ArgumentNullException(nameof(driver)); + } + + /// + /// Gets the underlying wrapped . + /// + public IWebElement WrappedElement { get; } + + /// + /// Gets the underlying parent wrapped + /// + public IWebDriver WrappedDriver => this.parentDriver; + + /// + /// Gets the DOM Tag of element + /// + public string TagName + { + get { + string tagName; try { - return this.WrappedElement.GetAttribute(attributeName); + tagName = this.WrappedElement.TagName; } catch (Exception ex) { this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); throw; } + + return tagName; } + } - /// - /// Gets the value of a declared HTML attribute of this element. - /// - /// The name of the HTML attribute to get the value of. - /// The HTML attribute's current value. Returns a if the - /// value is not set or the declared attribute does not exist. - /// - /// As opposed to the method, this method - /// only returns attributes declared in the element's HTML markup. To access the value - /// of an IDL property of the element, either use the - /// method or the method. - /// - public string? GetDomAttribute(string attributeName) + /// + /// Gets the text from the element + /// + public string Text + { + get { + string text; try { - return this.WrappedElement.GetDomAttribute(attributeName); + text = this.WrappedElement.Text; } catch (Exception ex) { this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); throw; } + + return text; } + } - /// - /// Gets the value of a JavaScript property of this element. - /// - /// The name of the JavaScript property to get the value of. - /// The JavaScript property's current value. Returns a if the - /// value is not set or the property does not exist. - public string? GetDomProperty(string propertyName) + /// + /// Gets a value indicating whether an element is currently enabled + /// + public bool Enabled + { + get { + bool enabled; try { - return this.WrappedElement.GetDomProperty(propertyName); + enabled = this.WrappedElement.Enabled; } catch (Exception ex) { this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); throw; } + + return enabled; } + } - /// - /// Method to return the value of a CSS Property - /// - /// CSS property key - /// string value of the CSS property - public string GetCssValue(string propertyName) + /// + /// Gets a value indicating whether this element is selected or not. This operation only applies to input elements such as checkboxes, options in a select and radio buttons. + /// + public bool Selected + { + get { - string cssValue; + bool selected; try { - cssValue = this.WrappedElement.GetCssValue(propertyName); + selected = this.WrappedElement.Selected; } catch (Exception ex) { @@ -1664,46 +1429,43 @@ public string GetCssValue(string propertyName) throw; } - return cssValue; + return selected; } + } - /// - /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. - /// - /// Thrown when this element does not have a shadow root. - /// A shadow root representation. - public ISearchContext GetShadowRoot() + /// + /// Gets the Location of an element and returns a Point object + /// + public Point Location + { + get { + Point location; try { - GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); - this.parentDriver.OnGettingShadowRoot(e); - ISearchContext shadowRoot = this.WrappedElement.GetShadowRoot(); - this.parentDriver.OnGetShadowRootCompleted(e); - return new EventFiringShadowRoot(this.parentDriver, shadowRoot); + location = this.WrappedElement.Location; } catch (Exception ex) { this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); throw; } + + return location; } + } - /// - /// Finds the first element in the page that matches the object - /// - /// By mechanism to find the element - /// IWebElement object so that you can interaction that object - public IWebElement FindElement(By by) + /// + /// Gets the of the element on the page + /// + public Size Size + { + get { - IWebElement wrappedElement; + Size size; try { - FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); - this.parentDriver.OnFindingElement(e); - IWebElement element = this.WrappedElement.FindElement(by); - this.parentDriver.OnFindElementCompleted(e); - wrappedElement = this.parentDriver.WrapElement(element); + size = this.WrappedElement.Size; } catch (Exception ex) { @@ -1711,190 +1473,427 @@ public IWebElement FindElement(By by) throw; } - return wrappedElement; + return size; } + } - /// - /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page - /// - /// By mechanism to find the element - /// ReadOnlyCollection of IWebElement - public ReadOnlyCollection FindElements(By by) + /// + /// Gets a value indicating whether the element is currently being displayed + /// + public bool Displayed + { + get { + bool displayed; try { - FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); - this.parentDriver.OnFindingElement(e); - ReadOnlyCollection elements = this.WrappedElement.FindElements(by); - this.parentDriver.OnFindElementCompleted(e); - - List wrappedElementList = new List(elements.Count); - foreach (IWebElement element in elements) - { - IWebElement wrappedElement = this.parentDriver.WrapElement(element); - wrappedElementList.Add(wrappedElement); - } - - return wrappedElementList.AsReadOnly(); + displayed = this.WrappedElement.Displayed; } catch (Exception ex) { this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); throw; } + + return displayed; } + } - /// - /// Gets a object representing the image of the page on the screen. - /// - /// A object containing the image. - public Screenshot GetScreenshot() + /// + /// Gets the underlying EventFiringWebDriver for this element. + /// + protected EventFiringWebDriver ParentDriver => this.parentDriver; + + /// + /// Method to clear the text out of an Input element + /// + public void Clear() + { + try { - if (this.WrappedElement is not ITakesScreenshot screenshotDriver) - { - throw new NotSupportedException("Underlying element instance does not support taking screenshots"); - } + WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, null); + this.parentDriver.OnElementValueChanging(e); + this.WrappedElement.Clear(); + this.parentDriver.OnElementValueChanged(e); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + } - return screenshotDriver.GetScreenshot(); + /// + /// Method for sending native key strokes to the browser + /// + /// String containing what you would like to type onto the screen + public void SendKeys(string text) + { + try + { + WebElementValueEventArgs e = new WebElementValueEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, text); + this.parentDriver.OnElementValueChanging(e); + this.WrappedElement.SendKeys(text); + this.parentDriver.OnElementValueChanged(e); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare to the current . - /// if the specified is equal to the current ; otherwise, . - public override bool Equals(object obj) + /// + /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. + /// If this causes the current page to change, then this method will block until the new page is loaded. + /// + public void Submit() + { + try { - if (obj is not IWebElement other) - { - return false; - } + this.WrappedElement.Submit(); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + } - if (other is IWrapsElement otherWrapper) - { - other = otherWrapper.WrappedElement; - } + /// + /// Click this element. If this causes a new page to load, this method will block until + /// the page has loaded. At this point, you should discard all references to this element + /// and any further operations performed on this element will have undefined behavior unless + /// you know that the element and the page will still be present. If this element is not + /// clickable, then this operation is a no-op since it's pretty common for someone to + /// accidentally miss the target when clicking in Real Life + /// + public void Click() + { + try + { + WebElementEventArgs e = new WebElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); + this.parentDriver.OnElementClicking(e); + this.WrappedElement.Click(); + this.parentDriver.OnElementClicked(e); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + } + + /// + /// If this current element is a form, or an element within a form, then this will be submitted to the remote server. If this causes the current page to change, then this method will block until the new page is loaded. + /// + /// Attribute you wish to get details of + /// The attribute's current value or null if the value is not set. + public string? GetAttribute(string attributeName) + { + try + { + return this.WrappedElement.GetAttribute(attributeName); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + } - return WrappedElement.Equals(other); + /// + /// Gets the value of a declared HTML attribute of this element. + /// + /// The name of the HTML attribute to get the value of. + /// The HTML attribute's current value. Returns a if the + /// value is not set or the declared attribute does not exist. + /// + /// As opposed to the method, this method + /// only returns attributes declared in the element's HTML markup. To access the value + /// of an IDL property of the element, either use the + /// method or the method. + /// + public string? GetDomAttribute(string attributeName) + { + try + { + return this.WrappedElement.GetDomAttribute(attributeName); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Return the hash code for this . - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() + /// + /// Gets the value of a JavaScript property of this element. + /// + /// The name of the JavaScript property to get the value of. + /// The JavaScript property's current value. Returns a if the + /// value is not set or the property does not exist. + public string? GetDomProperty(string propertyName) + { + try + { + return this.WrappedElement.GetDomProperty(propertyName); + } + catch (Exception ex) { - return this.WrappedElement.GetHashCode(); + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } } /// - /// EventFiringShadowElement allows you to have access to specific shadow elements + /// Method to return the value of a CSS Property /// - private class EventFiringShadowRoot : ISearchContext, IWrapsDriver + /// CSS property key + /// string value of the CSS property + public string GetCssValue(string propertyName) { - private readonly EventFiringWebDriver parentDriver; + string cssValue; + try + { + cssValue = this.WrappedElement.GetCssValue(propertyName); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } + + return cssValue; + } - /// - /// Initializes a new instance of the class. - /// - /// The instance hosting this element. - /// The to wrap for event firing. - public EventFiringShadowRoot(EventFiringWebDriver driver, ISearchContext searchContext) + /// + /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. + /// + /// Thrown when this element does not have a shadow root. + /// A shadow root representation. + public ISearchContext GetShadowRoot() + { + try + { + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement); + this.parentDriver.OnGettingShadowRoot(e); + ISearchContext shadowRoot = this.WrappedElement.GetShadowRoot(); + this.parentDriver.OnGetShadowRootCompleted(e); + return new EventFiringShadowRoot(this.parentDriver, shadowRoot); + } + catch (Exception ex) { - this.WrappedSearchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); - this.parentDriver = driver; + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Gets the underlying wrapped . - /// - public ISearchContext WrappedSearchContext { get; } + /// + /// Finds the first element in the page that matches the object + /// + /// By mechanism to find the element + /// IWebElement object so that you can interaction that object + public IWebElement FindElement(By by) + { + IWebElement wrappedElement; + try + { + FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); + this.parentDriver.OnFindingElement(e); + IWebElement element = this.WrappedElement.FindElement(by); + this.parentDriver.OnFindElementCompleted(e); + wrappedElement = this.parentDriver.WrapElement(element); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; + } - /// - /// Gets the underlying parent wrapped - /// - public IWebDriver WrappedDriver => this.parentDriver; + return wrappedElement; + } - /// - /// Finds the first element in the page that matches the object - /// - /// By mechanism to find the element - /// IWebElement object so that you can interaction that object - public IWebElement FindElement(By by) + /// + /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page + /// + /// By mechanism to find the element + /// ReadOnlyCollection of IWebElement + public ReadOnlyCollection FindElements(By by) + { + try { - IWebElement wrappedElement; - try - { - GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); - this.parentDriver.OnGettingShadowRoot(e); - IWebElement element = this.WrappedSearchContext.FindElement(by); - this.parentDriver.OnGetShadowRootCompleted(e); - wrappedElement = new EventFiringWebElement(this.parentDriver, element); - } - catch (Exception ex) + FindElementEventArgs e = new FindElementEventArgs(this.parentDriver.WrappedDriver, this.WrappedElement, by); + this.parentDriver.OnFindingElement(e); + ReadOnlyCollection elements = this.WrappedElement.FindElements(by); + this.parentDriver.OnFindElementCompleted(e); + + List wrappedElementList = new List(elements.Count); + foreach (IWebElement element in elements) { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; + IWebElement wrappedElement = this.parentDriver.WrapElement(element); + wrappedElementList.Add(wrappedElement); } - return wrappedElement; + return wrappedElementList.AsReadOnly(); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } + } - /// - /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page - /// - /// By mechanism to find the element - /// ReadOnlyCollection of IWebElement - public ReadOnlyCollection FindElements(By by) + /// + /// Gets a object representing the image of the page on the screen. + /// + /// A object containing the image. + public Screenshot GetScreenshot() + { + if (this.WrappedElement is not ITakesScreenshot screenshotDriver) { - try - { - GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); - this.parentDriver.OnGettingShadowRoot(e); - ReadOnlyCollection elements = this.WrappedSearchContext.FindElements(by); - this.parentDriver.OnGetShadowRootCompleted(e); - - List wrappedElementList = new List(elements.Count); - foreach (IWebElement element in elements) - { - IWebElement wrappedElement = this.parentDriver.WrapElement(element); - wrappedElementList.Add(wrappedElement); - } - - return wrappedElementList.AsReadOnly(); - } - catch (Exception ex) - { - this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); - throw; - } + throw new NotSupportedException("Underlying element instance does not support taking screenshots"); + } + + return screenshotDriver.GetScreenshot(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare to the current . + /// if the specified is equal to the current ; otherwise, . + public override bool Equals(object obj) + { + if (obj is not IWebElement other) + { + return false; + } + + if (other is IWrapsElement otherWrapper) + { + other = otherWrapper.WrappedElement; + } + + return WrappedElement.Equals(other); + } + + /// + /// Return the hash code for this . + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return this.WrappedElement.GetHashCode(); + } + } + + /// + /// EventFiringShadowElement allows you to have access to specific shadow elements + /// + private class EventFiringShadowRoot : ISearchContext, IWrapsDriver + { + private readonly EventFiringWebDriver parentDriver; + + /// + /// Initializes a new instance of the class. + /// + /// The instance hosting this element. + /// The to wrap for event firing. + public EventFiringShadowRoot(EventFiringWebDriver driver, ISearchContext searchContext) + { + this.WrappedSearchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); + this.parentDriver = driver; + } + + /// + /// Gets the underlying wrapped . + /// + public ISearchContext WrappedSearchContext { get; } + + /// + /// Gets the underlying parent wrapped + /// + public IWebDriver WrappedDriver => this.parentDriver; + /// + /// Finds the first element in the page that matches the object + /// + /// By mechanism to find the element + /// IWebElement object so that you can interaction that object + public IWebElement FindElement(By by) + { + IWebElement wrappedElement; + try + { + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); + this.parentDriver.OnGettingShadowRoot(e); + IWebElement element = this.WrappedSearchContext.FindElement(by); + this.parentDriver.OnGetShadowRootCompleted(e); + wrappedElement = new EventFiringWebElement(this.parentDriver, element); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare to the current . - /// if the specified is equal to the current ; otherwise, . - public override bool Equals(object obj) + return wrappedElement; + } + + /// + /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page + /// + /// By mechanism to find the element + /// ReadOnlyCollection of IWebElement + public ReadOnlyCollection FindElements(By by) + { + try { - if (obj is not ISearchContext other) + GetShadowRootEventArgs e = new GetShadowRootEventArgs(this.parentDriver.WrappedDriver, this.WrappedSearchContext); + this.parentDriver.OnGettingShadowRoot(e); + ReadOnlyCollection elements = this.WrappedSearchContext.FindElements(by); + this.parentDriver.OnGetShadowRootCompleted(e); + + List wrappedElementList = new List(elements.Count); + foreach (IWebElement element in elements) { - return false; + IWebElement wrappedElement = this.parentDriver.WrapElement(element); + wrappedElementList.Add(wrappedElement); } - return WrappedSearchContext.Equals(other); + return wrappedElementList.AsReadOnly(); + } + catch (Exception ex) + { + this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex)); + throw; } - /// - /// Return the hash code for this . - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare to the current . + /// if the specified is equal to the current ; otherwise, . + public override bool Equals(object obj) + { + if (obj is not ISearchContext other) { - return this.WrappedSearchContext.GetHashCode(); + return false; } + + return WrappedSearchContext.Equals(other); + } + + /// + /// Return the hash code for this . + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return this.WrappedSearchContext.GetHashCode(); } } } diff --git a/dotnet/src/support/Events/FindElementEventArgs.cs b/dotnet/src/support/Events/FindElementEventArgs.cs index b89e4779acffe..c28ad02569d90 100644 --- a/dotnet/src/support/Events/FindElementEventArgs.cs +++ b/dotnet/src/support/Events/FindElementEventArgs.cs @@ -19,51 +19,50 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events related to finding elements. +/// +public class FindElementEventArgs : EventArgs { /// - /// Provides data for events related to finding elements. + /// Initializes a new instance of the class. /// - public class FindElementEventArgs : EventArgs + /// The WebDriver instance used in finding elements. + /// The object containing the method used to find elements. + /// If or are . + public FindElementEventArgs(IWebDriver driver, By method) + : this(driver, null, method) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used in finding elements. - /// The object containing the method used to find elements. - /// If or are . - public FindElementEventArgs(IWebDriver driver, By method) - : this(driver, null, method) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used in finding elements. - /// The parent element used as the context for the search, or if none exists. - /// The object containing the method used to find elements. - /// If or are . - public FindElementEventArgs(IWebDriver driver, IWebElement? element, By method) - { - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.Element = element; - this.FindMethod = method ?? throw new ArgumentNullException(nameof(method)); - } + /// + /// Initializes a new instance of the class. + /// + /// The WebDriver instance used in finding elements. + /// The parent element used as the context for the search, or if none exists. + /// The object containing the method used to find elements. + /// If or are . + public FindElementEventArgs(IWebDriver driver, IWebElement? element, By method) + { + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.Element = element; + this.FindMethod = method ?? throw new ArgumentNullException(nameof(method)); + } - /// - /// Gets the WebDriver instance used in finding elements. - /// - public IWebDriver Driver { get; } + /// + /// Gets the WebDriver instance used in finding elements. + /// + public IWebDriver Driver { get; } - /// - /// Gets the parent element used as the context for the search, or if no element is associated. - /// - public IWebElement? Element { get; } + /// + /// Gets the parent element used as the context for the search, or if no element is associated. + /// + public IWebElement? Element { get; } - /// - /// Gets the object containing the method used to find elements. - /// - public By FindMethod { get; } - } + /// + /// Gets the object containing the method used to find elements. + /// + public By FindMethod { get; } } diff --git a/dotnet/src/support/Events/GetShadowRootEventArgs.cs b/dotnet/src/support/Events/GetShadowRootEventArgs.cs index 95c989ef0f7f2..a028f3c806125 100644 --- a/dotnet/src/support/Events/GetShadowRootEventArgs.cs +++ b/dotnet/src/support/Events/GetShadowRootEventArgs.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events related to getting shadow root of the web element. +/// +public class GetShadowRootEventArgs : EventArgs { /// - /// Provides data for events related to getting shadow root of the web element. + /// Initializes a new instance of the class. /// - public class GetShadowRootEventArgs : EventArgs + /// The WebDriver instance used in the current context. + /// The parent searc context used as the context for getting shadow root. + /// If or are . + public GetShadowRootEventArgs(IWebDriver driver, ISearchContext searchContext) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used in the current context. - /// The parent searc context used as the context for getting shadow root. - /// If or are . - public GetShadowRootEventArgs(IWebDriver driver, ISearchContext searchContext) - { - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.SearchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); - } + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.SearchContext = searchContext ?? throw new ArgumentNullException(nameof(searchContext)); + } - /// - /// Gets the WebDriver instance used in the current context. - /// - public IWebDriver Driver { get; } + /// + /// Gets the WebDriver instance used in the current context. + /// + public IWebDriver Driver { get; } - /// - /// Gets the parent search context used as the context for getting shadow root. - /// - public ISearchContext SearchContext { get; } - } + /// + /// Gets the parent search context used as the context for getting shadow root. + /// + public ISearchContext SearchContext { get; } } diff --git a/dotnet/src/support/Events/WebDriverExceptionEventArgs.cs b/dotnet/src/support/Events/WebDriverExceptionEventArgs.cs index 75aa790caeaf5..03dcd5247f954 100644 --- a/dotnet/src/support/Events/WebDriverExceptionEventArgs.cs +++ b/dotnet/src/support/Events/WebDriverExceptionEventArgs.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events relating to exception handling. +/// +public class WebDriverExceptionEventArgs : EventArgs { /// - /// Provides data for events relating to exception handling. + /// Initializes a new instance of the class. /// - public class WebDriverExceptionEventArgs : EventArgs + /// The WebDriver instance throwing the exception. + /// The exception thrown by the driver. + /// If or are . + public WebDriverExceptionEventArgs(IWebDriver driver, Exception thrownException) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance throwing the exception. - /// The exception thrown by the driver. - /// If or are . - public WebDriverExceptionEventArgs(IWebDriver driver, Exception thrownException) - { - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.ThrownException = thrownException ?? throw new ArgumentNullException(nameof(thrownException)); - } + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.ThrownException = thrownException ?? throw new ArgumentNullException(nameof(thrownException)); + } - /// - /// Gets the exception thrown by the driver. - /// - public Exception ThrownException { get; } + /// + /// Gets the exception thrown by the driver. + /// + public Exception ThrownException { get; } - /// - /// Gets the WebDriver instance. - /// - public IWebDriver Driver { get; } - } + /// + /// Gets the WebDriver instance. + /// + public IWebDriver Driver { get; } } diff --git a/dotnet/src/support/Events/WebDriverNavigationEventArgs.cs b/dotnet/src/support/Events/WebDriverNavigationEventArgs.cs index ce6af33399f2b..4bd01d29cc1e0 100644 --- a/dotnet/src/support/Events/WebDriverNavigationEventArgs.cs +++ b/dotnet/src/support/Events/WebDriverNavigationEventArgs.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events relating to navigation. +/// +public class WebDriverNavigationEventArgs : EventArgs { /// - /// Provides data for events relating to navigation. + /// Initializes a new instance of the class. /// - public class WebDriverNavigationEventArgs : EventArgs + /// The WebDriver instance used in navigation. + /// If is . + public WebDriverNavigationEventArgs(IWebDriver driver) + : this(driver, null) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used in navigation. - /// If is . - public WebDriverNavigationEventArgs(IWebDriver driver) - : this(driver, null) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used in navigation. - /// The URL navigated to by the driver, or if none exists. - /// If is . - public WebDriverNavigationEventArgs(IWebDriver driver, string? url) - { - this.Url = url; - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + /// + /// Initializes a new instance of the class. + /// + /// The WebDriver instance used in navigation. + /// The URL navigated to by the driver, or if none exists. + /// If is . + public WebDriverNavigationEventArgs(IWebDriver driver, string? url) + { + this.Url = url; + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - /// - /// Gets the URL navigated to by the driver, or if no URL could be determined. - /// - public string? Url { get; } + /// + /// Gets the URL navigated to by the driver, or if no URL could be determined. + /// + public string? Url { get; } - /// - /// Gets the WebDriver instance used in navigation. - /// - public IWebDriver Driver { get; } - } + /// + /// Gets the WebDriver instance used in navigation. + /// + public IWebDriver Driver { get; } } diff --git a/dotnet/src/support/Events/WebDriverScriptEventArgs.cs b/dotnet/src/support/Events/WebDriverScriptEventArgs.cs index 2251376293da3..be198ecf62945 100644 --- a/dotnet/src/support/Events/WebDriverScriptEventArgs.cs +++ b/dotnet/src/support/Events/WebDriverScriptEventArgs.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events relating to executing JavaScript. +/// +public class WebDriverScriptEventArgs : EventArgs { /// - /// Provides data for events relating to executing JavaScript. + /// Initializes a new instance of the class. /// - public class WebDriverScriptEventArgs : EventArgs + /// The WebDriver instance used to execute the script. + /// The script executed by the driver. + /// If or are . + public WebDriverScriptEventArgs(IWebDriver driver, string script) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used to execute the script. - /// The script executed by the driver. - /// If or are . - public WebDriverScriptEventArgs(IWebDriver driver, string script) - { - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.Script = script ?? throw new ArgumentNullException(nameof(script)); - } + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.Script = script ?? throw new ArgumentNullException(nameof(script)); + } - /// - /// Gets the WebDriver instance used to execute the script. - /// - public IWebDriver Driver { get; } + /// + /// Gets the WebDriver instance used to execute the script. + /// + public IWebDriver Driver { get; } - /// - /// Gets the script executed by the driver. - /// - public string Script { get; } - } + /// + /// Gets the script executed by the driver. + /// + public string Script { get; } } diff --git a/dotnet/src/support/Events/WebElementEventArgs.cs b/dotnet/src/support/Events/WebElementEventArgs.cs index 383b3f593f5f3..6b427c1d231fd 100644 --- a/dotnet/src/support/Events/WebElementEventArgs.cs +++ b/dotnet/src/support/Events/WebElementEventArgs.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events relating to elements. +/// +public class WebElementEventArgs : EventArgs { /// - /// Provides data for events relating to elements. + /// Initializes a new instance of the class. /// - public class WebElementEventArgs : EventArgs + /// The WebDriver instance used for the action. + /// The element used for the action. + /// If or are . + public WebElementEventArgs(IWebDriver driver, IWebElement element) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used for the action. - /// The element used for the action. - /// If or are . - public WebElementEventArgs(IWebDriver driver, IWebElement element) - { - this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.Element = element ?? throw new ArgumentNullException(nameof(element)); - } + this.Driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.Element = element ?? throw new ArgumentNullException(nameof(element)); + } - /// - /// Gets the WebDriver instance used for the action. - /// - public IWebDriver Driver { get; } + /// + /// Gets the WebDriver instance used for the action. + /// + public IWebDriver Driver { get; } - /// - /// Gets the element used for the action. - /// - public IWebElement Element { get; } - } + /// + /// Gets the element used for the action. + /// + public IWebElement Element { get; } } diff --git a/dotnet/src/support/Events/WebElementValueEventArgs.cs b/dotnet/src/support/Events/WebElementValueEventArgs.cs index 76ef60e0f72d9..774aeea6725ce 100644 --- a/dotnet/src/support/Events/WebElementValueEventArgs.cs +++ b/dotnet/src/support/Events/WebElementValueEventArgs.cs @@ -19,29 +19,28 @@ using System; -namespace OpenQA.Selenium.Support.Events +namespace OpenQA.Selenium.Support.Events; + +/// +/// Provides data for events related to finding elements. +/// +public class WebElementValueEventArgs : WebElementEventArgs { /// - /// Provides data for events related to finding elements. + /// Initializes a new instance of the class. /// - public class WebElementValueEventArgs : WebElementEventArgs + /// The WebDriver instance used for the action. + /// The element used for the action. + /// The new value for the element. + /// If or are . + public WebElementValueEventArgs(IWebDriver driver, IWebElement element, string? value) + : base(driver, element) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used for the action. - /// The element used for the action. - /// The new value for the element. - /// If or are . - public WebElementValueEventArgs(IWebDriver driver, IWebElement element, string? value) - : base(driver, element) - { - this.Value = value; - } - - /// - /// Gets the Value that is written to the element. - /// - public string? Value { get; } + this.Value = value; } + + /// + /// Gets the Value that is written to the element. + /// + public string? Value { get; } } diff --git a/dotnet/src/support/Extensions/WebDriverExtensions.cs b/dotnet/src/support/Extensions/WebDriverExtensions.cs index 9b0274b6afded..8ef995719dfda 100644 --- a/dotnet/src/support/Extensions/WebDriverExtensions.cs +++ b/dotnet/src/support/Extensions/WebDriverExtensions.cs @@ -20,127 +20,126 @@ using System; using System.Reflection; -namespace OpenQA.Selenium.Support.Extensions +namespace OpenQA.Selenium.Support.Extensions; + +/// +/// Provides extension methods for convenience in using WebDriver. +/// +public static class WebDriverExtensions { /// - /// Provides extension methods for convenience in using WebDriver. + /// Gets a object representing the image of the page on the screen. /// - public static class WebDriverExtensions + /// The driver instance to extend. + /// A object containing the image. + /// Thrown if this instance + /// does not implement , or the capabilities of the driver + /// indicate that it cannot take screenshots. + public static Screenshot TakeScreenshot(this IWebDriver driver) { - /// - /// Gets a object representing the image of the page on the screen. - /// - /// The driver instance to extend. - /// A object containing the image. - /// Thrown if this instance - /// does not implement , or the capabilities of the driver - /// indicate that it cannot take screenshots. - public static Screenshot TakeScreenshot(this IWebDriver driver) + ITakesScreenshot? screenshotDriver = GetDriverAs(driver); + if (screenshotDriver is null) { - ITakesScreenshot? screenshotDriver = GetDriverAs(driver); - if (screenshotDriver is null) - { - IHasCapabilities capabilitiesDriver = driver as IHasCapabilities - ?? throw new WebDriverException("Driver does not implement ITakesScreenshot or IHasCapabilities"); - - if (capabilitiesDriver.Capabilities.GetCapability(CapabilityType.TakesScreenshot) is not true) - { - throw new WebDriverException("Driver capabilities do not support taking screenshots"); - } + IHasCapabilities capabilitiesDriver = driver as IHasCapabilities + ?? throw new WebDriverException("Driver does not implement ITakesScreenshot or IHasCapabilities"); - MethodInfo executeMethod = driver.GetType().GetMethod("Execute", BindingFlags.Instance | BindingFlags.NonPublic)!; + if (capabilitiesDriver.Capabilities.GetCapability(CapabilityType.TakesScreenshot) is not true) + { + throw new WebDriverException("Driver capabilities do not support taking screenshots"); + } - object? responseObject = executeMethod.Invoke(driver, new object?[] { DriverCommand.Screenshot, null }); - if (responseObject is not Response screenshotResponse) - { - throw new WebDriverException($"Unexpected failure getting screenshot; response was not in the proper format: {responseObject}"); - } + MethodInfo executeMethod = driver.GetType().GetMethod("Execute", BindingFlags.Instance | BindingFlags.NonPublic)!; - string screenshotResult = screenshotResponse.Value!.ToString(); - return new Screenshot(screenshotResult); + object? responseObject = executeMethod.Invoke(driver, new object?[] { DriverCommand.Screenshot, null }); + if (responseObject is not Response screenshotResponse) + { + throw new WebDriverException($"Unexpected failure getting screenshot; response was not in the proper format: {responseObject}"); } - return screenshotDriver.GetScreenshot(); + string screenshotResult = screenshotResponse.Value!.ToString(); + return new Screenshot(screenshotResult); } - /// - /// Executes JavaScript in the context of the currently selected frame or window - /// - /// The driver instance to extend. - /// The JavaScript code to execute. - /// The arguments to the script. - /// Thrown if this instance - /// does not implement - public static void ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) - { - ExecuteJavaScriptInternal(driver, script, args); - } - - /// - /// Executes JavaScript in the context of the currently selected frame or window - /// - /// Expected return type of the JavaScript execution. - /// The driver instance to extend. - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// Thrown if this instance - /// does not implement , or if the actual return type - /// of the JavaScript execution does not match the expected type. - public static T? ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) - { - var value = ExecuteJavaScriptInternal(driver, script, args); - if (value == null) - { - if (default(T) != null) - { - throw new WebDriverException("Script returned null, but desired type is a non-nullable value type"); - } + return screenshotDriver.GetScreenshot(); + } - return default; - } + /// + /// Executes JavaScript in the context of the currently selected frame or window + /// + /// The driver instance to extend. + /// The JavaScript code to execute. + /// The arguments to the script. + /// Thrown if this instance + /// does not implement + public static void ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) + { + ExecuteJavaScriptInternal(driver, script, args); + } - if (value is T t) + /// + /// Executes JavaScript in the context of the currently selected frame or window + /// + /// Expected return type of the JavaScript execution. + /// The driver instance to extend. + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// Thrown if this instance + /// does not implement , or if the actual return type + /// of the JavaScript execution does not match the expected type. + public static T? ExecuteJavaScript(this IWebDriver driver, string script, params object?[] args) + { + var value = ExecuteJavaScriptInternal(driver, script, args); + if (value == null) + { + if (default(T) != null) { - return t; + throw new WebDriverException("Script returned null, but desired type is a non-nullable value type"); } - try - { - return (T)Convert.ChangeType(value, typeof(T)); - } - catch (Exception exp) - { - throw new WebDriverException("Script returned a value, but the result could not be cast to the desired type", exp); - } + return default; } - private static object? ExecuteJavaScriptInternal(IWebDriver driver, string script, object?[] args) + if (value is T t) { - IJavaScriptExecutor? executor = GetDriverAs(driver) - ?? throw new WebDriverException("Driver does not implement IJavaScriptExecutor"); + return t; + } - return executor.ExecuteScript(script, args); + try + { + return (T)Convert.ChangeType(value, typeof(T)); + } + catch (Exception exp) + { + throw new WebDriverException("Script returned a value, but the result could not be cast to the desired type", exp); } + } - private static T? GetDriverAs(IWebDriver driver) where T : class + private static object? ExecuteJavaScriptInternal(IWebDriver driver, string script, object?[] args) + { + IJavaScriptExecutor? executor = GetDriverAs(driver) + ?? throw new WebDriverException("Driver does not implement IJavaScriptExecutor"); + + return executor.ExecuteScript(script, args); + } + + private static T? GetDriverAs(IWebDriver driver) where T : class + { + T? convertedDriver = driver as T; + if (convertedDriver == null) { - T? convertedDriver = driver as T; - if (convertedDriver == null) + // If the driver doesn't directly implement the desired interface, but does + // implement IWrapsDriver, walk up the hierarchy of wrapped drivers until + // either we find a class that does implement the desired interface, or is + // no longer wrapping a driver. + IWrapsDriver? driverWrapper = driver as IWrapsDriver; + while (convertedDriver == null && driverWrapper != null) { - // If the driver doesn't directly implement the desired interface, but does - // implement IWrapsDriver, walk up the hierarchy of wrapped drivers until - // either we find a class that does implement the desired interface, or is - // no longer wrapping a driver. - IWrapsDriver? driverWrapper = driver as IWrapsDriver; - while (convertedDriver == null && driverWrapper != null) - { - convertedDriver = driverWrapper.WrappedDriver as T; - driverWrapper = driverWrapper.WrappedDriver as IWrapsDriver; - } + convertedDriver = driverWrapper.WrappedDriver as T; + driverWrapper = driverWrapper.WrappedDriver as IWrapsDriver; } - - return convertedDriver; } + + return convertedDriver; } } diff --git a/dotnet/src/support/UI/ILoadableComponent.cs b/dotnet/src/support/UI/ILoadableComponent.cs index 4b86420c1ee17..e69f6f885b07c 100644 --- a/dotnet/src/support/UI/ILoadableComponent.cs +++ b/dotnet/src/support/UI/ILoadableComponent.cs @@ -17,31 +17,30 @@ // under the License. // -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Interface allows for the component to be used in Nested Component scenarios such that the +/// child component class does not have to declare the generic type of the parent explicitly. +/// +/// +/// public class HypotheticalLoadableComponent : LoadableComponent<T> { +/// ILoadableComponent parent; +/// public HypotheticalLoadableComponent(ILoadableComponent parent) { +/// this.parent = parent; +/// } +/// protected void EvaluateLoadedStatus() { //code to determine loaded state } +/// protected void ExecuteLoad() { +/// parent.Load(); //loads the parent +/// //code to load this component +/// } +/// } +/// +public interface ILoadableComponent { /// - /// Interface allows for the component to be used in Nested Component scenarios such that the - /// child component class does not have to declare the generic type of the parent explicitly. + /// Loads the component. /// - /// - /// public class HypotheticalLoadableComponent : LoadableComponent<T> { - /// ILoadableComponent parent; - /// public HypotheticalLoadableComponent(ILoadableComponent parent) { - /// this.parent = parent; - /// } - /// protected void EvaluateLoadedStatus() { //code to determine loaded state } - /// protected void ExecuteLoad() { - /// parent.Load(); //loads the parent - /// //code to load this component - /// } - /// } - /// - public interface ILoadableComponent - { - /// - /// Loads the component. - /// - /// A reference to this . - ILoadableComponent Load(); - } + /// A reference to this . + ILoadableComponent Load(); } diff --git a/dotnet/src/support/UI/LoadableComponentException.cs b/dotnet/src/support/UI/LoadableComponentException.cs index 82bcef512a345..ad5d0fbe444c2 100644 --- a/dotnet/src/support/UI/LoadableComponentException.cs +++ b/dotnet/src/support/UI/LoadableComponentException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// This exception is thrown by to indicate that +/// the component was not successfully loaded. +/// +[Serializable] +public class LoadableComponentException : WebDriverException { /// - /// This exception is thrown by to indicate that - /// the component was not successfully loaded. + /// Initializes a new instance of the class. /// - [Serializable] - public class LoadableComponentException : WebDriverException + public LoadableComponentException() { - /// - /// Initializes a new instance of the class. - /// - public LoadableComponentException() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message of the exception - public LoadableComponentException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message of the exception + public LoadableComponentException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public LoadableComponentException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public LoadableComponentException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/support/UI/LoadableComponent{T}.cs b/dotnet/src/support/UI/LoadableComponent{T}.cs index 14e66fc8b4293..2bf087b4699e9 100644 --- a/dotnet/src/support/UI/LoadableComponent{T}.cs +++ b/dotnet/src/support/UI/LoadableComponent{T}.cs @@ -17,126 +17,125 @@ // under the License. // -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Represents any abstraction of something that can be loaded. +/// This may be an entire web page, or simply a component within that page (such as a login box or menu) or even a service. +/// +/// +/// // Example usage: +/// new MyComponent().Load(); +/// +/// +/// +/// The type to be returned (normally the subclass' type) +/// +/// After the method is called, the component will be loaded and ready for use. +/// Overload the protected Load and IsLoaded members to both load a component and determine if the component is already loaded. +/// +public abstract class LoadableComponent : ILoadableComponent + where T : LoadableComponent { /// - /// Represents any abstraction of something that can be loaded. - /// This may be an entire web page, or simply a component within that page (such as a login box or menu) or even a service. - /// - /// - /// // Example usage: - /// new MyComponent().Load(); - /// - /// + /// Gets or sets the message for the exception thrown when a component cannot be loaded + /// + public virtual string? UnableToLoadMessage { get; set; } + + /// + /// Gets a value indicating whether the component is fully loaded. /// - /// The type to be returned (normally the subclass' type) /// - /// After the method is called, the component will be loaded and ready for use. - /// Overload the protected Load and IsLoaded members to both load a component and determine if the component is already loaded. + /// When the component is loaded, this property will return true or false depending on + /// the execution of to indicate the not loaded state. /// - public abstract class LoadableComponent : ILoadableComponent - where T : LoadableComponent + protected bool IsLoaded { - /// - /// Gets or sets the message for the exception thrown when a component cannot be loaded - /// - public virtual string? UnableToLoadMessage { get; set; } - - /// - /// Gets a value indicating whether the component is fully loaded. - /// - /// - /// When the component is loaded, this property will return true or false depending on - /// the execution of to indicate the not loaded state. - /// - protected bool IsLoaded + get { - get - { - try - { - return this.EvaluateLoadedStatus(); - } - catch (WebDriverException) - { - return false; - } - } - } - - /// - /// Ensure that the component is currently loaded. - /// - /// The loaded component. - /// This is equivalent to the Get() method in Java version. - public virtual T Load() - { - if (this.IsLoaded) + try { - return (T)this; + return this.EvaluateLoadedStatus(); } - else + catch (WebDriverException) { - this.TryLoad(); - } - - if (!this.IsLoaded) - { - throw new LoadableComponentException(this.UnableToLoadMessage); + return false; } + } + } + /// + /// Ensure that the component is currently loaded. + /// + /// The loaded component. + /// This is equivalent to the Get() method in Java version. + public virtual T Load() + { + if (this.IsLoaded) + { return (T)this; } - - /// - /// Ensure that the component is currently loaded. - /// - /// The loaded instance. - ILoadableComponent ILoadableComponent.Load() + else { - return (ILoadableComponent)this.Load(); + this.TryLoad(); } - /// - /// HandleLoadError gives a subclass the opportunity to handle a that occurred - /// during the execution of . - /// - /// The exception which occurs on load. - protected virtual void HandleLoadError(WebDriverException ex) + if (!this.IsLoaded) { + throw new LoadableComponentException(this.UnableToLoadMessage); } - /// - /// When this method returns, the component modeled by the subclass should be fully loaded. This - /// subclass is expected to navigate to an appropriate page or trigger loading the correct HTML - /// should this be necessary. - /// - protected abstract void ExecuteLoad(); + return (T)this; + } + + /// + /// Ensure that the component is currently loaded. + /// + /// The loaded instance. + ILoadableComponent ILoadableComponent.Load() + { + return (ILoadableComponent)this.Load(); + } + + /// + /// HandleLoadError gives a subclass the opportunity to handle a that occurred + /// during the execution of . + /// + /// The exception which occurs on load. + protected virtual void HandleLoadError(WebDriverException ex) + { + } - /// - /// Determine whether or not the component is loaded. Subclasses are expected to provide the details - /// to determine if the page or component is loaded. - /// - /// A boolean value indicating if the component is loaded. - protected abstract bool EvaluateLoadedStatus(); + /// + /// When this method returns, the component modeled by the subclass should be fully loaded. This + /// subclass is expected to navigate to an appropriate page or trigger loading the correct HTML + /// should this be necessary. + /// + protected abstract void ExecuteLoad(); - /// - /// Attempts to load this component, providing an opportunity for the user to handle any errors encountered - /// during the load process. - /// - /// A self-reference to this - protected T TryLoad() - { - try - { - this.ExecuteLoad(); - } - catch (WebDriverException e) - { - this.HandleLoadError(e); - } + /// + /// Determine whether or not the component is loaded. Subclasses are expected to provide the details + /// to determine if the page or component is loaded. + /// + /// A boolean value indicating if the component is loaded. + protected abstract bool EvaluateLoadedStatus(); - return (T)this; + /// + /// Attempts to load this component, providing an opportunity for the user to handle any errors encountered + /// during the load process. + /// + /// A self-reference to this + protected T TryLoad() + { + try + { + this.ExecuteLoad(); } + catch (WebDriverException e) + { + this.HandleLoadError(e); + } + + return (T)this; } } diff --git a/dotnet/src/support/UI/PopupWindowFinder.cs b/dotnet/src/support/UI/PopupWindowFinder.cs index 95e57a5ad0385..e7c65bb255e3c 100644 --- a/dotnet/src/support/UI/PopupWindowFinder.cs +++ b/dotnet/src/support/UI/PopupWindowFinder.cs @@ -21,123 +21,122 @@ using System.Collections.ObjectModel; using System.Linq; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Provides a mechanism by which the window handle of an invoked +/// popup browser window may be determined. +/// +/// +/// +/// // Store the current window handle so you can switch back to the +/// // original window when you close the popup. +/// string current = driver.CurrentWindowHandle; +/// PopupWindowFinder finder = new PopupWindowFinder(driver); +/// string newHandle = finder.Click(driver.FindElement(By.LinkText("Open new window"))); +/// driver.SwitchTo.Window(newHandle); +/// +/// +public class PopupWindowFinder { + private readonly IWebDriver driver; + private readonly TimeSpan timeout; + private readonly TimeSpan sleepInterval; + /// - /// Provides a mechanism by which the window handle of an invoked - /// popup browser window may be determined. + /// Initializes a new instance of the class. /// - /// - /// - /// // Store the current window handle so you can switch back to the - /// // original window when you close the popup. - /// string current = driver.CurrentWindowHandle; - /// PopupWindowFinder finder = new PopupWindowFinder(driver); - /// string newHandle = finder.Click(driver.FindElement(By.LinkText("Open new window"))); - /// driver.SwitchTo.Window(newHandle); - /// - /// - public class PopupWindowFinder + /// The instance that is used + /// to manipulate the popup window. + /// When using this constructor overload, the timeout will be 5 seconds, + /// and the check for a new window will be performed every 250 milliseconds. + /// If is . + public PopupWindowFinder(IWebDriver driver) + : this(driver, DefaultTimeout, DefaultSleepInterval) { - private readonly IWebDriver driver; - private readonly TimeSpan timeout; - private readonly TimeSpan sleepInterval; + } - /// - /// Initializes a new instance of the class. - /// - /// The instance that is used - /// to manipulate the popup window. - /// When using this constructor overload, the timeout will be 5 seconds, - /// and the check for a new window will be performed every 250 milliseconds. - /// If is . - public PopupWindowFinder(IWebDriver driver) - : this(driver, DefaultTimeout, DefaultSleepInterval) - { - } + /// + /// Initializes a new instance of the class + /// with the specified timeout. + /// + /// The instance that is used + /// to manipulate the popup window. + /// The representing the amount of + /// time to wait for the popup window to appear. + /// When using this constructor overload, the check for a new window + /// will be performed every 250 milliseconds. + /// If is . + public PopupWindowFinder(IWebDriver driver, TimeSpan timeout) + : this(driver, timeout, DefaultSleepInterval) + { + } - /// - /// Initializes a new instance of the class - /// with the specified timeout. - /// - /// The instance that is used - /// to manipulate the popup window. - /// The representing the amount of - /// time to wait for the popup window to appear. - /// When using this constructor overload, the check for a new window - /// will be performed every 250 milliseconds. - /// If is . - public PopupWindowFinder(IWebDriver driver, TimeSpan timeout) - : this(driver, timeout, DefaultSleepInterval) - { - } + /// + /// Initializes a new instance of the class + /// with the specified timeout and using the specified interval to check for + /// the existence of the new window. + /// + /// The instance that is used + /// to manipulate the popup window. + /// The representing the amount of + /// time to wait for the popup window to appear. + /// The representing the + /// amount of time to wait between checks of the available window handles. + /// If is . + public PopupWindowFinder(IWebDriver driver, TimeSpan timeout, TimeSpan sleepInterval) + { + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + this.timeout = timeout; + this.sleepInterval = sleepInterval; + } + + private static TimeSpan DefaultTimeout => TimeSpan.FromSeconds(5); - /// - /// Initializes a new instance of the class - /// with the specified timeout and using the specified interval to check for - /// the existence of the new window. - /// - /// The instance that is used - /// to manipulate the popup window. - /// The representing the amount of - /// time to wait for the popup window to appear. - /// The representing the - /// amount of time to wait between checks of the available window handles. - /// If is . - public PopupWindowFinder(IWebDriver driver, TimeSpan timeout, TimeSpan sleepInterval) + private static TimeSpan DefaultSleepInterval => TimeSpan.FromMilliseconds(250); + + /// + /// Clicks on an element that is expected to trigger a popup browser window. + /// + /// The that, when clicked, invokes + /// the popup browser window. + /// The window handle of the popup browser window. + /// Thrown if no popup window appears within the specified timeout. + /// Thrown if the element to click is . + public string Click(IWebElement element) + { + if (element is null) { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - this.timeout = timeout; - this.sleepInterval = sleepInterval; + throw new ArgumentNullException(nameof(element), "element cannot be null"); } - private static TimeSpan DefaultTimeout => TimeSpan.FromSeconds(5); - - private static TimeSpan DefaultSleepInterval => TimeSpan.FromMilliseconds(250); + return this.Invoke(element.Click); + } - /// - /// Clicks on an element that is expected to trigger a popup browser window. - /// - /// The that, when clicked, invokes - /// the popup browser window. - /// The window handle of the popup browser window. - /// Thrown if no popup window appears within the specified timeout. - /// Thrown if the element to click is . - public string Click(IWebElement element) + /// + /// Invokes a method that is expected to trigger a popup browser window. + /// + /// An that, when run, invokes + /// the popup browser window. + /// The window handle of the popup browser window. + /// Thrown if no popup window appears within the specified timeout. + /// Thrown if the action to invoke is . + public string Invoke(Action popupMethod) + { + if (popupMethod is null) { - if (element is null) - { - throw new ArgumentNullException(nameof(element), "element cannot be null"); - } - - return this.Invoke(element.Click); + throw new ArgumentNullException(nameof(popupMethod), "popupMethod cannot be null"); } - /// - /// Invokes a method that is expected to trigger a popup browser window. - /// - /// An that, when run, invokes - /// the popup browser window. - /// The window handle of the popup browser window. - /// Thrown if no popup window appears within the specified timeout. - /// Thrown if the action to invoke is . - public string Invoke(Action popupMethod) + ReadOnlyCollection existingHandles = this.driver.WindowHandles; + popupMethod(); + WebDriverWait wait = new WebDriverWait(SystemClock.Instance, this.driver, this.timeout, this.sleepInterval); + string popupHandle = wait.Until(driver => { - if (popupMethod is null) - { - throw new ArgumentNullException(nameof(popupMethod), "popupMethod cannot be null"); - } - - ReadOnlyCollection existingHandles = this.driver.WindowHandles; - popupMethod(); - WebDriverWait wait = new WebDriverWait(SystemClock.Instance, this.driver, this.timeout, this.sleepInterval); - string popupHandle = wait.Until(driver => - { - ReadOnlyCollection newHandles = driver.WindowHandles; - return newHandles.Except(existingHandles, StringComparer.Ordinal).FirstOrDefault(); - }); + ReadOnlyCollection newHandles = driver.WindowHandles; + return newHandles.Except(existingHandles, StringComparer.Ordinal).FirstOrDefault(); + }); - return popupHandle; - } + return popupHandle; } } diff --git a/dotnet/src/support/UI/SelectElement.cs b/dotnet/src/support/UI/SelectElement.cs index 070554d54192c..488d4b3d41e45 100644 --- a/dotnet/src/support/UI/SelectElement.cs +++ b/dotnet/src/support/UI/SelectElement.cs @@ -23,429 +23,428 @@ using System.Globalization; using System.Text; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Provides a convenience method for manipulating selections of options in an HTML select element. +/// +public class SelectElement : IWrapsElement { /// - /// Provides a convenience method for manipulating selections of options in an HTML select element. + /// Initializes a new instance of the class. /// - public class SelectElement : IWrapsElement + /// The element to be wrapped + /// Thrown when the object is + /// Thrown when the element wrapped is not a <select> element. + public SelectElement(IWebElement element) { - /// - /// Initializes a new instance of the class. - /// - /// The element to be wrapped - /// Thrown when the object is - /// Thrown when the element wrapped is not a <select> element. - public SelectElement(IWebElement element) + if (element is null) { - if (element is null) - { - throw new ArgumentNullException(nameof(element), "element cannot be null"); - } + throw new ArgumentNullException(nameof(element), "element cannot be null"); + } - string tagName = element.TagName; + string tagName = element.TagName; - if (string.IsNullOrEmpty(tagName) || !string.Equals(tagName, "select", StringComparison.OrdinalIgnoreCase)) - { - throw new UnexpectedTagNameException("select", tagName); - } + if (string.IsNullOrEmpty(tagName) || !string.Equals(tagName, "select", StringComparison.OrdinalIgnoreCase)) + { + throw new UnexpectedTagNameException("select", tagName); + } - this.WrappedElement = element; + this.WrappedElement = element; - // let check if it's a multiple - string? attribute = element.GetAttribute("multiple"); - this.IsMultiple = attribute != null && !attribute.Equals("false", StringComparison.OrdinalIgnoreCase); - } + // let check if it's a multiple + string? attribute = element.GetAttribute("multiple"); + this.IsMultiple = attribute != null && !attribute.Equals("false", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Gets the wrapped by this object. + /// + public IWebElement WrappedElement { get; } + + /// + /// Gets a value indicating whether the parent element supports multiple selections. + /// + public bool IsMultiple { get; } + + /// + /// Gets the list of options for the select element. + /// + public IList Options => this.WrappedElement.FindElements(By.TagName("option")); - /// - /// Gets the wrapped by this object. - /// - public IWebElement WrappedElement { get; } - - /// - /// Gets a value indicating whether the parent element supports multiple selections. - /// - public bool IsMultiple { get; } - - /// - /// Gets the list of options for the select element. - /// - public IList Options => this.WrappedElement.FindElements(By.TagName("option")); - - /// - /// Gets the selected item within the select element. - /// - /// If more than one item is selected this will return the first item. - /// Thrown if no option is selected. - public IWebElement SelectedOption + /// + /// Gets the selected item within the select element. + /// + /// If more than one item is selected this will return the first item. + /// Thrown if no option is selected. + public IWebElement SelectedOption + { + get { - get + foreach (IWebElement option in this.Options) { - foreach (IWebElement option in this.Options) + if (option.Selected) { - if (option.Selected) - { - return option; - } + return option; } - - throw new NoSuchElementException("No option is selected"); } + + throw new NoSuchElementException("No option is selected"); } + } - /// - /// Gets all of the selected options within the select element. - /// - public IList AllSelectedOptions + /// + /// Gets all of the selected options within the select element. + /// + public IList AllSelectedOptions + { + get { - get + List returnValue = new List(); + foreach (IWebElement option in this.Options) { - List returnValue = new List(); - foreach (IWebElement option in this.Options) + if (option.Selected) { - if (option.Selected) - { - returnValue.Add(option); - } + returnValue.Add(option); } - - return returnValue; } + + return returnValue; + } + } + + /// + /// Select all options by the text displayed. + /// + /// The text of the option to be selected. + /// Default value is false. If true a partial match on the Options list will be performed, otherwise exact match. + /// When given "Bar" this method would select an option like: + /// + /// <option value="foo">Bar</option> + /// + /// + /// If is . + /// Thrown if there is no element with the given text present. + public void SelectByText(string text, bool partialMatch = false) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text), "text must not be null"); + } + + bool matched = false; + ReadOnlyCollection options; + + if (!partialMatch) + { + // try to find the option via XPATH ... + options = this.WrappedElement.FindElements(By.XPath(".//option[normalize-space(.) = " + EscapeQuotes(text) + "]")); + } + else + { + options = this.WrappedElement.FindElements(By.XPath(".//option[contains(normalize-space(.), " + EscapeQuotes(text) + ")]")); } - /// - /// Select all options by the text displayed. - /// - /// The text of the option to be selected. - /// Default value is false. If true a partial match on the Options list will be performed, otherwise exact match. - /// When given "Bar" this method would select an option like: - /// - /// <option value="foo">Bar</option> - /// - /// - /// If is . - /// Thrown if there is no element with the given text present. - public void SelectByText(string text, bool partialMatch = false) + foreach (IWebElement option in options) { - if (text is null) + SetSelected(option, true); + if (!this.IsMultiple) { - throw new ArgumentNullException(nameof(text), "text must not be null"); + return; } - bool matched = false; - ReadOnlyCollection options; + matched = true; + } - if (!partialMatch) + if (options.Count == 0 && text.Contains(" ")) + { + string substringWithoutSpace = GetLongestSubstringWithoutSpace(text); + IList candidates; + if (string.IsNullOrEmpty(substringWithoutSpace)) { - // try to find the option via XPATH ... - options = this.WrappedElement.FindElements(By.XPath(".//option[normalize-space(.) = " + EscapeQuotes(text) + "]")); + // hmm, text is either empty or contains only spaces - get all options ... + candidates = this.WrappedElement.FindElements(By.TagName("option")); } else { - options = this.WrappedElement.FindElements(By.XPath(".//option[contains(normalize-space(.), " + EscapeQuotes(text) + ")]")); - } - - foreach (IWebElement option in options) - { - SetSelected(option, true); - if (!this.IsMultiple) - { - return; - } - - matched = true; + // get candidates via XPATH ... + candidates = this.WrappedElement.FindElements(By.XPath(".//option[contains(., " + EscapeQuotes(substringWithoutSpace) + ")]")); } - if (options.Count == 0 && text.Contains(" ")) + foreach (IWebElement option in candidates) { - string substringWithoutSpace = GetLongestSubstringWithoutSpace(text); - IList candidates; - if (string.IsNullOrEmpty(substringWithoutSpace)) + if (text == option.Text) { - // hmm, text is either empty or contains only spaces - get all options ... - candidates = this.WrappedElement.FindElements(By.TagName("option")); - } - else - { - // get candidates via XPATH ... - candidates = this.WrappedElement.FindElements(By.XPath(".//option[contains(., " + EscapeQuotes(substringWithoutSpace) + ")]")); - } - - foreach (IWebElement option in candidates) - { - if (text == option.Text) + SetSelected(option, true); + if (!this.IsMultiple) { - SetSelected(option, true); - if (!this.IsMultiple) - { - return; - } - - matched = true; + return; } + + matched = true; } } + } - if (!matched) + if (!matched) + { + throw new NoSuchElementException("Cannot locate element with text: " + text); + } + } + + /// + /// Select an option by the value. + /// + /// The value of the option to be selected. + /// When given "foo" this method will select an option like: + /// + /// <option value="foo">Bar</option> + /// + /// + /// Thrown when no element with the specified value is found. + public void SelectByValue(string value) + { + StringBuilder builder = new StringBuilder(".//option[@value = "); + builder.Append(EscapeQuotes(value)); + builder.Append("]"); + IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); + + bool matched = false; + foreach (IWebElement option in options) + { + SetSelected(option, true); + if (!this.IsMultiple) { - throw new NoSuchElementException("Cannot locate element with text: " + text); + return; } + + matched = true; } - /// - /// Select an option by the value. - /// - /// The value of the option to be selected. - /// When given "foo" this method will select an option like: - /// - /// <option value="foo">Bar</option> - /// - /// - /// Thrown when no element with the specified value is found. - public void SelectByValue(string value) + if (!matched) { - StringBuilder builder = new StringBuilder(".//option[@value = "); - builder.Append(EscapeQuotes(value)); - builder.Append("]"); - IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); - - bool matched = false; - foreach (IWebElement option in options) - { - SetSelected(option, true); - if (!this.IsMultiple) - { - return; - } + throw new NoSuchElementException("Cannot locate option with value: " + value); + } + } - matched = true; - } + /// + /// Select the option by the index, as determined by the "index" attribute of the element. + /// + /// The value of the index attribute of the option to be selected. + /// Thrown when no element exists with the specified index attribute. + public void SelectByIndex(int index) + { + string match = index.ToString(CultureInfo.InvariantCulture); - if (!matched) + foreach (IWebElement option in this.Options) + { + if (option.GetAttribute("index") == match) { - throw new NoSuchElementException("Cannot locate option with value: " + value); + SetSelected(option, true); + return; } } - /// - /// Select the option by the index, as determined by the "index" attribute of the element. - /// - /// The value of the index attribute of the option to be selected. - /// Thrown when no element exists with the specified index attribute. - public void SelectByIndex(int index) + throw new NoSuchElementException("Cannot locate option with index: " + index); + } + + /// + /// Clear all selected entries. This is only valid when the SELECT supports multiple selections. + /// + /// Thrown when attempting to deselect all options from a SELECT + /// that does not support multiple selections. + public void DeselectAll() + { + if (!this.IsMultiple) { - string match = index.ToString(CultureInfo.InvariantCulture); + throw new InvalidOperationException("You may only deselect all options if multi-select is supported"); + } - foreach (IWebElement option in this.Options) - { - if (option.GetAttribute("index") == match) - { - SetSelected(option, true); - return; - } - } + foreach (IWebElement option in this.Options) + { + SetSelected(option, false); + } + } - throw new NoSuchElementException("Cannot locate option with index: " + index); + /// + /// Deselect the option by the text displayed. + /// + /// Thrown when attempting to deselect option from a SELECT + /// that does not support multiple selections. + /// Thrown when no element exists with the specified test attribute. + /// The text of the option to be deselected. + /// When given "Bar" this method would deselect an option like: + /// + /// <option value="foo">Bar</option> + /// + /// + public void DeselectByText(string text) + { + if (!this.IsMultiple) + { + throw new InvalidOperationException("You may only deselect option if multi-select is supported"); } - /// - /// Clear all selected entries. This is only valid when the SELECT supports multiple selections. - /// - /// Thrown when attempting to deselect all options from a SELECT - /// that does not support multiple selections. - public void DeselectAll() + bool matched = false; + StringBuilder builder = new StringBuilder(".//option[normalize-space(.) = "); + builder.Append(EscapeQuotes(text)); + builder.Append("]"); + IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); + foreach (IWebElement option in options) { - if (!this.IsMultiple) - { - throw new InvalidOperationException("You may only deselect all options if multi-select is supported"); - } + SetSelected(option, false); + matched = true; + } - foreach (IWebElement option in this.Options) - { - SetSelected(option, false); - } + if (!matched) + { + throw new NoSuchElementException("Cannot locate option with text: " + text); } + } - /// - /// Deselect the option by the text displayed. - /// - /// Thrown when attempting to deselect option from a SELECT - /// that does not support multiple selections. - /// Thrown when no element exists with the specified test attribute. - /// The text of the option to be deselected. - /// When given "Bar" this method would deselect an option like: - /// - /// <option value="foo">Bar</option> - /// - /// - public void DeselectByText(string text) + /// + /// Deselect the option having value matching the specified text. + /// + /// Thrown when attempting to deselect option from a SELECT + /// that does not support multiple selections. + /// Thrown when no element exists with the specified value attribute. + /// The value of the option to deselect. + /// When given "foo" this method will deselect an option like: + /// + /// <option value="foo">Bar</option> + /// + /// + public void DeselectByValue(string value) + { + if (!this.IsMultiple) { - if (!this.IsMultiple) - { - throw new InvalidOperationException("You may only deselect option if multi-select is supported"); - } + throw new InvalidOperationException("You may only deselect option if multi-select is supported"); + } - bool matched = false; - StringBuilder builder = new StringBuilder(".//option[normalize-space(.) = "); - builder.Append(EscapeQuotes(text)); - builder.Append("]"); - IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); - foreach (IWebElement option in options) - { - SetSelected(option, false); - matched = true; - } + bool matched = false; + StringBuilder builder = new StringBuilder(".//option[@value = "); + builder.Append(EscapeQuotes(value)); + builder.Append("]"); + IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); + foreach (IWebElement option in options) + { + SetSelected(option, false); + matched = true; + } - if (!matched) - { - throw new NoSuchElementException("Cannot locate option with text: " + text); - } + if (!matched) + { + throw new NoSuchElementException("Cannot locate option with value: " + value); } + } - /// - /// Deselect the option having value matching the specified text. - /// - /// Thrown when attempting to deselect option from a SELECT - /// that does not support multiple selections. - /// Thrown when no element exists with the specified value attribute. - /// The value of the option to deselect. - /// When given "foo" this method will deselect an option like: - /// - /// <option value="foo">Bar</option> - /// - /// - public void DeselectByValue(string value) + /// + /// Deselect the option by the index, as determined by the "index" attribute of the element. + /// + /// Thrown when attempting to deselect option from a SELECT + /// that does not support multiple selections. + /// Thrown when no element exists with the specified index attribute. + /// The value of the index attribute of the option to deselect. + public void DeselectByIndex(int index) + { + if (!this.IsMultiple) { - if (!this.IsMultiple) - { - throw new InvalidOperationException("You may only deselect option if multi-select is supported"); - } + throw new InvalidOperationException("You may only deselect option if multi-select is supported"); + } - bool matched = false; - StringBuilder builder = new StringBuilder(".//option[@value = "); - builder.Append(EscapeQuotes(value)); - builder.Append("]"); - IList options = this.WrappedElement.FindElements(By.XPath(builder.ToString())); - foreach (IWebElement option in options) + string match = index.ToString(CultureInfo.InvariantCulture); + foreach (IWebElement option in this.Options) + { + if (match == option.GetAttribute("index")) { SetSelected(option, false); - matched = true; - } - - if (!matched) - { - throw new NoSuchElementException("Cannot locate option with value: " + value); + return; } } - /// - /// Deselect the option by the index, as determined by the "index" attribute of the element. - /// - /// Thrown when attempting to deselect option from a SELECT - /// that does not support multiple selections. - /// Thrown when no element exists with the specified index attribute. - /// The value of the index attribute of the option to deselect. - public void DeselectByIndex(int index) + throw new NoSuchElementException("Cannot locate option with index: " + index); + } + + private static string EscapeQuotes(string toEscape) + { + // Convert strings with both quotes and ticks into: foo'"bar -> concat("foo'", '"', "bar") + if (toEscape.IndexOf("\"", StringComparison.OrdinalIgnoreCase) > -1 && toEscape.IndexOf("'", StringComparison.OrdinalIgnoreCase) > -1) { - if (!this.IsMultiple) + bool quoteIsLast = false; + if (toEscape.LastIndexOf("\"", StringComparison.OrdinalIgnoreCase) == toEscape.Length - 1) { - throw new InvalidOperationException("You may only deselect option if multi-select is supported"); + quoteIsLast = true; } - string match = index.ToString(CultureInfo.InvariantCulture); - foreach (IWebElement option in this.Options) + List substrings = new List(toEscape.Split('\"')); + if (quoteIsLast && string.IsNullOrEmpty(substrings[substrings.Count - 1])) { - if (match == option.GetAttribute("index")) - { - SetSelected(option, false); - return; - } + // If the last character is a quote ('"'), we end up with an empty entry + // at the end of the list, which is unnecessary. We don't want to split + // ignoring *all* empty entries, since that might mask legitimate empty + // strings. Instead, just remove the empty ending entry. + substrings.RemoveAt(substrings.Count - 1); } - throw new NoSuchElementException("Cannot locate option with index: " + index); - } - - private static string EscapeQuotes(string toEscape) - { - // Convert strings with both quotes and ticks into: foo'"bar -> concat("foo'", '"', "bar") - if (toEscape.IndexOf("\"", StringComparison.OrdinalIgnoreCase) > -1 && toEscape.IndexOf("'", StringComparison.OrdinalIgnoreCase) > -1) + StringBuilder quoted = new StringBuilder("concat("); + for (int i = 0; i < substrings.Count; i++) { - bool quoteIsLast = false; - if (toEscape.LastIndexOf("\"", StringComparison.OrdinalIgnoreCase) == toEscape.Length - 1) - { - quoteIsLast = true; - } - - List substrings = new List(toEscape.Split('\"')); - if (quoteIsLast && string.IsNullOrEmpty(substrings[substrings.Count - 1])) - { - // If the last character is a quote ('"'), we end up with an empty entry - // at the end of the list, which is unnecessary. We don't want to split - // ignoring *all* empty entries, since that might mask legitimate empty - // strings. Instead, just remove the empty ending entry. - substrings.RemoveAt(substrings.Count - 1); - } - - StringBuilder quoted = new StringBuilder("concat("); - for (int i = 0; i < substrings.Count; i++) + quoted.Append("\"").Append(substrings[i]).Append("\""); + if (i == substrings.Count - 1) { - quoted.Append("\"").Append(substrings[i]).Append("\""); - if (i == substrings.Count - 1) + if (quoteIsLast) { - if (quoteIsLast) - { - quoted.Append(", '\"')"); - } - else - { - quoted.Append(")"); - } + quoted.Append(", '\"')"); } else { - quoted.Append(", '\"', "); + quoted.Append(")"); } } - - return quoted.ToString(); + else + { + quoted.Append(", '\"', "); + } } - // Escape string with just a quote into being single quoted: f"oo -> 'f"oo' - if (toEscape.IndexOf("\"", StringComparison.OrdinalIgnoreCase) > -1) - { - return string.Format(CultureInfo.InvariantCulture, "'{0}'", toEscape); - } + return quoted.ToString(); + } - // Otherwise return the quoted string - return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", toEscape); + // Escape string with just a quote into being single quoted: f"oo -> 'f"oo' + if (toEscape.IndexOf("\"", StringComparison.OrdinalIgnoreCase) > -1) + { + return string.Format(CultureInfo.InvariantCulture, "'{0}'", toEscape); } - private static string GetLongestSubstringWithoutSpace(string s) + // Otherwise return the quoted string + return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", toEscape); + } + + private static string GetLongestSubstringWithoutSpace(string s) + { + string result = string.Empty; + foreach (string substring in s.Split(' ')) { - string result = string.Empty; - foreach (string substring in s.Split(' ')) + if (substring.Length > result.Length) { - if (substring.Length > result.Length) - { - result = substring; - } + result = substring; } - - return result; } - private static void SetSelected(IWebElement option, bool select) + return result; + } + + private static void SetSelected(IWebElement option, bool select) + { + if (select && !option.Enabled) { - if (select && !option.Enabled) - { - throw new InvalidOperationException("You may not select a disabled option"); - } + throw new InvalidOperationException("You may not select a disabled option"); + } - bool isSelected = option.Selected; - if ((!isSelected && select) || (isSelected && !select)) - { - option.Click(); - } + bool isSelected = option.Selected; + if ((!isSelected && select) || (isSelected && !select)) + { + option.Click(); } } } diff --git a/dotnet/src/support/UI/SlowLoadableComponent{T}.cs b/dotnet/src/support/UI/SlowLoadableComponent{T}.cs index 900e162b28c04..868204995af2f 100644 --- a/dotnet/src/support/UI/SlowLoadableComponent{T}.cs +++ b/dotnet/src/support/UI/SlowLoadableComponent{T}.cs @@ -21,121 +21,120 @@ using System.Globalization; using System.Threading; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// A which might not have finished loading when Load() returns. +/// After a call to Load(), the IsLoaded property should continue to return false until the component has fully loaded. +/// +/// +/// // Example usage: +/// new MySlowComponent().Load(); +/// +/// +/// +/// Override the HandleErrors() method to check for error conditions which caused to fail. +/// The type to be returned (normally the subclass' type) +public abstract class SlowLoadableComponent : LoadableComponent + where T : SlowLoadableComponent { /// - /// A which might not have finished loading when Load() returns. - /// After a call to Load(), the IsLoaded property should continue to return false until the component has fully loaded. - /// - /// - /// // Example usage: - /// new MySlowComponent().Load(); - /// - /// + /// Initializes a new instance of the class. /// - /// Override the HandleErrors() method to check for error conditions which caused to fail. - /// The type to be returned (normally the subclass' type) - public abstract class SlowLoadableComponent : LoadableComponent - where T : SlowLoadableComponent + /// The within which the component should be loaded. + protected SlowLoadableComponent(TimeSpan timeout) + : this(timeout, SystemClock.Instance) { - /// - /// Initializes a new instance of the class. - /// - /// The within which the component should be loaded. - protected SlowLoadableComponent(TimeSpan timeout) - : this(timeout, SystemClock.Instance) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The within which the component should be loaded. - /// The to use when measuring the timeout. - /// If is . - protected SlowLoadableComponent(TimeSpan timeout, IClock clock) - { - this.Clock = clock ?? throw new ArgumentNullException(nameof(clock)); - this.Timeout = timeout; - } + /// + /// Initializes a new instance of the class. + /// + /// The within which the component should be loaded. + /// The to use when measuring the timeout. + /// If is . + protected SlowLoadableComponent(TimeSpan timeout, IClock clock) + { + this.Clock = clock ?? throw new ArgumentNullException(nameof(clock)); + this.Timeout = timeout; + } - /// - /// Gets or sets the time to sleep between each check of the load status of the component. - /// - public TimeSpan SleepInterval { get; set; } = TimeSpan.FromMilliseconds(200); + /// + /// Gets or sets the time to sleep between each check of the load status of the component. + /// + public TimeSpan SleepInterval { get; set; } = TimeSpan.FromMilliseconds(200); - /// - /// Gets the timeout interval before which this component must be considered loaded. - /// - protected TimeSpan Timeout { get; } + /// + /// Gets the timeout interval before which this component must be considered loaded. + /// + protected TimeSpan Timeout { get; } - /// - /// Gets the clock object providing timing for monitoring the load status of this component. - /// - protected IClock Clock { get; } + /// + /// Gets the clock object providing timing for monitoring the load status of this component. + /// + protected IClock Clock { get; } - /// - /// Ensures that the component is currently loaded. - /// - /// The loaded component. - /// This is equivalent to the Get() method in Java version. - public override T Load() + /// + /// Ensures that the component is currently loaded. + /// + /// The loaded component. + /// This is equivalent to the Get() method in Java version. + public override T Load() + { + if (this.IsLoaded) { - if (this.IsLoaded) - { - return (T)this; - } - else - { - this.TryLoad(); - } - - DateTime end = this.Clock.LaterBy(this.Timeout); - - while (this.Clock.IsNowBefore(end)) - { - if (this.IsLoaded) - { - return (T)this; - } + return (T)this; + } + else + { + this.TryLoad(); + } - this.HandleErrors(); - this.Wait(); - } + DateTime end = this.Clock.LaterBy(this.Timeout); + while (this.Clock.IsNowBefore(end)) + { if (this.IsLoaded) { return (T)this; } - else - { - if (string.IsNullOrEmpty(UnableToLoadMessage)) - { - this.UnableToLoadMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds.", this.Timeout.TotalSeconds); - } - throw new WebDriverTimeoutException(this.UnableToLoadMessage); - } + this.HandleErrors(); + this.Wait(); } - /// - /// Checks for well known error cases, which would mean that loading has finished, but an error - /// condition was seen. - /// - /// - /// This method should be overridden so that expected errors can be automatically handled. - /// - protected virtual void HandleErrors() + if (this.IsLoaded) { - // no-op by default + return (T)this; } - - /// - /// Waits between polls of the load status of this component. - /// - protected virtual void Wait() + else { - Thread.Sleep(this.SleepInterval); + if (string.IsNullOrEmpty(UnableToLoadMessage)) + { + this.UnableToLoadMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds.", this.Timeout.TotalSeconds); + } + + throw new WebDriverTimeoutException(this.UnableToLoadMessage); } } + + /// + /// Checks for well known error cases, which would mean that loading has finished, but an error + /// condition was seen. + /// + /// + /// This method should be overridden so that expected errors can be automatically handled. + /// + protected virtual void HandleErrors() + { + // no-op by default + } + + /// + /// Waits between polls of the load status of this component. + /// + protected virtual void Wait() + { + Thread.Sleep(this.SleepInterval); + } } diff --git a/dotnet/src/support/UI/UnexpectedTagNameException.cs b/dotnet/src/support/UI/UnexpectedTagNameException.cs index b9df3207fc4bc..9bc5672fd1a32 100644 --- a/dotnet/src/support/UI/UnexpectedTagNameException.cs +++ b/dotnet/src/support/UI/UnexpectedTagNameException.cs @@ -20,54 +20,53 @@ using System; using System.Globalization; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// The exception thrown when using the Select class on a tag that +/// does not support the HTML select element's selection semantics. +/// +[Serializable] +public class UnexpectedTagNameException : WebDriverException { /// - /// The exception thrown when using the Select class on a tag that - /// does not support the HTML select element's selection semantics. + /// Initializes a new instance of the class with + /// the expected tag name and the actual tag name. /// - [Serializable] - public class UnexpectedTagNameException : WebDriverException + /// The tag name that was expected. + /// The actual tag name of the element. + public UnexpectedTagNameException(string expected, string actual) + : base(string.Format(CultureInfo.InvariantCulture, "Element should have been {0} but was {1}", expected, actual)) { - /// - /// Initializes a new instance of the class with - /// the expected tag name and the actual tag name. - /// - /// The tag name that was expected. - /// The actual tag name of the element. - public UnexpectedTagNameException(string expected, string actual) - : base(string.Format(CultureInfo.InvariantCulture, "Element should have been {0} but was {1}", expected, actual)) - { - } + } - /// - /// Initializes a new instance of the class. - /// - public UnexpectedTagNameException() - { - } + /// + /// Initializes a new instance of the class. + /// + public UnexpectedTagNameException() + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message of the exception - public UnexpectedTagNameException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message of the exception + public UnexpectedTagNameException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public UnexpectedTagNameException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public UnexpectedTagNameException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/Alert.cs b/dotnet/src/webdriver/Alert.cs index fb32d3e0fb0c4..3f7e671a18e53 100644 --- a/dotnet/src/webdriver/Alert.cs +++ b/dotnet/src/webdriver/Alert.cs @@ -20,68 +20,67 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can manipulate JavaScript alerts. +/// +internal class Alert : IAlert { + private readonly WebDriver driver; + /// - /// Defines the interface through which the user can manipulate JavaScript alerts. + /// Initializes a new instance of the class. /// - internal class Alert : IAlert + /// The for which the alerts will be managed. + public Alert(WebDriver driver) { - private readonly WebDriver driver; + this.driver = driver; + } - /// - /// Initializes a new instance of the class. - /// - /// The for which the alerts will be managed. - public Alert(WebDriver driver) + /// + /// Gets the text of the alert. + /// + public string? Text + { + get { - this.driver = driver; + Response commandResponse = this.driver.Execute(DriverCommand.GetAlertText, null); + return (string?)commandResponse.Value; } + } - /// - /// Gets the text of the alert. - /// - public string? Text - { - get - { - Response commandResponse = this.driver.Execute(DriverCommand.GetAlertText, null); - return (string?)commandResponse.Value; - } - } + /// + /// Dismisses the alert. + /// + public void Dismiss() + { + this.driver.Execute(DriverCommand.DismissAlert, null); + } - /// - /// Dismisses the alert. - /// - public void Dismiss() - { - this.driver.Execute(DriverCommand.DismissAlert, null); - } + /// + /// Accepts the alert. + /// + public void Accept() + { + this.driver.Execute(DriverCommand.AcceptAlert, null); + } - /// - /// Accepts the alert. - /// - public void Accept() + /// + /// Sends keys to the alert. + /// + /// The keystrokes to send. + /// If is . + public void SendKeys(string keysToSend) + { + if (keysToSend is null) { - this.driver.Execute(DriverCommand.AcceptAlert, null); + throw new ArgumentNullException(nameof(keysToSend), "Keys to send must not be null."); } - /// - /// Sends keys to the alert. - /// - /// The keystrokes to send. - /// If is . - public void SendKeys(string keysToSend) - { - if (keysToSend is null) - { - throw new ArgumentNullException(nameof(keysToSend), "Keys to send must not be null."); - } - - Dictionary parameters = new Dictionary(); - parameters.Add("text", keysToSend); + Dictionary parameters = new Dictionary(); + parameters.Add("text", keysToSend); - this.driver.Execute(DriverCommand.SetAlertValue, parameters); - } + this.driver.Execute(DriverCommand.SetAlertValue, parameters); } } diff --git a/dotnet/src/webdriver/By.cs b/dotnet/src/webdriver/By.cs index b538a727d7265..b280d54589493 100644 --- a/dotnet/src/webdriver/By.cs +++ b/dotnet/src/webdriver/By.cs @@ -24,384 +24,383 @@ using System.Globalization; using System.Text.RegularExpressions; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism by which to find elements within a document. +/// +/// It is possible to create your own locating mechanisms for finding documents. +/// In order to do this,subclass this class and override the protected methods. However, +/// it is expected that that all subclasses rely on the basic finding mechanisms provided +/// through static methods of this class. An example of this can be found in OpenQA.Support.ByIdOrName +/// +[Serializable] +public class By { + private const string CssSelectorMechanism = "css selector"; + private const string XPathSelectorMechanism = "xpath"; + private const string TagNameMechanism = "tag name"; + private const string LinkTextMechanism = "link text"; + private const string PartialLinkTextMechanism = "partial link text"; + /// - /// Provides a mechanism by which to find elements within a document. + /// Initializes a new instance of the class. /// - /// It is possible to create your own locating mechanisms for finding documents. - /// In order to do this,subclass this class and override the protected methods. However, - /// it is expected that that all subclasses rely on the basic finding mechanisms provided - /// through static methods of this class. An example of this can be found in OpenQA.Support.ByIdOrName + protected By() + { + } + + /// + /// Initializes a new instance of the class using the specified mechanism and criteria for finding elements. + /// + /// The mechanism to use in finding elements. + /// The criteria to use in finding elements. + /// + /// Customizing nothing else, instances using this constructor will attempt to find elements + /// using the method, taking string arguments. /// - [Serializable] - public class By + protected By(string mechanism, string criteria) { - private const string CssSelectorMechanism = "css selector"; - private const string XPathSelectorMechanism = "xpath"; - private const string TagNameMechanism = "tag name"; - private const string LinkTextMechanism = "link text"; - private const string PartialLinkTextMechanism = "partial link text"; - - /// - /// Initializes a new instance of the class. - /// - protected By() - { - } + this.Mechanism = mechanism; + this.Criteria = criteria; + this.FindElementMethod = (ISearchContext context) => ((IFindsElement)context).FindElement(this.Mechanism, this.Criteria); + this.FindElementsMethod = (ISearchContext context) => ((IFindsElement)context).FindElements(this.Mechanism, this.Criteria); + } + + /// + /// Initializes a new instance of the class using the given functions to find elements. + /// + /// A function that takes an object implementing + /// and returns the found . + /// A function that takes an object implementing + /// and returns a of the foundIWebElements. + /// IWebElements/>. + protected By(Func findElementMethod, Func> findElementsMethod) + { + this.FindElementMethod = findElementMethod; + this.FindElementsMethod = findElementsMethod; + } - /// - /// Initializes a new instance of the class using the specified mechanism and criteria for finding elements. - /// - /// The mechanism to use in finding elements. - /// The criteria to use in finding elements. - /// - /// Customizing nothing else, instances using this constructor will attempt to find elements - /// using the method, taking string arguments. - /// - protected By(string mechanism, string criteria) + /// + /// Gets the value of the mechanism for this class instance. + /// + public string Mechanism { get; } = string.Empty; + + /// + /// Gets the value of the criteria for this class instance. + /// + public string Criteria { get; } = string.Empty; + + /// + /// Gets or sets the value of the description for this class instance. + /// + protected string Description { get; set; } = "OpenQA.Selenium.By"; + + /// + /// Gets or sets the method used to find a single element matching specified criteria, or throws if no element is found. + /// + protected Func? FindElementMethod { get; set; } + + /// + /// Gets or sets the method used to find all elements matching specified criteria. + /// + protected Func>? FindElementsMethod { get; set; } + + /// + /// Determines if two instances are equal. + /// + /// One instance to compare. + /// The other instance to compare. + /// if the two instances are equal; otherwise, . + public static bool operator ==(By? one, By? two) + { + // If both are null, or both are same instance, return true. + if (ReferenceEquals(one, two)) { - this.Mechanism = mechanism; - this.Criteria = criteria; - this.FindElementMethod = (ISearchContext context) => ((IFindsElement)context).FindElement(this.Mechanism, this.Criteria); - this.FindElementsMethod = (ISearchContext context) => ((IFindsElement)context).FindElements(this.Mechanism, this.Criteria); + return true; } - /// - /// Initializes a new instance of the class using the given functions to find elements. - /// - /// A function that takes an object implementing - /// and returns the found . - /// A function that takes an object implementing - /// and returns a of the foundIWebElements. - /// IWebElements/>. - protected By(Func findElementMethod, Func> findElementsMethod) + // If one is null, but not both, return false. + if ((one is null) || (two is null)) { - this.FindElementMethod = findElementMethod; - this.FindElementsMethod = findElementsMethod; + return false; } - /// - /// Gets the value of the mechanism for this class instance. - /// - public string Mechanism { get; } = string.Empty; - - /// - /// Gets the value of the criteria for this class instance. - /// - public string Criteria { get; } = string.Empty; - - /// - /// Gets or sets the value of the description for this class instance. - /// - protected string Description { get; set; } = "OpenQA.Selenium.By"; - - /// - /// Gets or sets the method used to find a single element matching specified criteria, or throws if no element is found. - /// - protected Func? FindElementMethod { get; set; } - - /// - /// Gets or sets the method used to find all elements matching specified criteria. - /// - protected Func>? FindElementsMethod { get; set; } - - /// - /// Determines if two instances are equal. - /// - /// One instance to compare. - /// The other instance to compare. - /// if the two instances are equal; otherwise, . - public static bool operator ==(By? one, By? two) + return one.Equals(two); + } + + /// + /// Determines if two instances are unequal. + /// s + /// One instance to compare. + /// The other instance to compare. + /// if the two instances are not equal; otherwise, . + public static bool operator !=(By? one, By? two) + { + return !(one == two); + } + + /// + /// Gets a mechanism to find elements by their ID. + /// + /// The ID to find. + /// A object the driver can use to find the elements. + /// If is . + public static By Id(string idToFind) + { + if (idToFind == null) { - // If both are null, or both are same instance, return true. - if (ReferenceEquals(one, two)) - { - return true; - } - - // If one is null, but not both, return false. - if ((one is null) || (two is null)) - { - return false; - } - - return one.Equals(two); + throw new ArgumentNullException(nameof(idToFind), "Cannot find elements with a null id attribute."); } - /// - /// Determines if two instances are unequal. - /// s - /// One instance to compare. - /// The other instance to compare. - /// if the two instances are not equal; otherwise, . - public static bool operator !=(By? one, By? two) + string selector = EscapeCssSelector(idToFind); + By by = new By(CssSelectorMechanism, "#" + selector); + by.Description = "By.Id: " + idToFind; + if (string.IsNullOrEmpty(selector)) { - return !(one == two); + // Finding multiple elements with an empty ID will return + // an empty list. However, finding by a CSS selector of '#' + // throws an exception, even in the multiple elements case, + // which means we need to short-circuit that behavior. + by.FindElementsMethod = (ISearchContext context) => new List().AsReadOnly(); } - /// - /// Gets a mechanism to find elements by their ID. - /// - /// The ID to find. - /// A object the driver can use to find the elements. - /// If is . - public static By Id(string idToFind) + return by; + } + + /// + /// Gets a mechanism to find elements by their link text. + /// + /// The link text to find. + /// A object the driver can use to find the elements. + /// If is null. + public static By LinkText(string linkTextToFind) + { + if (linkTextToFind == null) { - if (idToFind == null) - { - throw new ArgumentNullException(nameof(idToFind), "Cannot find elements with a null id attribute."); - } - - string selector = EscapeCssSelector(idToFind); - By by = new By(CssSelectorMechanism, "#" + selector); - by.Description = "By.Id: " + idToFind; - if (string.IsNullOrEmpty(selector)) - { - // Finding multiple elements with an empty ID will return - // an empty list. However, finding by a CSS selector of '#' - // throws an exception, even in the multiple elements case, - // which means we need to short-circuit that behavior. - by.FindElementsMethod = (ISearchContext context) => new List().AsReadOnly(); - } - - return by; + throw new ArgumentNullException(nameof(linkTextToFind), "Cannot find elements when link text is null."); } - /// - /// Gets a mechanism to find elements by their link text. - /// - /// The link text to find. - /// A object the driver can use to find the elements. - /// If is null. - public static By LinkText(string linkTextToFind) + return new By(LinkTextMechanism, linkTextToFind) { - if (linkTextToFind == null) - { - throw new ArgumentNullException(nameof(linkTextToFind), "Cannot find elements when link text is null."); - } - - return new By(LinkTextMechanism, linkTextToFind) - { - Description = "By.LinkText: " + linkTextToFind - }; - } + Description = "By.LinkText: " + linkTextToFind + }; + } - /// - /// Gets a mechanism to find elements by their name. - /// - /// The name to find. - /// A object the driver can use to find the elements. - /// If is null. - public static By Name(string nameToFind) + /// + /// Gets a mechanism to find elements by their name. + /// + /// The name to find. + /// A object the driver can use to find the elements. + /// If is null. + public static By Name(string nameToFind) + { + if (nameToFind == null) { - if (nameToFind == null) - { - throw new ArgumentNullException(nameof(nameToFind), "Cannot find elements when name text is null."); - } - - return new By(CssSelectorMechanism, $"*[name =\"{EscapeCssSelector(nameToFind)}\"]") - { - Description = "By.Name: " + nameToFind - }; + throw new ArgumentNullException(nameof(nameToFind), "Cannot find elements when name text is null."); } - /// - /// Gets a mechanism to find elements by an XPath query. - /// When searching within a WebElement using xpath be aware that WebDriver follows standard conventions: - /// a search prefixed with "//" will search the entire document, not just the children of this current node. - /// Use ".//" to limit your search to the children of this WebElement. - /// - /// The XPath query to use. - /// A object the driver can use to find the elements. - /// If is null. - public static By XPath(string xpathToFind) + return new By(CssSelectorMechanism, $"*[name =\"{EscapeCssSelector(nameToFind)}\"]") { - if (xpathToFind == null) - { - throw new ArgumentNullException(nameof(xpathToFind), "Cannot find elements when the XPath expression is null."); - } - - return new By(XPathSelectorMechanism, xpathToFind) - { - Description = "By.XPath: " + xpathToFind - }; - } + Description = "By.Name: " + nameToFind + }; + } - /// - /// Gets a mechanism to find elements by their CSS class. - /// - /// The CSS class to find. - /// A object the driver can use to find the elements. - /// If an element has many classes then this will match against each of them. - /// For example if the value is "one two onone", then the following values for the - /// className parameter will match: "one" and "two". - /// If is null. - public static By ClassName(string classNameToFind) + /// + /// Gets a mechanism to find elements by an XPath query. + /// When searching within a WebElement using xpath be aware that WebDriver follows standard conventions: + /// a search prefixed with "//" will search the entire document, not just the children of this current node. + /// Use ".//" to limit your search to the children of this WebElement. + /// + /// The XPath query to use. + /// A object the driver can use to find the elements. + /// If is null. + public static By XPath(string xpathToFind) + { + if (xpathToFind == null) { - if (classNameToFind == null) - { - throw new ArgumentNullException(nameof(classNameToFind), "Cannot find elements when the class name expression is null."); - } - - string selector = "." + EscapeCssSelector(classNameToFind); - if (selector.Contains(" ")) - { - // Finding elements by class name with whitespace is not allowed. - // However, converting the single class name to a valid CSS selector - // by prepending a '.' may result in a still-valid, but incorrect - // selector. Thus, we short-circuit that behavior here. - throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); - } - - return new By(CssSelectorMechanism, selector) - { - Description = "By.ClassName[Contains]: " + classNameToFind - }; + throw new ArgumentNullException(nameof(xpathToFind), "Cannot find elements when the XPath expression is null."); } - /// - /// Gets a mechanism to find elements by a partial match on their link text. - /// - /// The partial link text to find. - /// A object the driver can use to find the elements. - /// If is null. - public static By PartialLinkText(string partialLinkTextToFind) + return new By(XPathSelectorMechanism, xpathToFind) { - if (partialLinkTextToFind == null) - { - throw new ArgumentNullException(nameof(partialLinkTextToFind), "Cannot find elements when partial link text is null."); - } - - return new By(PartialLinkTextMechanism, partialLinkTextToFind) - { - Description = "By.PartialLinkText: " + partialLinkTextToFind - }; - } + Description = "By.XPath: " + xpathToFind + }; + } - /// - /// Gets a mechanism to find elements by their tag name. - /// - /// The tag name to find. - /// A object the driver can use to find the elements. - /// If is null. - public static By TagName(string tagNameToFind) + /// + /// Gets a mechanism to find elements by their CSS class. + /// + /// The CSS class to find. + /// A object the driver can use to find the elements. + /// If an element has many classes then this will match against each of them. + /// For example if the value is "one two onone", then the following values for the + /// className parameter will match: "one" and "two". + /// If is null. + public static By ClassName(string classNameToFind) + { + if (classNameToFind == null) { - if (tagNameToFind == null) - { - throw new ArgumentNullException(nameof(tagNameToFind), "Cannot find elements when name tag name is null."); - } - - return new By(TagNameMechanism, tagNameToFind) - { - Description = "By.TagName: " + tagNameToFind - }; + throw new ArgumentNullException(nameof(classNameToFind), "Cannot find elements when the class name expression is null."); } - /// - /// Gets a mechanism to find elements by their cascading style sheet (CSS) selector. - /// - /// The CSS selector to find. - /// A object the driver can use to find the elements. - /// If is null. - public static By CssSelector(string cssSelectorToFind) + string selector = "." + EscapeCssSelector(classNameToFind); + if (selector.Contains(" ")) { - if (cssSelectorToFind == null) - { - throw new ArgumentNullException(nameof(cssSelectorToFind), "Cannot find elements when name CSS selector is null."); - } - - return new By(CssSelectorMechanism, cssSelectorToFind) - { - Description = "By.CssSelector: " + cssSelectorToFind - }; + // Finding elements by class name with whitespace is not allowed. + // However, converting the single class name to a valid CSS selector + // by prepending a '.' may result in a still-valid, but incorrect + // selector. Thus, we short-circuit that behavior here. + throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); } - /// - /// Finds the first element matching the criteria. - /// - /// An object to use to search for the elements. - /// The first matching on the current context. - /// If no element matches the criteria. - public virtual IWebElement FindElement(ISearchContext context) + return new By(CssSelectorMechanism, selector) { - if (this.FindElementMethod is not { } findElementMethod) - { - throw new InvalidOperationException("FindElement method not set. Override the By.FindElement method, set the By.FindElementMethod property, or use a constructor that sets a query mechanism."); - } + Description = "By.ClassName[Contains]: " + classNameToFind + }; + } - return findElementMethod(context); + /// + /// Gets a mechanism to find elements by a partial match on their link text. + /// + /// The partial link text to find. + /// A object the driver can use to find the elements. + /// If is null. + public static By PartialLinkText(string partialLinkTextToFind) + { + if (partialLinkTextToFind == null) + { + throw new ArgumentNullException(nameof(partialLinkTextToFind), "Cannot find elements when partial link text is null."); } - /// - /// Finds all elements matching the criteria. - /// - /// An object to use to search for the elements. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - public virtual ReadOnlyCollection FindElements(ISearchContext context) + return new By(PartialLinkTextMechanism, partialLinkTextToFind) { - if (this.FindElementsMethod is not { } findElementsMethod) - { - throw new InvalidOperationException("FindElements method not set. Override the By.FindElements method, set the By.FindElementsMethod property, or use a constructor that sets a query mechanism."); - } + Description = "By.PartialLinkText: " + partialLinkTextToFind + }; + } - return findElementsMethod(context); + /// + /// Gets a mechanism to find elements by their tag name. + /// + /// The tag name to find. + /// A object the driver can use to find the elements. + /// If is null. + public static By TagName(string tagNameToFind) + { + if (tagNameToFind == null) + { + throw new ArgumentNullException(nameof(tagNameToFind), "Cannot find elements when name tag name is null."); } - /// - /// Gets a string representation of the finder. - /// - /// The string displaying the finder content. - public override string ToString() + return new By(TagNameMechanism, tagNameToFind) + { + Description = "By.TagName: " + tagNameToFind + }; + } + + /// + /// Gets a mechanism to find elements by their cascading style sheet (CSS) selector. + /// + /// The CSS selector to find. + /// A object the driver can use to find the elements. + /// If is null. + public static By CssSelector(string cssSelectorToFind) + { + if (cssSelectorToFind == null) { - return this.Description; + throw new ArgumentNullException(nameof(cssSelectorToFind), "Cannot find elements when name CSS selector is null."); } - /// - /// Determines whether the specified Object is equal - /// to the current Object. - /// - /// The Object to compare with the - /// current Object. - /// if the specified Object - /// is equal to the current Object; otherwise, - /// . - public override bool Equals(object? obj) + return new By(CssSelectorMechanism, cssSelectorToFind) { - var other = obj as By; + Description = "By.CssSelector: " + cssSelectorToFind + }; + } - // TODO(dawagner): This isn't ideal - return other != null && this.Description.Equals(other.Description); + /// + /// Finds the first element matching the criteria. + /// + /// An object to use to search for the elements. + /// The first matching on the current context. + /// If no element matches the criteria. + public virtual IWebElement FindElement(ISearchContext context) + { + if (this.FindElementMethod is not { } findElementMethod) + { + throw new InvalidOperationException("FindElement method not set. Override the By.FindElement method, set the By.FindElementMethod property, or use a constructor that sets a query mechanism."); } - /// - /// Serves as a hash function for a particular type. - /// - /// A hash code for the current Object. - public override int GetHashCode() + return findElementMethod(context); + } + + /// + /// Finds all elements matching the criteria. + /// + /// An object to use to search for the elements. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + public virtual ReadOnlyCollection FindElements(ISearchContext context) + { + if (this.FindElementsMethod is not { } findElementsMethod) { - return this.Description.GetHashCode(); + throw new InvalidOperationException("FindElements method not set. Override the By.FindElements method, set the By.FindElementsMethod property, or use a constructor that sets a query mechanism."); } - /// - /// Escapes invalid characters in a CSS selector. - /// - /// The selector to escape. - /// The selector with invalid characters escaped. - internal static string EscapeCssSelector(string selector) - { - string escaped = InvalidCharsRegex.Replace(selector, @"\$1"); - if (selector.Length > 0 && char.IsDigit(selector[0])) - { - int digitCode = 30 + int.Parse(selector.Substring(0, 1), CultureInfo.InvariantCulture); + return findElementsMethod(context); + } + + /// + /// Gets a string representation of the finder. + /// + /// The string displaying the finder content. + public override string ToString() + { + return this.Description; + } - escaped = $"\\{digitCode.ToString(CultureInfo.InvariantCulture)} {selector.Substring(1)}"; - } + /// + /// Determines whether the specified Object is equal + /// to the current Object. + /// + /// The Object to compare with the + /// current Object. + /// if the specified Object + /// is equal to the current Object; otherwise, + /// . + public override bool Equals(object? obj) + { + var other = obj as By; - return escaped; + // TODO(dawagner): This isn't ideal + return other != null && this.Description.Equals(other.Description); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// A hash code for the current Object. + public override int GetHashCode() + { + return this.Description.GetHashCode(); + } + + /// + /// Escapes invalid characters in a CSS selector. + /// + /// The selector to escape. + /// The selector with invalid characters escaped. + internal static string EscapeCssSelector(string selector) + { + string escaped = InvalidCharsRegex.Replace(selector, @"\$1"); + if (selector.Length > 0 && char.IsDigit(selector[0])) + { + int digitCode = 30 + int.Parse(selector.Substring(0, 1), CultureInfo.InvariantCulture); + + escaped = $"\\{digitCode.ToString(CultureInfo.InvariantCulture)} {selector.Substring(1)}"; } - private static readonly Regex InvalidCharsRegex = new Regex(@"([ '""\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]\(\)])", RegexOptions.Compiled); + return escaped; } + + private static readonly Regex InvalidCharsRegex = new Regex(@"([ '""\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]\(\)])", RegexOptions.Compiled); } diff --git a/dotnet/src/webdriver/CapabilityType.cs b/dotnet/src/webdriver/CapabilityType.cs index 4ee0f04884cbb..1d35063c63f69 100644 --- a/dotnet/src/webdriver/CapabilityType.cs +++ b/dotnet/src/webdriver/CapabilityType.cs @@ -20,189 +20,188 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides types of capabilities for the DesiredCapabilities object. +/// +public static class CapabilityType { /// - /// Provides types of capabilities for the DesiredCapabilities object. + /// Capability name used for the browser name. /// - public static class CapabilityType - { - /// - /// Capability name used for the browser name. - /// - public static readonly string BrowserName = "browserName"; - - /// - /// Capability name used for the browser version. - /// - public static readonly string BrowserVersion = "browserVersion"; - - /// - /// Capability name used for the platform name. - /// - public static readonly string PlatformName = "platformName"; - - /// - /// Capability name used for the browser platform. - /// - public static readonly string Platform = "platform"; - - /// - /// Capability name used for the browser version. - /// - public static readonly string Version = "version"; - - /// - /// Capability name used to indicate whether JavaScript is enabled for the browser. - /// - public static readonly string IsJavaScriptEnabled = "javascriptEnabled"; - - /// - /// Capability name used to indicate whether the browser can take screenshots. - /// - public static readonly string TakesScreenshot = "takesScreenshot"; - - /// - /// Capability name used to indicate whether the browser can handle alerts. - /// - public static readonly string HandlesAlerts = "handlesAlerts"; - - /// - /// Capability name used to indicate whether the browser can find elements via CSS selectors. - /// - public static readonly string SupportsFindingByCss = "cssSelectorsEnabled"; - - /// - /// Capability name used for the browser proxy. - /// - public static readonly string Proxy = "proxy"; - - /// - /// Capability name used to indicate whether the browser supports rotation. - /// - public static readonly string Rotatable = "rotatable"; - - /// - /// Capability name used to indicate whether the browser accepts SSL certificates. - /// - public static readonly string AcceptSslCertificates = "acceptSslCerts"; - - /// - /// Capability name used to indicate whether the browser accepts SSL certificates on W3C Endpoints - /// - public static readonly string AcceptInsecureCertificates = "acceptInsecureCerts"; - - /// - /// Capability name used to indicate whether the browser uses native events. - /// - public static readonly string HasNativeEvents = "nativeEvents"; - - /// - /// Capability name used to indicate how the browser handles unexpected alerts. - /// - public static readonly string UnexpectedAlertBehavior = "unexpectedAlertBehaviour"; - - /// - /// Capability name used to indicate how the browser handles unhandled user prompts. - /// - public static readonly string UnhandledPromptBehavior = "unhandledPromptBehavior"; - - /// - /// Capability name used to indicate the page load strategy for the browser. - /// - public static readonly string PageLoadStrategy = "pageLoadStrategy"; - - /// - /// Capability name used to indicate the logging preferences for the session. - /// - public static readonly string LoggingPreferences = "loggingPrefs"; - - /// - /// Capability name used to disable the check for overlapping elements. - /// - public static readonly string DisableOverlappedElementCheck = "overlappingCheckDisabled"; - - /// - /// Capability name used to enable the profiling log for the session. - /// - public static readonly string EnableProfiling = "webdriver.logging.profiler.enabled"; - - /// - /// Capability name used to indicate whether the driver supports geolocation context. - /// - public static readonly string SupportsLocationContext = "locationContextEnabled"; - - /// - /// Capability name used to indicate whether the driver supports application cache. - /// - public static readonly string SupportsApplicationCache = "applicationCacheEnabled"; - - /// - /// Capability name used to indicate whether the driver supports web storage. - /// - public static readonly string SupportsWebStorage = "webStorageEnabled"; - - /// - /// Capability name used to indicate whether the driver supports setting the browser window's size and position. - /// - public static readonly string SetWindowRect = "setWindowRect"; - - /// - /// Capability name used to get or set timeout values when creating a session. - /// - public static readonly string Timeouts = "timeouts"; - - /// - /// Capability name used to get or set whether <input type='file'/> elements must be visible to upload files. - /// - public static readonly string UseStrictFileInteractability = "strictFileInteractability"; - - /// - /// Capability name used to get a value indicating whether to request URL of a WebSocket - /// connection for bidirectional communication with a driver. - /// - public static readonly string WebSocketUrl = "webSocketUrl"; - - /// - /// Capability name used to get a value indicating whether files may be downloaded from remote end. - /// - public static readonly string EnableDownloads = "se:downloadsEnabled"; - - private static readonly HashSet KnownSpecCompliantCapabilityNames = new HashSet() - { - BrowserName, - BrowserVersion, - PlatformName, - AcceptInsecureCertificates, - PageLoadStrategy, - Proxy, - SetWindowRect, - Timeouts, - UnhandledPromptBehavior, - UseStrictFileInteractability, - WebSocketUrl - }; - - /// - /// Gets a value indicating whether a given capability name is compliant with the - /// W3C WebDriver Specification. - /// - /// The name of the capability to check for compliance. - /// if the capability name is valid according to the rules - /// of the specification; otherwise, . - public static bool IsSpecCompliantCapabilityName([NotNullWhen(true)] string? capabilityName) - { - if (capabilityName is null) - { - return false; - } + public static readonly string BrowserName = "browserName"; + + /// + /// Capability name used for the browser version. + /// + public static readonly string BrowserVersion = "browserVersion"; + + /// + /// Capability name used for the platform name. + /// + public static readonly string PlatformName = "platformName"; + + /// + /// Capability name used for the browser platform. + /// + public static readonly string Platform = "platform"; + + /// + /// Capability name used for the browser version. + /// + public static readonly string Version = "version"; + + /// + /// Capability name used to indicate whether JavaScript is enabled for the browser. + /// + public static readonly string IsJavaScriptEnabled = "javascriptEnabled"; + + /// + /// Capability name used to indicate whether the browser can take screenshots. + /// + public static readonly string TakesScreenshot = "takesScreenshot"; + + /// + /// Capability name used to indicate whether the browser can handle alerts. + /// + public static readonly string HandlesAlerts = "handlesAlerts"; + + /// + /// Capability name used to indicate whether the browser can find elements via CSS selectors. + /// + public static readonly string SupportsFindingByCss = "cssSelectorsEnabled"; + + /// + /// Capability name used for the browser proxy. + /// + public static readonly string Proxy = "proxy"; + + /// + /// Capability name used to indicate whether the browser supports rotation. + /// + public static readonly string Rotatable = "rotatable"; + + /// + /// Capability name used to indicate whether the browser accepts SSL certificates. + /// + public static readonly string AcceptSslCertificates = "acceptSslCerts"; + + /// + /// Capability name used to indicate whether the browser accepts SSL certificates on W3C Endpoints + /// + public static readonly string AcceptInsecureCertificates = "acceptInsecureCerts"; + + /// + /// Capability name used to indicate whether the browser uses native events. + /// + public static readonly string HasNativeEvents = "nativeEvents"; + + /// + /// Capability name used to indicate how the browser handles unexpected alerts. + /// + public static readonly string UnexpectedAlertBehavior = "unexpectedAlertBehaviour"; + + /// + /// Capability name used to indicate how the browser handles unhandled user prompts. + /// + public static readonly string UnhandledPromptBehavior = "unhandledPromptBehavior"; + + /// + /// Capability name used to indicate the page load strategy for the browser. + /// + public static readonly string PageLoadStrategy = "pageLoadStrategy"; + + /// + /// Capability name used to indicate the logging preferences for the session. + /// + public static readonly string LoggingPreferences = "loggingPrefs"; + + /// + /// Capability name used to disable the check for overlapping elements. + /// + public static readonly string DisableOverlappedElementCheck = "overlappingCheckDisabled"; + + /// + /// Capability name used to enable the profiling log for the session. + /// + public static readonly string EnableProfiling = "webdriver.logging.profiler.enabled"; + + /// + /// Capability name used to indicate whether the driver supports geolocation context. + /// + public static readonly string SupportsLocationContext = "locationContextEnabled"; + + /// + /// Capability name used to indicate whether the driver supports application cache. + /// + public static readonly string SupportsApplicationCache = "applicationCacheEnabled"; - if (KnownSpecCompliantCapabilityNames.Contains(capabilityName) || capabilityName.Contains(":")) - { - return true; - } + /// + /// Capability name used to indicate whether the driver supports web storage. + /// + public static readonly string SupportsWebStorage = "webStorageEnabled"; + /// + /// Capability name used to indicate whether the driver supports setting the browser window's size and position. + /// + public static readonly string SetWindowRect = "setWindowRect"; + + /// + /// Capability name used to get or set timeout values when creating a session. + /// + public static readonly string Timeouts = "timeouts"; + + /// + /// Capability name used to get or set whether <input type='file'/> elements must be visible to upload files. + /// + public static readonly string UseStrictFileInteractability = "strictFileInteractability"; + + /// + /// Capability name used to get a value indicating whether to request URL of a WebSocket + /// connection for bidirectional communication with a driver. + /// + public static readonly string WebSocketUrl = "webSocketUrl"; + + /// + /// Capability name used to get a value indicating whether files may be downloaded from remote end. + /// + public static readonly string EnableDownloads = "se:downloadsEnabled"; + + private static readonly HashSet KnownSpecCompliantCapabilityNames = new HashSet() + { + BrowserName, + BrowserVersion, + PlatformName, + AcceptInsecureCertificates, + PageLoadStrategy, + Proxy, + SetWindowRect, + Timeouts, + UnhandledPromptBehavior, + UseStrictFileInteractability, + WebSocketUrl + }; + + /// + /// Gets a value indicating whether a given capability name is compliant with the + /// W3C WebDriver Specification. + /// + /// The name of the capability to check for compliance. + /// if the capability name is valid according to the rules + /// of the specification; otherwise, . + public static bool IsSpecCompliantCapabilityName([NotNullWhen(true)] string? capabilityName) + { + if (capabilityName is null) + { return false; } + + if (KnownSpecCompliantCapabilityNames.Contains(capabilityName) || capabilityName.Contains(":")) + { + return true; + } + + return false; } } diff --git a/dotnet/src/webdriver/Chrome/ChromeDriver.cs b/dotnet/src/webdriver/Chrome/ChromeDriver.cs index b39f7cf72453f..419fb8bb70cc3 100644 --- a/dotnet/src/webdriver/Chrome/ChromeDriver.cs +++ b/dotnet/src/webdriver/Chrome/ChromeDriver.cs @@ -23,172 +23,171 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +/// +/// Provides a mechanism to write tests against Chrome +/// +/// +/// +/// [TestFixture] +/// public class Testing +/// { +/// private IWebDriver driver; +/// +/// [SetUp] +/// public void SetUp() +/// { +/// driver = new ChromeDriver(); +/// } +/// +/// [Test] +/// public void TestGoogle() +/// { +/// driver.Navigate().GoToUrl("http://www.google.co.uk/"); +/// /* +/// * Rest of the test +/// */ +/// } +/// +/// [TearDown] +/// public void TearDown() +/// { +/// driver.Quit(); +/// } +/// } +/// +/// +public class ChromeDriver : ChromiumDriver { + private static readonly Dictionary chromeCustomCommands = new Dictionary() + { + { ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute") }, + { GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks") }, + { SelectCastSinkCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/set_sink_to_use") }, + { StartCastTabMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_tab_mirroring") }, + { StartCastDesktopMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_desktop_mirroring") }, + { GetCastIssueMessageCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_issue_message") }, + { StopCastingCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/stop_casting") } + }; + /// - /// Provides a mechanism to write tests against Chrome + /// Initializes a new instance of the class. /// - /// - /// - /// [TestFixture] - /// public class Testing - /// { - /// private IWebDriver driver; - /// - /// [SetUp] - /// public void SetUp() - /// { - /// driver = new ChromeDriver(); - /// } - /// - /// [Test] - /// public void TestGoogle() - /// { - /// driver.Navigate().GoToUrl("http://www.google.co.uk/"); - /// /* - /// * Rest of the test - /// */ - /// } - /// - /// [TearDown] - /// public void TearDown() - /// { - /// driver.Quit(); - /// } - /// } - /// - /// - public class ChromeDriver : ChromiumDriver + public ChromeDriver() + : this(new ChromeOptions()) { - private static readonly Dictionary chromeCustomCommands = new Dictionary() - { - { ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute") }, - { GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks") }, - { SelectCastSinkCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/set_sink_to_use") }, - { StartCastTabMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_tab_mirroring") }, - { StartCastDesktopMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_desktop_mirroring") }, - { GetCastIssueMessageCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_issue_message") }, - { StopCastingCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/stop_casting") } - }; - - /// - /// Initializes a new instance of the class. - /// - public ChromeDriver() - : this(new ChromeOptions()) - { - } + } - /// - /// Initializes a new instance of the class using the specified options. - /// - /// The to be used with the Chrome driver. - /// If is . - public ChromeDriver(ChromeOptions options) - : this(ChromeDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified options. + /// + /// The to be used with the Chrome driver. + /// If is . + public ChromeDriver(ChromeOptions options) + : this(ChromeDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified driver service. - /// - /// The used to initialize the driver. - /// If is . - public ChromeDriver(ChromeDriverService service) - : this(service, new ChromeOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified driver service. + /// + /// The used to initialize the driver. + /// If is . + public ChromeDriver(ChromeDriverService service) + : this(service, new ChromeOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing ChromeDriver.exe. - /// - /// The full path to the directory containing ChromeDriver.exe. - public ChromeDriver(string chromeDriverDirectory) - : this(chromeDriverDirectory, new ChromeOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing ChromeDriver.exe. + /// + /// The full path to the directory containing ChromeDriver.exe. + public ChromeDriver(string chromeDriverDirectory) + : this(chromeDriverDirectory, new ChromeOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing ChromeDriver.exe and options. - /// - /// The full path to the directory containing ChromeDriver.exe. - /// The to be used with the Chrome driver. - /// If is . - public ChromeDriver(string chromeDriverDirectory, ChromeOptions options) - : this(chromeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing ChromeDriver.exe and options. + /// + /// The full path to the directory containing ChromeDriver.exe. + /// The to be used with the Chrome driver. + /// If is . + public ChromeDriver(string chromeDriverDirectory, ChromeOptions options) + : this(chromeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing ChromeDriver.exe, options, and command timeout. - /// - /// The full path to the directory containing ChromeDriver.exe. - /// The to be used with the Chrome driver. - /// The maximum amount of time to wait for each command. - /// If is . - public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpan commandTimeout) - : this(ChromeDriverService.CreateDefaultService(chromeDriverDirectory), options, commandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing ChromeDriver.exe, options, and command timeout. + /// + /// The full path to the directory containing ChromeDriver.exe. + /// The to be used with the Chrome driver. + /// The maximum amount of time to wait for each command. + /// If is . + public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpan commandTimeout) + : this(ChromeDriverService.CreateDefaultService(chromeDriverDirectory), options, commandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified - /// and options. - /// - /// The to use. - /// The used to initialize the driver. - /// If or are . - public ChromeDriver(ChromeDriverService service, ChromeOptions options) - : this(service, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified + /// and options. + /// + /// The to use. + /// The used to initialize the driver. + /// If or are . + public ChromeDriver(ChromeDriverService service, ChromeOptions options) + : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified . - /// - /// The to use. - /// The to be used with the Chrome driver. - /// The maximum amount of time to wait for each command. - /// If or are . - public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout) - : base(service, options, commandTimeout) - { - this.AddCustomChromeCommands(); - } + /// + /// Initializes a new instance of the class using the specified . + /// + /// The to use. + /// The to be used with the Chrome driver. + /// The maximum amount of time to wait for each command. + /// If or are . + public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout) + : base(service, options, commandTimeout) + { + this.AddCustomChromeCommands(); + } - /// - /// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver. - /// The keys of the dictionary are the names assigned to the command; the values are the - /// objects describing the command behavior. - /// - public static IReadOnlyDictionary CustomCommandDefinitions + /// + /// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver. + /// The keys of the dictionary are the names assigned to the command; the values are the + /// objects describing the command behavior. + /// + public static IReadOnlyDictionary CustomCommandDefinitions + { + get { - get + Dictionary customCommands = new Dictionary(); + foreach (KeyValuePair entry in ChromiumCustomCommands) { - Dictionary customCommands = new Dictionary(); - foreach (KeyValuePair entry in ChromiumCustomCommands) - { - customCommands[entry.Key] = entry.Value; - } - - foreach (KeyValuePair entry in chromeCustomCommands) - { - customCommands[entry.Key] = entry.Value; - } + customCommands[entry.Key] = entry.Value; + } - return new ReadOnlyDictionary(customCommands); + foreach (KeyValuePair entry in chromeCustomCommands) + { + customCommands[entry.Key] = entry.Value; } + + return new ReadOnlyDictionary(customCommands); } + } - private void AddCustomChromeCommands() + private void AddCustomChromeCommands() + { + foreach (KeyValuePair entry in CustomCommandDefinitions) { - foreach (KeyValuePair entry in CustomCommandDefinitions) - { - this.RegisterInternalDriverCommand(entry.Key, entry.Value); - } + this.RegisterInternalDriverCommand(entry.Key, entry.Value); } } } diff --git a/dotnet/src/webdriver/Chrome/ChromeDriverService.cs b/dotnet/src/webdriver/Chrome/ChromeDriverService.cs index a294c72fd3097..4b701ceed1598 100644 --- a/dotnet/src/webdriver/Chrome/ChromeDriverService.cs +++ b/dotnet/src/webdriver/Chrome/ChromeDriverService.cs @@ -21,74 +21,73 @@ using OpenQA.Selenium.Internal; using System.IO; -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +/// +/// Exposes the service provided by the native ChromeDriver executable. +/// +public sealed class ChromeDriverService : ChromiumDriverService { + private const string DefaultChromeDriverServiceExecutableName = "chromedriver"; + /// - /// Exposes the service provided by the native ChromeDriver executable. + /// Initializes a new instance of the class. /// - public sealed class ChromeDriverService : ChromiumDriverService + /// The full path to the ChromeDriver executable. + /// The file name of the ChromeDriver executable. + /// The port on which the ChromeDriver executable should listen. + private ChromeDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, executableFileName, port) { - private const string DefaultChromeDriverServiceExecutableName = "chromedriver"; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the ChromeDriver executable. - /// The file name of the ChromeDriver executable. - /// The port on which the ChromeDriver executable should listen. - private ChromeDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, executableFileName, port) - { - } + } - /// - protected override DriverOptions GetDefaultDriverOptions() - { - return new ChromeOptions(); - } + /// + protected override DriverOptions GetDefaultDriverOptions() + { + return new ChromeOptions(); + } - /// - /// Creates a default instance of the ChromeDriverService. - /// - /// A ChromeDriverService that implements default settings. - public static ChromeDriverService CreateDefaultService() - { - return new ChromeDriverService(null, null, PortUtilities.FindFreePort()); - } + /// + /// Creates a default instance of the ChromeDriverService. + /// + /// A ChromeDriverService that implements default settings. + public static ChromeDriverService CreateDefaultService() + { + return new ChromeDriverService(null, null, PortUtilities.FindFreePort()); + } - /// - /// Creates a default instance of the ChromeDriverService using a specified path to the ChromeDriver executable. - /// - /// The path to the executable or the directory containing the ChromeDriver executable. - /// A ChromeDriverService using a random port. - public static ChromeDriverService CreateDefaultService(string? driverPath) + /// + /// Creates a default instance of the ChromeDriverService using a specified path to the ChromeDriver executable. + /// + /// The path to the executable or the directory containing the ChromeDriver executable. + /// A ChromeDriverService using a random port. + public static ChromeDriverService CreateDefaultService(string? driverPath) + { + if (File.Exists(driverPath)) { - if (File.Exists(driverPath)) - { - string fileName = Path.GetFileName(driverPath); - string driverFolder = Path.GetDirectoryName(driverPath)!; - - return CreateDefaultService(driverFolder, fileName); - } - else - { - string fileName = ChromiumDriverServiceFileName(DefaultChromeDriverServiceExecutableName); - string? driverFolder = driverPath; + string fileName = Path.GetFileName(driverPath); + string driverFolder = Path.GetDirectoryName(driverPath)!; - return CreateDefaultService(driverFolder, fileName); - } + return CreateDefaultService(driverFolder, fileName); } - - /// - /// Creates a default instance of the ChromeDriverService using a specified path to the ChromeDriver executable with the given name. - /// - /// The directory containing the ChromeDriver executable. - /// The name of the ChromeDriver executable file. - /// A ChromeDriverService using a random port. - public static ChromeDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + else { - return new ChromeDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); + string fileName = ChromiumDriverServiceFileName(DefaultChromeDriverServiceExecutableName); + string? driverFolder = driverPath; + + return CreateDefaultService(driverFolder, fileName); } + } + /// + /// Creates a default instance of the ChromeDriverService using a specified path to the ChromeDriver executable with the given name. + /// + /// The directory containing the ChromeDriver executable. + /// The name of the ChromeDriver executable file. + /// A ChromeDriverService using a random port. + public static ChromeDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + { + return new ChromeDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); } + } diff --git a/dotnet/src/webdriver/Chrome/ChromeOptions.cs b/dotnet/src/webdriver/Chrome/ChromeOptions.cs index cf47856dc5ac3..85b0c287e8b82 100644 --- a/dotnet/src/webdriver/Chrome/ChromeOptions.cs +++ b/dotnet/src/webdriver/Chrome/ChromeOptions.cs @@ -21,75 +21,74 @@ using System; using System.Globalization; -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +/// +/// Class to manage options specific to +/// +/// +/// Used with ChromeDriver.exe v17.0.963.0 and higher. +/// +/// +/// +/// ChromeOptions options = new ChromeOptions(); +/// options.AddExtensions("\path\to\extension.crx"); +/// options.BinaryLocation = "\path\to\chrome"; +/// +/// +/// For use with ChromeDriver: +/// +/// +/// ChromeDriver driver = new ChromeDriver(options); +/// +/// +/// For use with RemoteWebDriver: +/// +/// +/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); +/// +/// +public class ChromeOptions : ChromiumOptions { + private const string ChromeOptionsCapabilityName = "chromeOptions"; + private const string BrowserNameValue = "chrome"; + /// - /// Class to manage options specific to + /// Initializes a new instance of the class. /// - /// - /// Used with ChromeDriver.exe v17.0.963.0 and higher. - /// - /// - /// - /// ChromeOptions options = new ChromeOptions(); - /// options.AddExtensions("\path\to\extension.crx"); - /// options.BinaryLocation = "\path\to\chrome"; - /// - /// - /// For use with ChromeDriver: - /// - /// - /// ChromeDriver driver = new ChromeDriver(options); - /// - /// - /// For use with RemoteWebDriver: - /// - /// - /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); - /// - /// - public class ChromeOptions : ChromiumOptions + public ChromeOptions() : base() { - private const string ChromeOptionsCapabilityName = "chromeOptions"; - private const string BrowserNameValue = "chrome"; - - /// - /// Initializes a new instance of the class. - /// - public ChromeOptions() : base() - { - this.BrowserName = BrowserNameValue; - } + this.BrowserName = BrowserNameValue; + } - /// - /// Gets the vendor prefix to apply to Chromium-specific capability names. - /// - protected override string VendorPrefix => "goog"; + /// + /// Gets the vendor prefix to apply to Chromium-specific capability names. + /// + protected override string VendorPrefix => "goog"; - /// - /// Gets the name of the capability used to store Chromium options in - /// an object. - /// - public override string CapabilityName => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.VendorPrefix, ChromeOptionsCapabilityName); + /// + /// Gets the name of the capability used to store Chromium options in + /// an object. + /// + public override string CapabilityName => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.VendorPrefix, ChromeOptionsCapabilityName); - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the Chrome driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// Calling this method adds capabilities to the Chrome-specific options object passed to - /// WebDriver executable (property name 'goog:chromeOptions'). - public void AddAdditionalChromeOption(string optionName, object optionValue) - { - this.AddAdditionalChromiumOption(optionName, optionValue); - } + /// + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the Chrome driver. + /// + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// Calling this method adds capabilities to the Chrome-specific options object passed to + /// WebDriver executable (property name 'goog:chromeOptions'). + public void AddAdditionalChromeOption(string optionName, object optionValue) + { + this.AddAdditionalChromiumOption(optionName, optionValue); } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumAndroidOptions.cs b/dotnet/src/webdriver/Chromium/ChromiumAndroidOptions.cs index 329fa887fbdbb..4ef773e171ca8 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumAndroidOptions.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumAndroidOptions.cs @@ -19,29 +19,28 @@ using OpenQA.Selenium.Internal; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Generates the capabilities for automating Chromium applications on Android +/// +public class ChromiumAndroidOptions : AndroidOptions { /// - /// Generates the capabilities for automating Chromium applications on Android + /// Initializes a new instance of the class. /// - public class ChromiumAndroidOptions : AndroidOptions + /// + public ChromiumAndroidOptions(string androidPackage) : base(androidPackage) { - /// - /// Initializes a new instance of the class. - /// - /// - public ChromiumAndroidOptions(string androidPackage) : base(androidPackage) - { - } + } - /// - /// Gets or sets a value indicating whether to use an already running app. - /// - public bool UseRunningApp { get; set; } + /// + /// Gets or sets a value indicating whether to use an already running app. + /// + public bool UseRunningApp { get; set; } - /// - /// Gets or sets the process name of the Activity hosting the app. - /// - public string? AndroidProcess { get; set; } - } + /// + /// Gets or sets the process name of the Activity hosting the app. + /// + public string? AndroidProcess { get; set; } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs index afe3d1b551fc3..a53c848c851fb 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs @@ -26,486 +26,485 @@ using System.IO; using System.Threading.Tasks; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Provides an abstract way to access Chromium-based browsers to run tests. +/// +public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools { /// - /// Provides an abstract way to access Chromium-based browsers to run tests. + /// Accept untrusted SSL Certificates + /// + public static readonly bool AcceptUntrustedCertificates = true; + + /// + /// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser. + /// + public static readonly string ExecuteCdp = "executeCdpCommand"; + + /// + /// Command for getting cast sinks in a driver for a Chromium-based browser. + /// + public static readonly string GetCastSinksCommand = "getCastSinks"; + + /// + /// Command for selecting a cast sink in a driver for a Chromium-based browser. + /// + public static readonly string SelectCastSinkCommand = "selectCastSink"; + + /// + /// Command for starting cast tab mirroring in a driver for a Chromium-based browser. + /// + public static readonly string StartCastTabMirroringCommand = "startCastTabMirroring"; + + /// + /// Command for starting cast desktop mirroring in a driver for a Chromium-based browser. + /// + public static readonly string StartCastDesktopMirroringCommand = "startCastDesktopMirroring"; + + /// + /// Command for getting a cast issued message in a driver for a Chromium-based browser. + /// + public static readonly string GetCastIssueMessageCommand = "getCastIssueMessage"; + + /// + /// Command for stopping casting in a driver for a Chromium-based browser. + /// + public static readonly string StopCastingCommand = "stopCasting"; + + /// + /// Command for getting the simulated network conditions in a driver for a Chromium-based browser. + /// + public static readonly string GetNetworkConditionsCommand = "getNetworkConditions"; + + /// + /// Command for setting the simulated network conditions in a driver for a Chromium-based browser. /// - public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools + public static readonly string SetNetworkConditionsCommand = "setNetworkConditions"; + + /// + /// Command for deleting the simulated network conditions in a driver for a Chromium-based browser. + /// + public static readonly string DeleteNetworkConditionsCommand = "deleteNetworkConditions"; + + /// + /// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser. + /// + public static readonly string SendChromeCommand = "sendChromeCommand"; + + /// + /// Command for executing a Chrome DevTools Protocol command that returns a result in a driver for a Chromium-based browser. + /// + public static readonly string SendChromeCommandWithResult = "sendChromeCommandWithResult"; + + /// + /// Command for launching an app in a driver for a Chromium-based browser. + /// + public static readonly string LaunchAppCommand = "launchAppCommand"; + + /// + /// Command for setting permissions in a driver for a Chromium-based browser. + /// + public static readonly string SetPermissionCommand = "setPermission"; + + private readonly string optionsCapabilityName; + private DevToolsSession? devToolsSession; + + private static readonly Dictionary chromiumCustomCommands = new Dictionary() { - /// - /// Accept untrusted SSL Certificates - /// - public static readonly bool AcceptUntrustedCertificates = true; - - /// - /// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser. - /// - public static readonly string ExecuteCdp = "executeCdpCommand"; - - /// - /// Command for getting cast sinks in a driver for a Chromium-based browser. - /// - public static readonly string GetCastSinksCommand = "getCastSinks"; - - /// - /// Command for selecting a cast sink in a driver for a Chromium-based browser. - /// - public static readonly string SelectCastSinkCommand = "selectCastSink"; - - /// - /// Command for starting cast tab mirroring in a driver for a Chromium-based browser. - /// - public static readonly string StartCastTabMirroringCommand = "startCastTabMirroring"; - - /// - /// Command for starting cast desktop mirroring in a driver for a Chromium-based browser. - /// - public static readonly string StartCastDesktopMirroringCommand = "startCastDesktopMirroring"; - - /// - /// Command for getting a cast issued message in a driver for a Chromium-based browser. - /// - public static readonly string GetCastIssueMessageCommand = "getCastIssueMessage"; - - /// - /// Command for stopping casting in a driver for a Chromium-based browser. - /// - public static readonly string StopCastingCommand = "stopCasting"; - - /// - /// Command for getting the simulated network conditions in a driver for a Chromium-based browser. - /// - public static readonly string GetNetworkConditionsCommand = "getNetworkConditions"; - - /// - /// Command for setting the simulated network conditions in a driver for a Chromium-based browser. - /// - public static readonly string SetNetworkConditionsCommand = "setNetworkConditions"; - - /// - /// Command for deleting the simulated network conditions in a driver for a Chromium-based browser. - /// - public static readonly string DeleteNetworkConditionsCommand = "deleteNetworkConditions"; - - /// - /// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser. - /// - public static readonly string SendChromeCommand = "sendChromeCommand"; - - /// - /// Command for executing a Chrome DevTools Protocol command that returns a result in a driver for a Chromium-based browser. - /// - public static readonly string SendChromeCommandWithResult = "sendChromeCommandWithResult"; - - /// - /// Command for launching an app in a driver for a Chromium-based browser. - /// - public static readonly string LaunchAppCommand = "launchAppCommand"; - - /// - /// Command for setting permissions in a driver for a Chromium-based browser. - /// - public static readonly string SetPermissionCommand = "setPermission"; - - private readonly string optionsCapabilityName; - private DevToolsSession? devToolsSession; - - private static readonly Dictionary chromiumCustomCommands = new Dictionary() - { - { GetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions") }, - { SetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions") }, - { DeleteNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/chromium/network_conditions") }, - { SendChromeCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command") }, - { SendChromeCommandWithResult, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command_and_get_result") }, - { LaunchAppCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/launch_app") }, - { SetPermissionCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/permissions") } - }; - - /// - /// Initializes a new instance of the class using the specified . - /// - /// The to use. - /// The to be used with the ChromiumDriver. - /// The maximum amount of time to wait for each command. - /// If or are . - /// If the Chromium options capability name is . - protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout) - : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) - { - this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options)); - } + { GetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions") }, + { SetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions") }, + { DeleteNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/chromium/network_conditions") }, + { SendChromeCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command") }, + { SendChromeCommandWithResult, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command_and_get_result") }, + { LaunchAppCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/launch_app") }, + { SetPermissionCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/permissions") } + }; - /// - /// Gets the dictionary of custom Chromium commands registered with the driver. - /// - protected static IReadOnlyDictionary ChromiumCustomCommands => new ReadOnlyDictionary(chromiumCustomCommands); - - /// - /// Uses DriverFinder to set Service attributes if necessary when creating the command executor - /// - /// - /// - /// - /// - private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) - { - if (service is null) - { - throw new ArgumentNullException(nameof(service)); - } + /// + /// Initializes a new instance of the class using the specified . + /// + /// The to use. + /// The to be used with the ChromiumDriver. + /// The maximum amount of time to wait for each command. + /// If or are . + /// If the Chromium options capability name is . + protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout) + : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + { + this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options)); + } - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + /// + /// Gets the dictionary of custom Chromium commands registered with the driver. + /// + protected static IReadOnlyDictionary ChromiumCustomCommands => new ReadOnlyDictionary(chromiumCustomCommands); - if (service.DriverServicePath == null) - { - DriverFinder finder = new DriverFinder(options); - string fullServicePath = finder.GetDriverPath(); - service.DriverServicePath = Path.GetDirectoryName(fullServicePath); - service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); - if (finder.TryGetBrowserPath(out string? browserPath)) - { - options.BinaryLocation = browserPath; - options.BrowserVersion = null; - } - } - return new DriverServiceCommandExecutor(service, commandTimeout); + /// + /// Uses DriverFinder to set Service attributes if necessary when creating the command executor + /// + /// + /// + /// + /// + private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + { + if (service is null) + { + throw new ArgumentNullException(nameof(service)); } - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// The Chromium driver does not allow a file detector to be set, - /// as the server component of the Chromium driver only - /// allows uploads from the local computer environment. Attempting to set - /// this property has no effect, but does not throw an exception. If you - /// are attempting to run the Chromium driver remotely, use - /// in conjunction with a standalone WebDriver server. - public override IFileDetector FileDetector + if (options is null) { - get => base.FileDetector; - set { } + throw new ArgumentNullException(nameof(options)); } - /// - /// Gets a value indicating whether a DevTools session is active. - /// - [MemberNotNullWhen(true, nameof(devToolsSession))] - public bool HasActiveDevToolsSession => this.devToolsSession != null; - - /// - /// Gets or sets the network condition emulation for Chromium. - /// - /// If the value is set to . - public ChromiumNetworkConditions NetworkConditions + if (service.DriverServicePath == null) { - get + DriverFinder finder = new DriverFinder(options); + string fullServicePath = finder.GetDriverPath(); + service.DriverServicePath = Path.GetDirectoryName(fullServicePath); + service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); + if (finder.TryGetBrowserPath(out string? browserPath)) { - Response response = this.Execute(GetNetworkConditionsCommand, null); - if (response.Value is not Dictionary responseDictionary) - { - throw new WebDriverException($"GetNetworkConditions command returned successfully, but data was not an object: {response.Value}"); - } - - return ChromiumNetworkConditions.FromDictionary(responseDictionary); + options.BinaryLocation = browserPath; + options.BrowserVersion = null; } + } + return new DriverServiceCommandExecutor(service, commandTimeout); + } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value), "value must not be null"); - } + /// + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. + /// + /// The Chromium driver does not allow a file detector to be set, + /// as the server component of the Chromium driver only + /// allows uploads from the local computer environment. Attempting to set + /// this property has no effect, but does not throw an exception. If you + /// are attempting to run the Chromium driver remotely, use + /// in conjunction with a standalone WebDriver server. + public override IFileDetector FileDetector + { + get => base.FileDetector; + set { } + } - Dictionary parameters = new Dictionary(); - parameters["network_conditions"] = value; + /// + /// Gets a value indicating whether a DevTools session is active. + /// + [MemberNotNullWhen(true, nameof(devToolsSession))] + public bool HasActiveDevToolsSession => this.devToolsSession != null; - this.Execute(SetNetworkConditionsCommand, parameters); + /// + /// Gets or sets the network condition emulation for Chromium. + /// + /// If the value is set to . + public ChromiumNetworkConditions NetworkConditions + { + get + { + Response response = this.Execute(GetNetworkConditionsCommand, null); + if (response.Value is not Dictionary responseDictionary) + { + throw new WebDriverException($"GetNetworkConditions command returned successfully, but data was not an object: {response.Value}"); } + + return ChromiumNetworkConditions.FromDictionary(responseDictionary); } - /// - /// Launches a Chromium based application. - /// - /// ID of the chromium app to launch. - /// If is . - public void LaunchApp(string id) + set { - if (id == null) + if (value == null) { - throw new ArgumentNullException(nameof(id), "id must not be null"); + throw new ArgumentNullException(nameof(value), "value must not be null"); } Dictionary parameters = new Dictionary(); - parameters["id"] = id; + parameters["network_conditions"] = value; - this.Execute(LaunchAppCommand, parameters); + this.Execute(SetNetworkConditionsCommand, parameters); } + } - /// - /// Set supported permission on browser. - /// - /// Name of item to set the permission on. - /// Value to set the permission to. - /// If or are . - public void SetPermission(string permissionName, string permissionValue) + /// + /// Launches a Chromium based application. + /// + /// ID of the chromium app to launch. + /// If is . + public void LaunchApp(string id) + { + if (id == null) { - if (permissionName == null) - { - throw new ArgumentNullException(nameof(permissionName), "name must not be null"); - } + throw new ArgumentNullException(nameof(id), "id must not be null"); + } - if (permissionValue == null) - { - throw new ArgumentNullException(nameof(permissionValue), "value must not be null"); - } + Dictionary parameters = new Dictionary(); + parameters["id"] = id; - Dictionary nameParameter = new Dictionary(); - nameParameter["name"] = permissionName; - Dictionary parameters = new Dictionary(); - parameters["descriptor"] = nameParameter; - parameters["state"] = permissionValue; - this.Execute(SetPermissionCommand, parameters); - } + this.Execute(LaunchAppCommand, parameters); + } - /// - /// Executes a custom Chrome Dev Tools Protocol Command. - /// - /// Name of the command to execute. - /// Parameters of the command to execute. - /// An object representing the result of the command, if applicable. - /// If is . - public object? ExecuteCdpCommand(string commandName, Dictionary commandParameters) + /// + /// Set supported permission on browser. + /// + /// Name of item to set the permission on. + /// Value to set the permission to. + /// If or are . + public void SetPermission(string permissionName, string permissionValue) + { + if (permissionName == null) { - if (commandName == null) - { - throw new ArgumentNullException(nameof(commandName), "commandName must not be null"); - } - - Dictionary parameters = new Dictionary(); - parameters["cmd"] = commandName; - parameters["params"] = commandParameters; - Response response = this.Execute(ExecuteCdp, parameters); - return response.Value; + throw new ArgumentNullException(nameof(permissionName), "name must not be null"); } - /// - /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. - /// - /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession() + if (permissionValue == null) { - return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); + throw new ArgumentNullException(nameof(permissionValue), "value must not be null"); } - /// - /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. - /// - /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession(DevToolsOptions options) + Dictionary nameParameter = new Dictionary(); + nameParameter["name"] = permissionName; + Dictionary parameters = new Dictionary(); + parameters["descriptor"] = nameParameter; + parameters["state"] = permissionValue; + this.Execute(SetPermissionCommand, parameters); + } + + /// + /// Executes a custom Chrome Dev Tools Protocol Command. + /// + /// Name of the command to execute. + /// Parameters of the command to execute. + /// An object representing the result of the command, if applicable. + /// If is . + public object? ExecuteCdpCommand(string commandName, Dictionary commandParameters) + { + if (commandName == null) { - if (this.devToolsSession == null) - { - if (!this.Capabilities.HasCapability(this.optionsCapabilityName)) - { - throw new WebDriverException("Cannot find " + this.optionsCapabilityName + " capability for driver"); - } + throw new ArgumentNullException(nameof(commandName), "commandName must not be null"); + } - object? optionsCapabilityObject = this.Capabilities.GetCapability(this.optionsCapabilityName); - if (optionsCapabilityObject is not Dictionary optionsCapability) - { - throw new WebDriverException($"Found {this.optionsCapabilityName} capability, but is not an object: {optionsCapabilityObject}"); - } + Dictionary parameters = new Dictionary(); + parameters["cmd"] = commandName; + parameters["params"] = commandParameters; + Response response = this.Execute(ExecuteCdp, parameters); + return response.Value; + } - if (!optionsCapability.TryGetValue("debuggerAddress", out object? debuggerAddress)) - { - throw new WebDriverException("Did not find debuggerAddress capability in " + this.optionsCapabilityName); - } + /// + /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. + /// + /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession() + { + return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); + } - try - { - DevToolsSession session = new DevToolsSession(debuggerAddress?.ToString()!, options); - Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult(); - this.devToolsSession = session; - } - catch (Exception e) - { - throw new WebDriverException("Unexpected error creating WebSocket DevTools session.", e); - } + /// + /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. + /// + /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession(DevToolsOptions options) + { + if (this.devToolsSession == null) + { + if (!this.Capabilities.HasCapability(this.optionsCapabilityName)) + { + throw new WebDriverException("Cannot find " + this.optionsCapabilityName + " capability for driver"); } - return this.devToolsSession; - } + object? optionsCapabilityObject = this.Capabilities.GetCapability(this.optionsCapabilityName); + if (optionsCapabilityObject is not Dictionary optionsCapability) + { + throw new WebDriverException($"Found {this.optionsCapabilityName} capability, but is not an object: {optionsCapabilityObject}"); + } - /// - /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. - /// - /// The version of the Chromium Developer Tools protocol to use. Defaults to autodetect the protocol version. - /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. - [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession(int devToolsProtocolVersion) - { - return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = devToolsProtocolVersion }); - } + if (!optionsCapability.TryGetValue("debuggerAddress", out object? debuggerAddress)) + { + throw new WebDriverException("Did not find debuggerAddress capability in " + this.optionsCapabilityName); + } - /// - /// Closes a DevTools session. - /// - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public void CloseDevToolsSession() - { - if (this.devToolsSession != null) + try { - Task.Run(async () => await this.devToolsSession.StopSession(manualDetach: true)).GetAwaiter().GetResult(); + DevToolsSession session = new DevToolsSession(debuggerAddress?.ToString()!, options); + Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult(); + this.devToolsSession = session; + } + catch (Exception e) + { + throw new WebDriverException("Unexpected error creating WebSocket DevTools session.", e); } } - /// - /// Clears simulated network conditions. - /// - public void ClearNetworkConditions() + return this.devToolsSession; + } + + /// + /// Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol. + /// + /// The version of the Chromium Developer Tools protocol to use. Defaults to autodetect the protocol version. + /// The active session to use to communicate with the Chromium Developer Tools debugging protocol. + [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession(int devToolsProtocolVersion) + { + return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = devToolsProtocolVersion }); + } + + /// + /// Closes a DevTools session. + /// + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public void CloseDevToolsSession() + { + if (this.devToolsSession != null) { - this.Execute(DeleteNetworkConditionsCommand, null); + Task.Run(async () => await this.devToolsSession.StopSession(manualDetach: true)).GetAwaiter().GetResult(); } + } + + /// + /// Clears simulated network conditions. + /// + public void ClearNetworkConditions() + { + this.Execute(DeleteNetworkConditionsCommand, null); + } - /// - /// Returns the list of cast sinks (Cast devices) available to the Chrome media router. - /// - /// The list of available sinks. - public List> GetCastSinks() + /// + /// Returns the list of cast sinks (Cast devices) available to the Chrome media router. + /// + /// The list of available sinks. + public List> GetCastSinks() + { + List> returnValue = new List>(); + Response response = this.Execute(GetCastSinksCommand, null); + if (response.Value is object?[] responseValue) { - List> returnValue = new List>(); - Response response = this.Execute(GetCastSinksCommand, null); - if (response.Value is object?[] responseValue) + foreach (object? entry in responseValue) { - foreach (object? entry in responseValue) + if (entry is Dictionary entryValue) { - if (entry is Dictionary entryValue) + Dictionary sink = new Dictionary(); + foreach (KeyValuePair pair in entryValue) { - Dictionary sink = new Dictionary(); - foreach (KeyValuePair pair in entryValue) - { - sink[pair.Key] = pair.Value!.ToString()!; - } - - returnValue.Add(sink); + sink[pair.Key] = pair.Value!.ToString()!; } + + returnValue.Add(sink); } } - return returnValue; } + return returnValue; + } - /// - /// Selects a cast sink (Cast device) as the recipient of media router intents (connect or play). - /// - /// Name of the target sink (device). - public void SelectCastSink(string deviceName) + /// + /// Selects a cast sink (Cast device) as the recipient of media router intents (connect or play). + /// + /// Name of the target sink (device). + public void SelectCastSink(string deviceName) + { + if (deviceName == null) { - if (deviceName == null) - { - throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); - } - - Dictionary parameters = new Dictionary(); - parameters["sinkName"] = deviceName; - this.Execute(SelectCastSinkCommand, parameters); + throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); } - /// - /// Initiates tab mirroring for the current browser tab on the specified device. - /// - /// Name of the target sink (device). - public void StartTabMirroring(string deviceName) - { - if (deviceName == null) - { - throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); - } - - Dictionary parameters = new Dictionary(); - parameters["sinkName"] = deviceName; - this.Execute(StartCastTabMirroringCommand, parameters); - } + Dictionary parameters = new Dictionary(); + parameters["sinkName"] = deviceName; + this.Execute(SelectCastSinkCommand, parameters); + } - /// - /// Initiates mirroring of the desktop on the specified device. - /// - /// Name of the target sink (device). - public void StartDesktopMirroring(string deviceName) + /// + /// Initiates tab mirroring for the current browser tab on the specified device. + /// + /// Name of the target sink (device). + public void StartTabMirroring(string deviceName) + { + if (deviceName == null) { - if (deviceName == null) - { - throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); - } - - Dictionary parameters = new Dictionary(); - parameters["sinkName"] = deviceName; - this.Execute(StartCastDesktopMirroringCommand, parameters); + throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); } - /// - /// Returns the error message if there is any issue in a Cast session. - /// - /// An error message. - public string? GetCastIssueMessage() + Dictionary parameters = new Dictionary(); + parameters["sinkName"] = deviceName; + this.Execute(StartCastTabMirroringCommand, parameters); + } + + /// + /// Initiates mirroring of the desktop on the specified device. + /// + /// Name of the target sink (device). + public void StartDesktopMirroring(string deviceName) + { + if (deviceName == null) { - Response response = this.Execute(GetCastIssueMessageCommand, null); - return (string?)response.Value; + throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); } - /// - /// Stops casting from media router to the specified device, if connected. - /// - /// Name of the target sink (device). - public void StopCasting(string deviceName) - { - if (deviceName == null) - { - throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); - } + Dictionary parameters = new Dictionary(); + parameters["sinkName"] = deviceName; + this.Execute(StartCastDesktopMirroringCommand, parameters); + } - Dictionary parameters = new Dictionary(); - parameters["sinkName"] = deviceName; - this.Execute(StopCastingCommand, parameters); - } + /// + /// Returns the error message if there is any issue in a Cast session. + /// + /// An error message. + public string? GetCastIssueMessage() + { + Response response = this.Execute(GetCastIssueMessageCommand, null); + return (string?)response.Value; + } - /// - /// Stops the driver from running - /// - /// if its in the process of disposing - protected override void Dispose(bool disposing) + /// + /// Stops casting from media router to the specified device, if connected. + /// + /// Name of the target sink (device). + public void StopCasting(string deviceName) + { + if (deviceName == null) { - if (disposing) - { - if (this.devToolsSession != null) - { - this.devToolsSession.Dispose(); - this.devToolsSession = null; - } - } - - base.Dispose(disposing); + throw new ArgumentNullException(nameof(deviceName), "deviceName must not be null"); } - private static ICapabilities ConvertOptionsToCapabilities(ChromiumOptions options) + Dictionary parameters = new Dictionary(); + parameters["sinkName"] = deviceName; + this.Execute(StopCastingCommand, parameters); + } + + /// + /// Stops the driver from running + /// + /// if its in the process of disposing + protected override void Dispose(bool disposing) + { + if (disposing) { - if (options == null) + if (this.devToolsSession != null) { - throw new ArgumentNullException(nameof(options), "options must not be null"); + this.devToolsSession.Dispose(); + this.devToolsSession = null; } + } + + base.Dispose(disposing); + } - return options.ToCapabilities(); + private static ICapabilities ConvertOptionsToCapabilities(ChromiumOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options), "options must not be null"); } + + return options.ToCapabilities(); } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs b/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs index e9fbf0f7f7218..9c10e635d8df8 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs @@ -21,179 +21,178 @@ using System.Globalization; using System.Text; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Exposes the service provided by the native ChromiumDriver executable. +/// +public abstract class ChromiumDriverService : DriverService { + private const string DefaultChromeDriverServiceExecutableName = "chromedriver"; + /// - /// Exposes the service provided by the native ChromiumDriver executable. + /// Initializes a new instance of the class. /// - public abstract class ChromiumDriverService : DriverService + /// The full path to the ChromeDriver executable. + /// The file name of the ChromeDriver executable. + /// The port on which the ChromeDriver executable should listen. + protected ChromiumDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, port, executableFileName) { - private const string DefaultChromeDriverServiceExecutableName = "chromedriver"; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the ChromeDriver executable. - /// The file name of the ChromeDriver executable. - /// The port on which the ChromeDriver executable should listen. - protected ChromiumDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, port, executableFileName) - { - } + } - /// - /// Gets or sets the location of the log file written to by the ChromeDriver executable. - /// or signify no log path. - /// - public string? LogPath { get; set; } - - /// - /// Gets or sets the base URL path prefix for commands (e.g., "wd/url"). - /// or signify no prefix. - /// - public string? UrlPathPrefix { get; set; } - - /// - /// Gets or sets the address of a server to contact for reserving a port. - /// or signify no port server. - /// - public string? PortServerAddress { get; set; } - - /// - /// Gets or sets the port on which the Android Debug Bridge is listening for commands. - /// A value less than or equal to 0, or , indicates no Android Debug Bridge specified. - /// - public int? AndroidDebugBridgePort { get; set; } - - /// - /// Gets or sets a value indicating whether to skip version compatibility check - /// between the driver and the browser. - /// Defaults to . - /// - public bool DisableBuildCheck { get; set; } - - /// - /// Gets or sets a value indicating whether to enable verbose logging for the ChromeDriver executable. - /// Defaults to . - /// - public bool EnableVerboseLogging { get; set; } - - /// - /// Gets or sets a value indicating whether to enable appending to an existing ChromeDriver log file. - /// Defaults to . - /// - public bool EnableAppendLog { get; set; } - - /// - /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the Chrome driver. - /// A value of or means only the local loopback address can connect. - /// - [Obsolete($"Use {nameof(AllowedIPAddresses)}")] - public string? WhitelistedIPAddresses - { - get => this.AllowedIPAddresses; - set => this.AllowedIPAddresses = value; - } + /// + /// Gets or sets the location of the log file written to by the ChromeDriver executable. + /// or signify no log path. + /// + public string? LogPath { get; set; } + + /// + /// Gets or sets the base URL path prefix for commands (e.g., "wd/url"). + /// or signify no prefix. + /// + public string? UrlPathPrefix { get; set; } + + /// + /// Gets or sets the address of a server to contact for reserving a port. + /// or signify no port server. + /// + public string? PortServerAddress { get; set; } + + /// + /// Gets or sets the port on which the Android Debug Bridge is listening for commands. + /// A value less than or equal to 0, or , indicates no Android Debug Bridge specified. + /// + public int? AndroidDebugBridgePort { get; set; } - /// - /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the Chrome driver. - /// A value of or means only the local loopback address can connect. - /// - public string? AllowedIPAddresses { get; set; } + /// + /// Gets or sets a value indicating whether to skip version compatibility check + /// between the driver and the browser. + /// Defaults to . + /// + public bool DisableBuildCheck { get; set; } + + /// + /// Gets or sets a value indicating whether to enable verbose logging for the ChromeDriver executable. + /// Defaults to . + /// + public bool EnableVerboseLogging { get; set; } + + /// + /// Gets or sets a value indicating whether to enable appending to an existing ChromeDriver log file. + /// Defaults to . + /// + public bool EnableAppendLog { get; set; } + + /// + /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the Chrome driver. + /// A value of or means only the local loopback address can connect. + /// + [Obsolete($"Use {nameof(AllowedIPAddresses)}")] + public string? WhitelistedIPAddresses + { + get => this.AllowedIPAddresses; + set => this.AllowedIPAddresses = value; + } + + /// + /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the Chrome driver. + /// A value of or means only the local loopback address can connect. + /// + public string? AllowedIPAddresses { get; set; } - /// - /// Gets the command-line arguments for the driver service. - /// - protected override string CommandLineArguments + /// + /// Gets the command-line arguments for the driver service. + /// + protected override string CommandLineArguments + { + get { - get + StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); + if (this.AndroidDebugBridgePort is int adb && adb > 0) { - StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); - if (this.AndroidDebugBridgePort is int adb && adb > 0) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --adb-port={0}", adb); - } - - if (this.SuppressInitialDiagnosticInformation) - { - argsBuilder.Append(" --silent"); - } - - if (this.DisableBuildCheck) - { - argsBuilder.Append(" --disable-build-check"); - } - - if (this.EnableVerboseLogging) - { - argsBuilder.Append(" --verbose"); - } - - if (this.EnableAppendLog) - { - argsBuilder.Append(" --append-log"); - } - - if (!string.IsNullOrEmpty(this.LogPath)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --log-path=\"{0}\"", this.LogPath); - } - - if (!string.IsNullOrEmpty(this.UrlPathPrefix)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --url-base={0}", this.UrlPathPrefix); - } - - if (!string.IsNullOrEmpty(this.PortServerAddress)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --port-server={0}", this.PortServerAddress); - } - - if (!string.IsNullOrEmpty(this.AllowedIPAddresses)) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -allowed-ips={0}", this.AllowedIPAddresses)); - } - - return argsBuilder.ToString(); + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --adb-port={0}", adb); } - } - /// - /// Returns the Chromium driver filename for the currently running platform - /// - /// The name of the Chromium executable. Default is "chromedriver". - /// The file name of the Chromium driver service executable. - protected static string ChromiumDriverServiceFileName(string fileName = DefaultChromeDriverServiceExecutableName) - { - // Unfortunately, detecting the currently running platform isn't as - // straightforward as you might hope. - // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform - // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx - const PlatformID PlatformIDMonoUnix = (PlatformID)128; + if (this.SuppressInitialDiagnosticInformation) + { + argsBuilder.Append(" --silent"); + } + + if (this.DisableBuildCheck) + { + argsBuilder.Append(" --disable-build-check"); + } + + if (this.EnableVerboseLogging) + { + argsBuilder.Append(" --verbose"); + } + + if (this.EnableAppendLog) + { + argsBuilder.Append(" --append-log"); + } + + if (!string.IsNullOrEmpty(this.LogPath)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --log-path=\"{0}\"", this.LogPath); + } + + if (!string.IsNullOrEmpty(this.UrlPathPrefix)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --url-base={0}", this.UrlPathPrefix); + } - switch (Environment.OSVersion.Platform) + if (!string.IsNullOrEmpty(this.PortServerAddress)) { - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.Win32Windows: - case PlatformID.WinCE: - fileName += ".exe"; - break; - - case PlatformID.MacOSX: - case PlatformID.Unix: - case PlatformIDMonoUnix: - break; - - // Don't handle the Xbox case. Let default handle it. - // case PlatformID.Xbox: - // break; - - default: - throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --port-server={0}", this.PortServerAddress); } - return fileName; + if (!string.IsNullOrEmpty(this.AllowedIPAddresses)) + { + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -allowed-ips={0}", this.AllowedIPAddresses)); + } + + return argsBuilder.ToString(); } } + + /// + /// Returns the Chromium driver filename for the currently running platform + /// + /// The name of the Chromium executable. Default is "chromedriver". + /// The file name of the Chromium driver service executable. + protected static string ChromiumDriverServiceFileName(string fileName = DefaultChromeDriverServiceExecutableName) + { + // Unfortunately, detecting the currently running platform isn't as + // straightforward as you might hope. + // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform + // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx + const PlatformID PlatformIDMonoUnix = (PlatformID)128; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + case PlatformID.Win32S: + case PlatformID.Win32Windows: + case PlatformID.WinCE: + fileName += ".exe"; + break; + + case PlatformID.MacOSX: + case PlatformID.Unix: + case PlatformIDMonoUnix: + break; + + // Don't handle the Xbox case. Let default handle it. + // case PlatformID.Xbox: + // break; + + default: + throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); + } + + return fileName; + } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumMobileEmulationDeviceSettings.cs b/dotnet/src/webdriver/Chromium/ChromiumMobileEmulationDeviceSettings.cs index 6ee6b182cde9e..b2f1780fca573 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumMobileEmulationDeviceSettings.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumMobileEmulationDeviceSettings.cs @@ -17,59 +17,58 @@ // under the License. // -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Represents the type-safe options for setting settings for emulating a +/// mobile device in the Chromium browser. +/// +public class ChromiumMobileEmulationDeviceSettings { /// - /// Represents the type-safe options for setting settings for emulating a - /// mobile device in the Chromium browser. + /// Initializes a new instance of the class. /// - public class ChromiumMobileEmulationDeviceSettings + public ChromiumMobileEmulationDeviceSettings() { - /// - /// Initializes a new instance of the class. - /// - public ChromiumMobileEmulationDeviceSettings() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The user agent string to be used by the browser when emulating - /// a mobile device. - public ChromiumMobileEmulationDeviceSettings(string? userAgent) - { - this.UserAgent = userAgent; - } + /// + /// Initializes a new instance of the class. + /// + /// The user agent string to be used by the browser when emulating + /// a mobile device. + public ChromiumMobileEmulationDeviceSettings(string? userAgent) + { + this.UserAgent = userAgent; + } - /// - /// Gets or sets the user agent string to be used by the browser when emulating - /// a mobile device. - /// - public string? UserAgent { get; set; } + /// + /// Gets or sets the user agent string to be used by the browser when emulating + /// a mobile device. + /// + public string? UserAgent { get; set; } - /// - /// Gets or sets the width in pixels to be used by the browser when emulating - /// a mobile device. - /// - public long Width { get; set; } + /// + /// Gets or sets the width in pixels to be used by the browser when emulating + /// a mobile device. + /// + public long Width { get; set; } - /// - /// Gets or sets the height in pixels to be used by the browser when emulating - /// a mobile device. - /// - public long Height { get; set; } + /// + /// Gets or sets the height in pixels to be used by the browser when emulating + /// a mobile device. + /// + public long Height { get; set; } - /// - /// Gets or sets the pixel ratio to be used by the browser when emulating - /// a mobile device. - /// - public double PixelRatio { get; set; } + /// + /// Gets or sets the pixel ratio to be used by the browser when emulating + /// a mobile device. + /// + public double PixelRatio { get; set; } - /// - /// Gets or sets a value indicating whether touch events should be enabled by - /// the browser when emulating a mobile device. Defaults to . - /// - public bool EnableTouchEvents { get; set; } = true; - } + /// + /// Gets or sets a value indicating whether touch events should be enabled by + /// the browser when emulating a mobile device. Defaults to . + /// + public bool EnableTouchEvents { get; set; } = true; } diff --git a/dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs b/dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs index 78a69b7516aee..ba118635732e0 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs @@ -21,114 +21,113 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Provides manipulation of getting and setting network conditions from Chromium. +/// +public class ChromiumNetworkConditions { + private long downloadThroughput = 0; + private long uploadThroughput = 0; + /// - /// Provides manipulation of getting and setting network conditions from Chromium. + /// Gets or sets a value indicating whether the network is offline. Defaults to . /// - public class ChromiumNetworkConditions - { - private long downloadThroughput = 0; - private long uploadThroughput = 0; - - /// - /// Gets or sets a value indicating whether the network is offline. Defaults to . - /// - [JsonPropertyName("offline")] - public bool IsOffline { get; set; } - - /// - /// Gets or sets the simulated latency of the connection. Typically given in milliseconds. - /// - [JsonIgnore] - public TimeSpan Latency { get; set; } = TimeSpan.Zero; - - /// - /// Gets or sets the throughput of the network connection in bytes/second for downloading. - /// - [JsonPropertyName("download_throughput")] - public long DownloadThroughput - { - get => this.downloadThroughput; - set - { - if (value < 0) - { - throw new WebDriverException("Download throughput cannot be negative."); - } + [JsonPropertyName("offline")] + public bool IsOffline { get; set; } - this.downloadThroughput = value; - } - } + /// + /// Gets or sets the simulated latency of the connection. Typically given in milliseconds. + /// + [JsonIgnore] + public TimeSpan Latency { get; set; } = TimeSpan.Zero; - /// - /// Gets or sets the throughput of the network connection in bytes/second for uploading. - /// - [JsonPropertyName("upload_throughput")] - public long UploadThroughput + /// + /// Gets or sets the throughput of the network connection in bytes/second for downloading. + /// + [JsonPropertyName("download_throughput")] + public long DownloadThroughput + { + get => this.downloadThroughput; + set { - get => this.uploadThroughput; - set + if (value < 0) { - if (value < 0) - { - throw new WebDriverException("Upload throughput cannot be negative."); - } - - this.uploadThroughput = value; + throw new WebDriverException("Download throughput cannot be negative."); } + + this.downloadThroughput = value; } + } - [JsonPropertyName("latency")] - [JsonInclude] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - internal long? SerializableLatency => Convert.ToInt64(this.Latency.TotalMilliseconds); - - /// - /// Creates a ChromiumNetworkConditions object from a dictionary of key-value pairs. - /// - /// The dictionary to use to create the object. - /// The ChromiumNetworkConditions object created from the dictionary. - public static ChromiumNetworkConditions FromDictionary(Dictionary dictionary) + /// + /// Gets or sets the throughput of the network connection in bytes/second for uploading. + /// + [JsonPropertyName("upload_throughput")] + public long UploadThroughput + { + get => this.uploadThroughput; + set { - ChromiumNetworkConditions conditions = new ChromiumNetworkConditions(); - if (dictionary.TryGetValue("offline", out object? offline)) + if (value < 0) { - conditions.IsOffline = (bool)offline!; + throw new WebDriverException("Upload throughput cannot be negative."); } - if (dictionary.TryGetValue("latency", out object? latency)) - { - conditions.Latency = TimeSpan.FromMilliseconds(Convert.ToDouble(latency)); - } + this.uploadThroughput = value; + } + } - if (dictionary.TryGetValue("upload_throughput", out object? uploadThroughput)) - { - conditions.UploadThroughput = (long)uploadThroughput!; - } + [JsonPropertyName("latency")] + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + internal long? SerializableLatency => Convert.ToInt64(this.Latency.TotalMilliseconds); - if (dictionary.TryGetValue("download_throughput", out object? downloadThroughput)) - { - conditions.DownloadThroughput = (long)downloadThroughput!; - } + /// + /// Creates a ChromiumNetworkConditions object from a dictionary of key-value pairs. + /// + /// The dictionary to use to create the object. + /// The ChromiumNetworkConditions object created from the dictionary. + public static ChromiumNetworkConditions FromDictionary(Dictionary dictionary) + { + ChromiumNetworkConditions conditions = new ChromiumNetworkConditions(); + if (dictionary.TryGetValue("offline", out object? offline)) + { + conditions.IsOffline = (bool)offline!; + } - return conditions; + if (dictionary.TryGetValue("latency", out object? latency)) + { + conditions.Latency = TimeSpan.FromMilliseconds(Convert.ToDouble(latency)); } - /// - /// Sets the upload and download throughput properties to the same value. - /// - /// The throughput of the network connection in bytes/second for both upload and download. - /// If is negative. - public void SetBidirectionalThroughput(long throughput) + if (dictionary.TryGetValue("upload_throughput", out object? uploadThroughput)) { - if (throughput < 0) - { - throw new ArgumentOutOfRangeException(nameof(throughput), "Throughput values cannot be negative."); - } + conditions.UploadThroughput = (long)uploadThroughput!; + } - this.uploadThroughput = throughput; - this.downloadThroughput = throughput; + if (dictionary.TryGetValue("download_throughput", out object? downloadThroughput)) + { + conditions.DownloadThroughput = (long)downloadThroughput!; } + + return conditions; + } + + /// + /// Sets the upload and download throughput properties to the same value. + /// + /// The throughput of the network connection in bytes/second for both upload and download. + /// If is negative. + public void SetBidirectionalThroughput(long throughput) + { + if (throughput < 0) + { + throw new ArgumentOutOfRangeException(nameof(throughput), "Throughput values cannot be negative."); + } + + this.uploadThroughput = throughput; + this.downloadThroughput = throughput; } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumOptions.cs b/dotnet/src/webdriver/Chromium/ChromiumOptions.cs index eb7805504eda8..6ad454ac77a12 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumOptions.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumOptions.cs @@ -22,647 +22,646 @@ using System.Collections.ObjectModel; using System.IO; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Abstract class to manage options specific to Chromium-based browsers. +/// +public abstract class ChromiumOptions : DriverOptions { + private const string ArgumentsChromeOption = "args"; + private const string BinaryChromeOption = "binary"; + private const string ExtensionsChromeOption = "extensions"; + private const string LocalStateChromeOption = "localState"; + private const string PreferencesChromeOption = "prefs"; + private const string DetachChromeOption = "detach"; + private const string DebuggerAddressChromeOption = "debuggerAddress"; + private const string ExcludeSwitchesChromeOption = "excludeSwitches"; + private const string MinidumpPathChromeOption = "minidumpPath"; + private const string MobileEmulationChromeOption = "mobileEmulation"; + private const string PerformanceLoggingPreferencesChromeOption = "perfLoggingPrefs"; + private const string WindowTypesChromeOption = "windowTypes"; + private const string UseSpecCompliantProtocolOption = "w3c"; + private bool useSpecCompliantProtocol = true; + private readonly List arguments = new List(); + private readonly List extensionFiles = new List(); + private readonly List encodedExtensions = new List(); + private readonly List excludedSwitches = new List(); + private readonly List windowTypes = new List(); + private readonly Dictionary additionalChromeOptions = new Dictionary(); + private Dictionary? userProfilePreferences; + private Dictionary? localStatePreferences; + + private string? mobileEmulationDeviceName; + private ChromiumMobileEmulationDeviceSettings? mobileEmulationDeviceSettings; + /// - /// Abstract class to manage options specific to Chromium-based browsers. + /// Initializes a new instance of the class. /// - public abstract class ChromiumOptions : DriverOptions + public ChromiumOptions() : base() { - private const string ArgumentsChromeOption = "args"; - private const string BinaryChromeOption = "binary"; - private const string ExtensionsChromeOption = "extensions"; - private const string LocalStateChromeOption = "localState"; - private const string PreferencesChromeOption = "prefs"; - private const string DetachChromeOption = "detach"; - private const string DebuggerAddressChromeOption = "debuggerAddress"; - private const string ExcludeSwitchesChromeOption = "excludeSwitches"; - private const string MinidumpPathChromeOption = "minidumpPath"; - private const string MobileEmulationChromeOption = "mobileEmulation"; - private const string PerformanceLoggingPreferencesChromeOption = "perfLoggingPrefs"; - private const string WindowTypesChromeOption = "windowTypes"; - private const string UseSpecCompliantProtocolOption = "w3c"; - private bool useSpecCompliantProtocol = true; - private readonly List arguments = new List(); - private readonly List extensionFiles = new List(); - private readonly List encodedExtensions = new List(); - private readonly List excludedSwitches = new List(); - private readonly List windowTypes = new List(); - private readonly Dictionary additionalChromeOptions = new Dictionary(); - private Dictionary? userProfilePreferences; - private Dictionary? localStatePreferences; - - private string? mobileEmulationDeviceName; - private ChromiumMobileEmulationDeviceSettings? mobileEmulationDeviceSettings; - - /// - /// Initializes a new instance of the class. - /// - public ChromiumOptions() : base() - { - this.AddKnownCapabilityName(this.CapabilityName, "current ChromiumOptions class instance"); - this.AddKnownCapabilityName(CapabilityType.LoggingPreferences, "SetLoggingPreference method"); - this.AddKnownCapabilityName(this.LoggingPreferencesChromeOption, "SetLoggingPreference method"); - this.AddKnownCapabilityName(ChromiumOptions.ArgumentsChromeOption, "AddArguments method"); - this.AddKnownCapabilityName(ChromiumOptions.BinaryChromeOption, "BinaryLocation property"); - this.AddKnownCapabilityName(ChromiumOptions.ExtensionsChromeOption, "AddExtensions method"); - this.AddKnownCapabilityName(ChromiumOptions.LocalStateChromeOption, "AddLocalStatePreference method"); - this.AddKnownCapabilityName(ChromiumOptions.PreferencesChromeOption, "AddUserProfilePreference method"); - this.AddKnownCapabilityName(ChromiumOptions.DetachChromeOption, "LeaveBrowserRunning property"); - this.AddKnownCapabilityName(ChromiumOptions.DebuggerAddressChromeOption, "DebuggerAddress property"); - this.AddKnownCapabilityName(ChromiumOptions.ExcludeSwitchesChromeOption, "AddExcludedArgument property"); - this.AddKnownCapabilityName(ChromiumOptions.MinidumpPathChromeOption, "MinidumpPath property"); - this.AddKnownCapabilityName(ChromiumOptions.MobileEmulationChromeOption, "EnableMobileEmulation method"); - this.AddKnownCapabilityName(ChromiumOptions.PerformanceLoggingPreferencesChromeOption, "PerformanceLoggingPreferences property"); - this.AddKnownCapabilityName(ChromiumOptions.WindowTypesChromeOption, "AddWindowTypes method"); - this.AddKnownCapabilityName(ChromiumOptions.UseSpecCompliantProtocolOption, "UseSpecCompliantProtocol property"); - } - - /// - /// Gets the vendor prefix to apply to Chromium-specific capability names. - /// - protected abstract string VendorPrefix { get; } - - private string LoggingPreferencesChromeOption => this.VendorPrefix + ":loggingPrefs"; - - /// - /// Gets the name of the capability used to store Chromium options in - /// an object. - /// - public abstract string CapabilityName { get; } - - /// - /// Gets or sets the location of the Chromium browser's binary executable file. - /// - public override string? BinaryLocation { get; set; } - - /// - /// Gets or sets a value indicating whether Chromium should be left running after the - /// ChromeDriver instance is exited. Defaults to . - /// - public bool LeaveBrowserRunning { get; set; } - - /// - /// Gets the list of arguments appended to the Chromium command line as a string array. - /// - public ReadOnlyCollection Arguments => this.arguments.AsReadOnly(); - - /// - /// Gets the list of extensions to be installed as an array of base64-encoded strings. - /// - public ReadOnlyCollection Extensions - { - get - { - List allExtensions = new List(this.encodedExtensions); - foreach (string extensionFile in this.extensionFiles) - { - byte[] extensionByteArray = File.ReadAllBytes(extensionFile); - string encodedExtension = Convert.ToBase64String(extensionByteArray); - allExtensions.Add(encodedExtension); - } - - return allExtensions.AsReadOnly(); - } - } + this.AddKnownCapabilityName(this.CapabilityName, "current ChromiumOptions class instance"); + this.AddKnownCapabilityName(CapabilityType.LoggingPreferences, "SetLoggingPreference method"); + this.AddKnownCapabilityName(this.LoggingPreferencesChromeOption, "SetLoggingPreference method"); + this.AddKnownCapabilityName(ChromiumOptions.ArgumentsChromeOption, "AddArguments method"); + this.AddKnownCapabilityName(ChromiumOptions.BinaryChromeOption, "BinaryLocation property"); + this.AddKnownCapabilityName(ChromiumOptions.ExtensionsChromeOption, "AddExtensions method"); + this.AddKnownCapabilityName(ChromiumOptions.LocalStateChromeOption, "AddLocalStatePreference method"); + this.AddKnownCapabilityName(ChromiumOptions.PreferencesChromeOption, "AddUserProfilePreference method"); + this.AddKnownCapabilityName(ChromiumOptions.DetachChromeOption, "LeaveBrowserRunning property"); + this.AddKnownCapabilityName(ChromiumOptions.DebuggerAddressChromeOption, "DebuggerAddress property"); + this.AddKnownCapabilityName(ChromiumOptions.ExcludeSwitchesChromeOption, "AddExcludedArgument property"); + this.AddKnownCapabilityName(ChromiumOptions.MinidumpPathChromeOption, "MinidumpPath property"); + this.AddKnownCapabilityName(ChromiumOptions.MobileEmulationChromeOption, "EnableMobileEmulation method"); + this.AddKnownCapabilityName(ChromiumOptions.PerformanceLoggingPreferencesChromeOption, "PerformanceLoggingPreferences property"); + this.AddKnownCapabilityName(ChromiumOptions.WindowTypesChromeOption, "AddWindowTypes method"); + this.AddKnownCapabilityName(ChromiumOptions.UseSpecCompliantProtocolOption, "UseSpecCompliantProtocol property"); + } - /// - /// Gets or sets the address of a Chromium debugger server to connect to. - /// Should be of the form "{hostname|IP address}:port". - /// - public string? DebuggerAddress { get; set; } + /// + /// Gets the vendor prefix to apply to Chromium-specific capability names. + /// + protected abstract string VendorPrefix { get; } - /// - /// Gets or sets the directory in which to store minidump files. - /// - public string? MinidumpPath { get; set; } + private string LoggingPreferencesChromeOption => this.VendorPrefix + ":loggingPrefs"; - /// - /// Gets or sets the performance logging preferences for the driver. - /// - public ChromiumPerformanceLoggingPreferences? PerformanceLoggingPreferences { get; set; } + /// + /// Gets the name of the capability used to store Chromium options in + /// an object. + /// + public abstract string CapabilityName { get; } + + /// + /// Gets or sets the location of the Chromium browser's binary executable file. + /// + public override string? BinaryLocation { get; set; } - /// - /// Gets or sets the options for automating Chromium applications on Android. - /// - public ChromiumAndroidOptions? AndroidOptions { get; set; } + /// + /// Gets or sets a value indicating whether Chromium should be left running after the + /// ChromeDriver instance is exited. Defaults to . + /// + public bool LeaveBrowserRunning { get; set; } + + /// + /// Gets the list of arguments appended to the Chromium command line as a string array. + /// + public ReadOnlyCollection Arguments => this.arguments.AsReadOnly(); - /// - /// Adds a single argument to the list of arguments to be appended to the browser executable command line. - /// - /// The argument to add. - /// If is or . - public void AddArgument(string argument) + /// + /// Gets the list of extensions to be installed as an array of base64-encoded strings. + /// + public ReadOnlyCollection Extensions + { + get { - if (string.IsNullOrEmpty(argument)) + List allExtensions = new List(this.encodedExtensions); + foreach (string extensionFile in this.extensionFiles) { - throw new ArgumentException("argument must not be null or empty", nameof(argument)); + byte[] extensionByteArray = File.ReadAllBytes(extensionFile); + string encodedExtension = Convert.ToBase64String(extensionByteArray); + allExtensions.Add(encodedExtension); } - this.AddArguments(argument); + return allExtensions.AsReadOnly(); } + } + + /// + /// Gets or sets the address of a Chromium debugger server to connect to. + /// Should be of the form "{hostname|IP address}:port". + /// + public string? DebuggerAddress { get; set; } + + /// + /// Gets or sets the directory in which to store minidump files. + /// + public string? MinidumpPath { get; set; } + + /// + /// Gets or sets the performance logging preferences for the driver. + /// + public ChromiumPerformanceLoggingPreferences? PerformanceLoggingPreferences { get; set; } + + /// + /// Gets or sets the options for automating Chromium applications on Android. + /// + public ChromiumAndroidOptions? AndroidOptions { get; set; } - /// - /// Adds arguments to be appended to the browser executable command line. - /// - /// An array of arguments to add. - /// If is . - public void AddArguments(params string[] argumentsToAdd) + /// + /// Adds a single argument to the list of arguments to be appended to the browser executable command line. + /// + /// The argument to add. + /// If is or . + public void AddArgument(string argument) + { + if (string.IsNullOrEmpty(argument)) { - this.AddArguments((IEnumerable)argumentsToAdd); + throw new ArgumentException("argument must not be null or empty", nameof(argument)); } - /// - /// Adds arguments to be appended to the browser executable command line. - /// - /// An object of arguments to add. - /// If is . - public void AddArguments(IEnumerable argumentsToAdd) - { - if (argumentsToAdd == null) - { - throw new ArgumentNullException(nameof(argumentsToAdd), "argumentsToAdd must not be null"); - } + this.AddArguments(argument); + } - this.arguments.AddRange(argumentsToAdd); - } + /// + /// Adds arguments to be appended to the browser executable command line. + /// + /// An array of arguments to add. + /// If is . + public void AddArguments(params string[] argumentsToAdd) + { + this.AddArguments((IEnumerable)argumentsToAdd); + } - /// - /// Adds a single argument to be excluded from the list of arguments passed by default - /// to the browser executable command line by chromedriver.exe. - /// - /// The argument to exclude. - /// If is or . - public void AddExcludedArgument(string argument) + /// + /// Adds arguments to be appended to the browser executable command line. + /// + /// An object of arguments to add. + /// If is . + public void AddArguments(IEnumerable argumentsToAdd) + { + if (argumentsToAdd == null) { - if (string.IsNullOrEmpty(argument)) - { - throw new ArgumentException("argument must not be null or empty", nameof(argument)); - } - - this.AddExcludedArguments(argument); + throw new ArgumentNullException(nameof(argumentsToAdd), "argumentsToAdd must not be null"); } - /// - /// Adds arguments to be excluded from the list of arguments passed by default - /// to the browser executable command line by chromedriver.exe. - /// - /// An array of arguments to exclude. - /// If is . - public void AddExcludedArguments(params string[] argumentsToExclude) + this.arguments.AddRange(argumentsToAdd); + } + + /// + /// Adds a single argument to be excluded from the list of arguments passed by default + /// to the browser executable command line by chromedriver.exe. + /// + /// The argument to exclude. + /// If is or . + public void AddExcludedArgument(string argument) + { + if (string.IsNullOrEmpty(argument)) { - this.AddExcludedArguments((IEnumerable)argumentsToExclude); + throw new ArgumentException("argument must not be null or empty", nameof(argument)); } - /// - /// Adds arguments to be excluded from the list of arguments passed by default - /// to the browser executable command line by chromedriver.exe. - /// - /// An object of arguments to exclude. - /// If is . - public void AddExcludedArguments(IEnumerable argumentsToExclude) - { - if (argumentsToExclude == null) - { - throw new ArgumentNullException(nameof(argumentsToExclude), "argumentsToExclude must not be null"); - } + this.AddExcludedArguments(argument); + } - this.excludedSwitches.AddRange(argumentsToExclude); - } + /// + /// Adds arguments to be excluded from the list of arguments passed by default + /// to the browser executable command line by chromedriver.exe. + /// + /// An array of arguments to exclude. + /// If is . + public void AddExcludedArguments(params string[] argumentsToExclude) + { + this.AddExcludedArguments((IEnumerable)argumentsToExclude); + } - /// - /// Adds a path to a packed Chrome extension (.crx file) to the list of extensions - /// to be installed in the instance of Chrome. - /// - /// The full path to the extension to add. - /// If is or . - public void AddExtension(string pathToExtension) + /// + /// Adds arguments to be excluded from the list of arguments passed by default + /// to the browser executable command line by chromedriver.exe. + /// + /// An object of arguments to exclude. + /// If is . + public void AddExcludedArguments(IEnumerable argumentsToExclude) + { + if (argumentsToExclude == null) { - if (string.IsNullOrEmpty(pathToExtension)) - { - throw new ArgumentException("pathToExtension must not be null or empty", nameof(pathToExtension)); - } - - this.AddExtensions(pathToExtension); + throw new ArgumentNullException(nameof(argumentsToExclude), "argumentsToExclude must not be null"); } - /// - /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed - /// in the instance of Chrome. - /// - /// An array of full paths to the extensions to add. - /// If is . - /// If any extension file path does not point to a file. - public void AddExtensions(params string[] extensions) + this.excludedSwitches.AddRange(argumentsToExclude); + } + + /// + /// Adds a path to a packed Chrome extension (.crx file) to the list of extensions + /// to be installed in the instance of Chrome. + /// + /// The full path to the extension to add. + /// If is or . + public void AddExtension(string pathToExtension) + { + if (string.IsNullOrEmpty(pathToExtension)) { - this.AddExtensions((IEnumerable)extensions); + throw new ArgumentException("pathToExtension must not be null or empty", nameof(pathToExtension)); } - /// - /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed - /// in the instance of Chrome. - /// - /// An of full paths to the extensions to add. - /// If is . - /// If any extension file path does not point to a file. - public void AddExtensions(IEnumerable extensions) - { - if (extensions == null) - { - throw new ArgumentNullException(nameof(extensions), "extensions must not be null"); - } + this.AddExtensions(pathToExtension); + } - foreach (string extension in extensions) - { - if (!File.Exists(extension)) - { - throw new FileNotFoundException("No extension found at the specified path", extension); - } + /// + /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed + /// in the instance of Chrome. + /// + /// An array of full paths to the extensions to add. + /// If is . + /// If any extension file path does not point to a file. + public void AddExtensions(params string[] extensions) + { + this.AddExtensions((IEnumerable)extensions); + } - this.extensionFiles.Add(extension); - } + /// + /// Adds a list of paths to packed Chrome extensions (.crx files) to be installed + /// in the instance of Chrome. + /// + /// An of full paths to the extensions to add. + /// If is . + /// If any extension file path does not point to a file. + public void AddExtensions(IEnumerable extensions) + { + if (extensions == null) + { + throw new ArgumentNullException(nameof(extensions), "extensions must not be null"); } - /// - /// Adds a base64-encoded string representing a Chrome extension to the list of extensions - /// to be installed in the instance of Chrome. - /// - /// A base64-encoded string representing the extension to add. - /// If is or . - /// If the extension string is not valid base-64. - public void AddEncodedExtension(string extension) + foreach (string extension in extensions) { - if (string.IsNullOrEmpty(extension)) + if (!File.Exists(extension)) { - throw new ArgumentException("extension must not be null or empty", nameof(extension)); + throw new FileNotFoundException("No extension found at the specified path", extension); } - this.AddEncodedExtensions(extension); + this.extensionFiles.Add(extension); } + } - /// - /// Adds a list of base64-encoded strings representing Chrome extensions to the list of extensions - /// to be installed in the instance of Chrome. - /// - /// An array of base64-encoded strings representing the extensions to add. - /// If is . - /// If an extension string is not valid base-64. - public void AddEncodedExtensions(params string[] extensions) + /// + /// Adds a base64-encoded string representing a Chrome extension to the list of extensions + /// to be installed in the instance of Chrome. + /// + /// A base64-encoded string representing the extension to add. + /// If is or . + /// If the extension string is not valid base-64. + public void AddEncodedExtension(string extension) + { + if (string.IsNullOrEmpty(extension)) { - this.AddEncodedExtensions((IEnumerable)extensions); + throw new ArgumentException("extension must not be null or empty", nameof(extension)); } - /// - /// Adds a list of base64-encoded strings representing Chrome extensions to be installed - /// in the instance of Chrome. - /// - /// An of base64-encoded strings - /// representing the extensions to add. - /// If is . - /// If an extension string is not valid base-64. - public void AddEncodedExtensions(IEnumerable extensions) - { - if (extensions == null) - { - throw new ArgumentNullException(nameof(extensions), "extensions must not be null"); - } + this.AddEncodedExtensions(extension); + } - foreach (string extension in extensions) - { - // Run the extension through the base64 converter to test that the - // string is not malformed. - try - { - Convert.FromBase64String(extension); - } - catch (FormatException ex) - { - throw new WebDriverException("Could not properly decode the base64 string", ex); - } - - this.encodedExtensions.Add(extension); - } - } + /// + /// Adds a list of base64-encoded strings representing Chrome extensions to the list of extensions + /// to be installed in the instance of Chrome. + /// + /// An array of base64-encoded strings representing the extensions to add. + /// If is . + /// If an extension string is not valid base-64. + public void AddEncodedExtensions(params string[] extensions) + { + this.AddEncodedExtensions((IEnumerable)extensions); + } - /// - /// Adds a preference for the user-specific profile or "user data directory." - /// If the specified preference already exists, it will be overwritten. - /// - /// The name of the preference to set. - /// The value of the preference to set. - /// If is . - public void AddUserProfilePreference(string preferenceName, object preferenceValue) + /// + /// Adds a list of base64-encoded strings representing Chrome extensions to be installed + /// in the instance of Chrome. + /// + /// An of base64-encoded strings + /// representing the extensions to add. + /// If is . + /// If an extension string is not valid base-64. + public void AddEncodedExtensions(IEnumerable extensions) + { + if (extensions == null) { - if (this.userProfilePreferences == null) - { - this.userProfilePreferences = new Dictionary(); - } - - this.userProfilePreferences[preferenceName] = preferenceValue; + throw new ArgumentNullException(nameof(extensions), "extensions must not be null"); } - /// - /// Adds a preference for the local state file in the user's data directory for Chromium. - /// If the specified preference already exists, it will be overwritten. - /// - /// The name of the preference to set. - /// The value of the preference to set. - /// If is . - public void AddLocalStatePreference(string preferenceName, object preferenceValue) + foreach (string extension in extensions) { - if (this.localStatePreferences == null) + // Run the extension through the base64 converter to test that the + // string is not malformed. + try { - this.localStatePreferences = new Dictionary(); + Convert.FromBase64String(extension); } - - this.localStatePreferences[preferenceName] = preferenceValue; - } - - /// - /// Allows the Chromium browser to emulate a mobile device. - /// - /// The name of the device to emulate. The device name must be a - /// valid device name from the Chrome DevTools Emulation panel. - /// Specifying an invalid device name will not throw an exeption, but - /// will generate an error in Chrome when the driver starts. To unset mobile - /// emulation, call this method with as the argument. - public void EnableMobileEmulation(string? deviceName) - { - this.mobileEmulationDeviceSettings = null; - this.mobileEmulationDeviceName = deviceName; - } - - /// - /// Allows the Chromium browser to emulate a mobile device. - /// - /// The - /// object containing the settings of the device to emulate. - /// Thrown if the device settings option does - /// not have a user agent string set. - /// Specifying an invalid device name will not throw an exeption, but - /// will generate an error in Chrome when the driver starts. To unset mobile - /// emulation, call this method with as the argument. - public void EnableMobileEmulation(ChromiumMobileEmulationDeviceSettings? deviceSettings) - { - this.mobileEmulationDeviceName = null; - if (deviceSettings != null && string.IsNullOrEmpty(deviceSettings.UserAgent)) + catch (FormatException ex) { - throw new ArgumentException("Device settings must include a user agent string.", nameof(deviceSettings)); + throw new WebDriverException("Could not properly decode the base64 string", ex); } - this.mobileEmulationDeviceSettings = deviceSettings; + this.encodedExtensions.Add(extension); } + } - /// - /// Adds a type of window that will be listed in the list of window handles - /// returned by the Chrome driver. - /// - /// The name of the window type to add. - /// This method can be used to allow the driver to access {webview} - /// elements by adding "webview" as a window type. - /// If is or . - public void AddWindowType(string windowType) + /// + /// Adds a preference for the user-specific profile or "user data directory." + /// If the specified preference already exists, it will be overwritten. + /// + /// The name of the preference to set. + /// The value of the preference to set. + /// If is . + public void AddUserProfilePreference(string preferenceName, object preferenceValue) + { + if (this.userProfilePreferences == null) { - if (string.IsNullOrEmpty(windowType)) - { - throw new ArgumentException("windowType must not be null or empty", nameof(windowType)); - } - - this.AddWindowTypes(windowType); + this.userProfilePreferences = new Dictionary(); } - /// - /// Adds a list of window types that will be listed in the list of window handles - /// returned by the Chromium driver. - /// - /// An array of window types to add. - /// If is . - public void AddWindowTypes(params string[] windowTypesToAdd) + this.userProfilePreferences[preferenceName] = preferenceValue; + } + + /// + /// Adds a preference for the local state file in the user's data directory for Chromium. + /// If the specified preference already exists, it will be overwritten. + /// + /// The name of the preference to set. + /// The value of the preference to set. + /// If is . + public void AddLocalStatePreference(string preferenceName, object preferenceValue) + { + if (this.localStatePreferences == null) { - this.AddWindowTypes((IEnumerable)windowTypesToAdd); + this.localStatePreferences = new Dictionary(); } - /// - /// Adds a list of window types that will be listed in the list of window handles - /// returned by the Chromium driver. - /// - /// An of window types to add. - /// If is . - public void AddWindowTypes(IEnumerable windowTypesToAdd) - { - if (windowTypesToAdd == null) - { - throw new ArgumentNullException(nameof(windowTypesToAdd), "windowTypesToAdd must not be null"); - } + this.localStatePreferences[preferenceName] = preferenceValue; + } - this.windowTypes.AddRange(windowTypesToAdd); - } - - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the Chromium driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// Calling this method adds capabilities to the Chromium-specific options object passed to - /// webdriver executable (e.g. property name 'goog:chromeOptions'). - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - protected void AddAdditionalChromiumOption(string optionName, object optionValue) - { - this.ValidateCapabilityName(optionName); - this.additionalChromeOptions[optionName] = optionValue; - } - - /// - /// Returns DesiredCapabilities for Chromium with these options included as - /// capabilities. This does not copy the options. Further changes will be - /// reflected in the returned capabilities. - /// - /// The DesiredCapabilities for Chrome with these options. - public override ICapabilities ToCapabilities() - { - Dictionary chromeOptions = this.BuildChromeOptionsDictionary(); - - IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(false); - capabilities.SetCapability(this.CapabilityName, chromeOptions); - - AddVendorSpecificChromiumCapabilities(capabilities); - - Dictionary? loggingPreferences = this.GenerateLoggingPreferencesDictionary(); - if (loggingPreferences != null) - { - capabilities.SetCapability(LoggingPreferencesChromeOption, loggingPreferences); - } + /// + /// Allows the Chromium browser to emulate a mobile device. + /// + /// The name of the device to emulate. The device name must be a + /// valid device name from the Chrome DevTools Emulation panel. + /// Specifying an invalid device name will not throw an exeption, but + /// will generate an error in Chrome when the driver starts. To unset mobile + /// emulation, call this method with as the argument. + public void EnableMobileEmulation(string? deviceName) + { + this.mobileEmulationDeviceSettings = null; + this.mobileEmulationDeviceName = deviceName; + } - return capabilities.AsReadOnly(); + /// + /// Allows the Chromium browser to emulate a mobile device. + /// + /// The + /// object containing the settings of the device to emulate. + /// Thrown if the device settings option does + /// not have a user agent string set. + /// Specifying an invalid device name will not throw an exeption, but + /// will generate an error in Chrome when the driver starts. To unset mobile + /// emulation, call this method with as the argument. + public void EnableMobileEmulation(ChromiumMobileEmulationDeviceSettings? deviceSettings) + { + this.mobileEmulationDeviceName = null; + if (deviceSettings != null && string.IsNullOrEmpty(deviceSettings.UserAgent)) + { + throw new ArgumentException("Device settings must include a user agent string.", nameof(deviceSettings)); } - /// - /// Adds vendor-specific capabilities for Chromium-based browsers. - /// - /// The capabilities to add. - protected virtual void AddVendorSpecificChromiumCapabilities(IWritableCapabilities capabilities) + this.mobileEmulationDeviceSettings = deviceSettings; + } + + /// + /// Adds a type of window that will be listed in the list of window handles + /// returned by the Chrome driver. + /// + /// The name of the window type to add. + /// This method can be used to allow the driver to access {webview} + /// elements by adding "webview" as a window type. + /// If is or . + public void AddWindowType(string windowType) + { + if (string.IsNullOrEmpty(windowType)) { + throw new ArgumentException("windowType must not be null or empty", nameof(windowType)); } - private Dictionary BuildChromeOptionsDictionary() + this.AddWindowTypes(windowType); + } + + /// + /// Adds a list of window types that will be listed in the list of window handles + /// returned by the Chromium driver. + /// + /// An array of window types to add. + /// If is . + public void AddWindowTypes(params string[] windowTypesToAdd) + { + this.AddWindowTypes((IEnumerable)windowTypesToAdd); + } + + /// + /// Adds a list of window types that will be listed in the list of window handles + /// returned by the Chromium driver. + /// + /// An of window types to add. + /// If is . + public void AddWindowTypes(IEnumerable windowTypesToAdd) + { + if (windowTypesToAdd == null) { - Dictionary chromeOptions = new Dictionary(); - if (this.Arguments.Count > 0) - { - chromeOptions[ArgumentsChromeOption] = this.Arguments; - } + throw new ArgumentNullException(nameof(windowTypesToAdd), "windowTypesToAdd must not be null"); + } - if (!string.IsNullOrEmpty(this.BinaryLocation)) - { - chromeOptions[BinaryChromeOption] = this.BinaryLocation!; - } + this.windowTypes.AddRange(windowTypesToAdd); + } - ReadOnlyCollection extensions = this.Extensions; - if (extensions.Count > 0) - { - chromeOptions[ExtensionsChromeOption] = extensions; - } + /// + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the Chromium driver. + /// + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// Calling this method adds capabilities to the Chromium-specific options object passed to + /// webdriver executable (e.g. property name 'goog:chromeOptions'). + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + protected void AddAdditionalChromiumOption(string optionName, object optionValue) + { + this.ValidateCapabilityName(optionName); + this.additionalChromeOptions[optionName] = optionValue; + } - if (this.localStatePreferences != null && this.localStatePreferences.Count > 0) - { - chromeOptions[LocalStateChromeOption] = this.localStatePreferences; - } + /// + /// Returns DesiredCapabilities for Chromium with these options included as + /// capabilities. This does not copy the options. Further changes will be + /// reflected in the returned capabilities. + /// + /// The DesiredCapabilities for Chrome with these options. + public override ICapabilities ToCapabilities() + { + Dictionary chromeOptions = this.BuildChromeOptionsDictionary(); - if (this.userProfilePreferences != null && this.userProfilePreferences.Count > 0) - { - chromeOptions[PreferencesChromeOption] = this.userProfilePreferences; - } + IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(false); + capabilities.SetCapability(this.CapabilityName, chromeOptions); - if (this.LeaveBrowserRunning) - { - chromeOptions[DetachChromeOption] = this.LeaveBrowserRunning; - } + AddVendorSpecificChromiumCapabilities(capabilities); - if (!this.useSpecCompliantProtocol) - { - chromeOptions[UseSpecCompliantProtocolOption] = this.useSpecCompliantProtocol; - } + Dictionary? loggingPreferences = this.GenerateLoggingPreferencesDictionary(); + if (loggingPreferences != null) + { + capabilities.SetCapability(LoggingPreferencesChromeOption, loggingPreferences); + } - if (!string.IsNullOrEmpty(this.DebuggerAddress)) - { - chromeOptions[DebuggerAddressChromeOption] = this.DebuggerAddress!; - } + return capabilities.AsReadOnly(); + } - if (this.excludedSwitches.Count > 0) - { - chromeOptions[ExcludeSwitchesChromeOption] = this.excludedSwitches; - } + /// + /// Adds vendor-specific capabilities for Chromium-based browsers. + /// + /// The capabilities to add. + protected virtual void AddVendorSpecificChromiumCapabilities(IWritableCapabilities capabilities) + { + } - if (!string.IsNullOrEmpty(this.MinidumpPath)) - { - chromeOptions[MinidumpPathChromeOption] = this.MinidumpPath!; - } + private Dictionary BuildChromeOptionsDictionary() + { + Dictionary chromeOptions = new Dictionary(); + if (this.Arguments.Count > 0) + { + chromeOptions[ArgumentsChromeOption] = this.Arguments; + } - if (!string.IsNullOrEmpty(this.mobileEmulationDeviceName) || this.mobileEmulationDeviceSettings != null) - { - chromeOptions[MobileEmulationChromeOption] = GenerateMobileEmulationSettingsDictionary(this.mobileEmulationDeviceSettings, this.mobileEmulationDeviceName); - } + if (!string.IsNullOrEmpty(this.BinaryLocation)) + { + chromeOptions[BinaryChromeOption] = this.BinaryLocation!; + } - if (this.PerformanceLoggingPreferences != null) - { - chromeOptions[PerformanceLoggingPreferencesChromeOption] = GeneratePerformanceLoggingPreferencesDictionary(this.PerformanceLoggingPreferences); - } + ReadOnlyCollection extensions = this.Extensions; + if (extensions.Count > 0) + { + chromeOptions[ExtensionsChromeOption] = extensions; + } - if (this.AndroidOptions != null) - { - AddAndroidOptions(chromeOptions, this.AndroidOptions); - } + if (this.localStatePreferences != null && this.localStatePreferences.Count > 0) + { + chromeOptions[LocalStateChromeOption] = this.localStatePreferences; + } - if (this.windowTypes.Count > 0) - { - chromeOptions[WindowTypesChromeOption] = this.windowTypes; - } + if (this.userProfilePreferences != null && this.userProfilePreferences.Count > 0) + { + chromeOptions[PreferencesChromeOption] = this.userProfilePreferences; + } - foreach (KeyValuePair pair in this.additionalChromeOptions) - { - chromeOptions.Add(pair.Key, pair.Value); - } + if (this.LeaveBrowserRunning) + { + chromeOptions[DetachChromeOption] = this.LeaveBrowserRunning; + } - return chromeOptions; + if (!this.useSpecCompliantProtocol) + { + chromeOptions[UseSpecCompliantProtocolOption] = this.useSpecCompliantProtocol; } - private static void AddAndroidOptions(Dictionary chromeOptions, ChromiumAndroidOptions androidOptions) + if (!string.IsNullOrEmpty(this.DebuggerAddress)) { - chromeOptions["androidPackage"] = androidOptions.AndroidPackage; + chromeOptions[DebuggerAddressChromeOption] = this.DebuggerAddress!; + } - if (!string.IsNullOrEmpty(androidOptions.AndroidDeviceSerial)) - { - chromeOptions["androidDeviceSerial"] = androidOptions.AndroidDeviceSerial!; - } + if (this.excludedSwitches.Count > 0) + { + chromeOptions[ExcludeSwitchesChromeOption] = this.excludedSwitches; + } - if (!string.IsNullOrEmpty(androidOptions.AndroidActivity)) - { - chromeOptions["androidActivity"] = androidOptions.AndroidActivity!; - } + if (!string.IsNullOrEmpty(this.MinidumpPath)) + { + chromeOptions[MinidumpPathChromeOption] = this.MinidumpPath!; + } - if (!string.IsNullOrEmpty(androidOptions.AndroidProcess)) - { - chromeOptions["androidProcess"] = androidOptions.AndroidProcess!; - } + if (!string.IsNullOrEmpty(this.mobileEmulationDeviceName) || this.mobileEmulationDeviceSettings != null) + { + chromeOptions[MobileEmulationChromeOption] = GenerateMobileEmulationSettingsDictionary(this.mobileEmulationDeviceSettings, this.mobileEmulationDeviceName); + } - if (androidOptions.UseRunningApp) - { - chromeOptions["androidUseRunningApp"] = androidOptions.UseRunningApp; - } + if (this.PerformanceLoggingPreferences != null) + { + chromeOptions[PerformanceLoggingPreferencesChromeOption] = GeneratePerformanceLoggingPreferencesDictionary(this.PerformanceLoggingPreferences); } - private static Dictionary GeneratePerformanceLoggingPreferencesDictionary(ChromiumPerformanceLoggingPreferences prefs) + if (this.AndroidOptions != null) { - Dictionary perfLoggingPrefsDictionary = new Dictionary(); - perfLoggingPrefsDictionary["enableNetwork"] = prefs.IsCollectingNetworkEvents; - perfLoggingPrefsDictionary["enablePage"] = prefs.IsCollectingPageEvents; + AddAndroidOptions(chromeOptions, this.AndroidOptions); + } - string tracingCategories = prefs.TracingCategories; - if (!string.IsNullOrEmpty(tracingCategories)) - { - perfLoggingPrefsDictionary["traceCategories"] = tracingCategories; - } + if (this.windowTypes.Count > 0) + { + chromeOptions[WindowTypesChromeOption] = this.windowTypes; + } - perfLoggingPrefsDictionary["bufferUsageReportingInterval"] = Convert.ToInt64(prefs.BufferUsageReportingInterval.TotalMilliseconds); + foreach (KeyValuePair pair in this.additionalChromeOptions) + { + chromeOptions.Add(pair.Key, pair.Value); + } - return perfLoggingPrefsDictionary; + return chromeOptions; + } + + private static void AddAndroidOptions(Dictionary chromeOptions, ChromiumAndroidOptions androidOptions) + { + chromeOptions["androidPackage"] = androidOptions.AndroidPackage; + + if (!string.IsNullOrEmpty(androidOptions.AndroidDeviceSerial)) + { + chromeOptions["androidDeviceSerial"] = androidOptions.AndroidDeviceSerial!; } - private static Dictionary GenerateMobileEmulationSettingsDictionary(ChromiumMobileEmulationDeviceSettings? settings, string? deviceName) + if (!string.IsNullOrEmpty(androidOptions.AndroidActivity)) { - Dictionary mobileEmulationSettings = new Dictionary(); + chromeOptions["androidActivity"] = androidOptions.AndroidActivity!; + } - if (!string.IsNullOrEmpty(deviceName)) - { - mobileEmulationSettings["deviceName"] = deviceName!; - } - else if (settings != null) + if (!string.IsNullOrEmpty(androidOptions.AndroidProcess)) + { + chromeOptions["androidProcess"] = androidOptions.AndroidProcess!; + } + + if (androidOptions.UseRunningApp) + { + chromeOptions["androidUseRunningApp"] = androidOptions.UseRunningApp; + } + } + + private static Dictionary GeneratePerformanceLoggingPreferencesDictionary(ChromiumPerformanceLoggingPreferences prefs) + { + Dictionary perfLoggingPrefsDictionary = new Dictionary(); + perfLoggingPrefsDictionary["enableNetwork"] = prefs.IsCollectingNetworkEvents; + perfLoggingPrefsDictionary["enablePage"] = prefs.IsCollectingPageEvents; + + string tracingCategories = prefs.TracingCategories; + if (!string.IsNullOrEmpty(tracingCategories)) + { + perfLoggingPrefsDictionary["traceCategories"] = tracingCategories; + } + + perfLoggingPrefsDictionary["bufferUsageReportingInterval"] = Convert.ToInt64(prefs.BufferUsageReportingInterval.TotalMilliseconds); + + return perfLoggingPrefsDictionary; + } + + private static Dictionary GenerateMobileEmulationSettingsDictionary(ChromiumMobileEmulationDeviceSettings? settings, string? deviceName) + { + Dictionary mobileEmulationSettings = new Dictionary(); + + if (!string.IsNullOrEmpty(deviceName)) + { + mobileEmulationSettings["deviceName"] = deviceName!; + } + else if (settings != null) + { + mobileEmulationSettings["userAgent"] = settings.UserAgent; + Dictionary deviceMetrics = new Dictionary(); + deviceMetrics["width"] = settings.Width; + deviceMetrics["height"] = settings.Height; + deviceMetrics["pixelRatio"] = settings.PixelRatio; + if (!settings.EnableTouchEvents) { - mobileEmulationSettings["userAgent"] = settings.UserAgent; - Dictionary deviceMetrics = new Dictionary(); - deviceMetrics["width"] = settings.Width; - deviceMetrics["height"] = settings.Height; - deviceMetrics["pixelRatio"] = settings.PixelRatio; - if (!settings.EnableTouchEvents) - { - deviceMetrics["touch"] = settings.EnableTouchEvents; - } - - mobileEmulationSettings["deviceMetrics"] = deviceMetrics; + deviceMetrics["touch"] = settings.EnableTouchEvents; } - return mobileEmulationSettings; + mobileEmulationSettings["deviceMetrics"] = deviceMetrics; } + + return mobileEmulationSettings; } } diff --git a/dotnet/src/webdriver/Chromium/ChromiumPerformanceLoggingPreferences.cs b/dotnet/src/webdriver/Chromium/ChromiumPerformanceLoggingPreferences.cs index 8c392fdd54f49..0253c75fe36f1 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumPerformanceLoggingPreferences.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumPerformanceLoggingPreferences.cs @@ -20,94 +20,93 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.Chromium +namespace OpenQA.Selenium.Chromium; + +/// +/// Represents the type-safe options for setting preferences for performance +/// logging in the Chromium browser. +/// +public class ChromiumPerformanceLoggingPreferences { + private TimeSpan bufferUsageReportingInterval = TimeSpan.FromMilliseconds(1000); + private readonly List tracingCategories = new List(); + /// - /// Represents the type-safe options for setting preferences for performance - /// logging in the Chromium browser. + /// Gets or sets a value indicating whether Chromium will collect events from the Network domain. + /// Defaults to . /// - public class ChromiumPerformanceLoggingPreferences - { - private TimeSpan bufferUsageReportingInterval = TimeSpan.FromMilliseconds(1000); - private readonly List tracingCategories = new List(); + public bool IsCollectingNetworkEvents { get; set; } = true; - /// - /// Gets or sets a value indicating whether Chromium will collect events from the Network domain. - /// Defaults to . - /// - public bool IsCollectingNetworkEvents { get; set; } = true; + /// + /// Gets or sets a value indicating whether Chromium will collect events from the Page domain. + /// Defaults to . + /// + public bool IsCollectingPageEvents { get; set; } = true; - /// - /// Gets or sets a value indicating whether Chromium will collect events from the Page domain. - /// Defaults to . - /// - public bool IsCollectingPageEvents { get; set; } = true; + /// + /// Gets or sets the interval between Chromium DevTools trace buffer usage events. + /// Defaults to 1000 milliseconds. + /// + /// Thrown when an attempt is made to set + /// the value to a time span of less than or equal to zero. + public TimeSpan BufferUsageReportingInterval + { + get => this.bufferUsageReportingInterval; - /// - /// Gets or sets the interval between Chromium DevTools trace buffer usage events. - /// Defaults to 1000 milliseconds. - /// - /// Thrown when an attempt is made to set - /// the value to a time span of less than or equal to zero. - public TimeSpan BufferUsageReportingInterval + set { - get => this.bufferUsageReportingInterval; - - set + if (value <= TimeSpan.Zero) { - if (value <= TimeSpan.Zero) - { - throw new ArgumentException("Interval must be greater than zero.", nameof(value)); - } - - this.bufferUsageReportingInterval = value; + throw new ArgumentException("Interval must be greater than zero.", nameof(value)); } + + this.bufferUsageReportingInterval = value; } + } - /// - /// Gets a comma-separated list of the categories for which tracing is enabled. - /// - public string TracingCategories => string.Join(",", this.tracingCategories); + /// + /// Gets a comma-separated list of the categories for which tracing is enabled. + /// + public string TracingCategories => string.Join(",", this.tracingCategories); - /// - /// Adds a single category to the list of Chromium tracing categories for which events should be collected. - /// - /// The category to add. - /// If is or . - public void AddTracingCategory(string category) + /// + /// Adds a single category to the list of Chromium tracing categories for which events should be collected. + /// + /// The category to add. + /// If is or . + public void AddTracingCategory(string category) + { + if (string.IsNullOrEmpty(category)) { - if (string.IsNullOrEmpty(category)) - { - throw new ArgumentException("category must not be null or empty", nameof(category)); - } - - this.AddTracingCategories(category); + throw new ArgumentException("category must not be null or empty", nameof(category)); } - /// - /// Adds categories to the list of Chromium tracing categories for which events should be collected. - /// - /// An array of categories to add. - /// If is . - public void AddTracingCategories(params string[] categoriesToAdd) - { - this.AddTracingCategories((IEnumerable)categoriesToAdd); - } + this.AddTracingCategories(category); + } - /// - /// Adds categories to the list of Chromium tracing categories for which events should be collected. - /// - /// An object of categories to add. - /// If is . - public void AddTracingCategories(IEnumerable categoriesToAdd) - { - if (categoriesToAdd == null) - { - throw new ArgumentNullException(nameof(categoriesToAdd), "categoriesToAdd must not be null"); - } + /// + /// Adds categories to the list of Chromium tracing categories for which events should be collected. + /// + /// An array of categories to add. + /// If is . + public void AddTracingCategories(params string[] categoriesToAdd) + { + this.AddTracingCategories((IEnumerable)categoriesToAdd); + } - // Adding a tracing category automatically turns timeline events off. - this.tracingCategories.AddRange(categoriesToAdd); + /// + /// Adds categories to the list of Chromium tracing categories for which events should be collected. + /// + /// An object of categories to add. + /// If is . + public void AddTracingCategories(IEnumerable categoriesToAdd) + { + if (categoriesToAdd == null) + { + throw new ArgumentNullException(nameof(categoriesToAdd), "categoriesToAdd must not be null"); } + + // Adding a tracing category automatically turns timeline events off. + this.tracingCategories.AddRange(categoriesToAdd); } } diff --git a/dotnet/src/webdriver/Command.cs b/dotnet/src/webdriver/Command.cs index ddf6ed4228995..b7acb42ef2dbf 100644 --- a/dotnet/src/webdriver/Command.cs +++ b/dotnet/src/webdriver/Command.cs @@ -24,150 +24,149 @@ using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a way to send commands to the remote server +/// +public class Command { - /// - /// Provides a way to send commands to the remote server - /// - public class Command + private readonly static JsonSerializerOptions s_jsonSerializerOptions = new() { - private readonly static JsonSerializerOptions s_jsonSerializerOptions = new() + TypeInfoResolverChain = { - TypeInfoResolverChain = - { - CommandJsonSerializerContext.Default, - new DefaultJsonTypeInfoResolver() - }, - Converters = { new ResponseValueJsonConverter() } - }; + CommandJsonSerializerContext.Default, + new DefaultJsonTypeInfoResolver() + }, + Converters = { new ResponseValueJsonConverter() } + }; - /// - /// Initializes a new instance of the class using a command name and a JSON-encoded string for the parameters. - /// - /// Name of the command - /// Parameters for the command as a JSON-encoded string. - public Command(string name, string jsonParameters) - : this(null, name, ConvertParametersFromJson(jsonParameters)) - { - } + /// + /// Initializes a new instance of the class using a command name and a JSON-encoded string for the parameters. + /// + /// Name of the command + /// Parameters for the command as a JSON-encoded string. + public Command(string name, string jsonParameters) + : this(null, name, ConvertParametersFromJson(jsonParameters)) + { + } - /// - /// Initializes a new instance of the class for a Session - /// - /// Session ID the driver is using - /// Name of the command - /// Parameters for that command - /// If is . - public Command(SessionId? sessionId, string name, Dictionary? parameters) - { - this.SessionId = sessionId; - this.Parameters = parameters ?? new Dictionary(); - this.Name = name ?? throw new ArgumentNullException(nameof(name)); - } + /// + /// Initializes a new instance of the class for a Session + /// + /// Session ID the driver is using + /// Name of the command + /// Parameters for that command + /// If is . + public Command(SessionId? sessionId, string name, Dictionary? parameters) + { + this.SessionId = sessionId; + this.Parameters = parameters ?? new Dictionary(); + this.Name = name ?? throw new ArgumentNullException(nameof(name)); + } - /// - /// Gets the SessionID of the command - /// - [JsonPropertyName("sessionId")] - public SessionId? SessionId { get; } + /// + /// Gets the SessionID of the command + /// + [JsonPropertyName("sessionId")] + public SessionId? SessionId { get; } - /// - /// Gets the command name - /// - [JsonPropertyName("name")] - public string Name { get; } + /// + /// Gets the command name + /// + [JsonPropertyName("name")] + public string Name { get; } - /// - /// Gets the parameters of the command - /// - [JsonPropertyName("parameters")] - public Dictionary Parameters { get; } + /// + /// Gets the parameters of the command + /// + [JsonPropertyName("parameters")] + public Dictionary Parameters { get; } - /// - /// Gets the parameters of the command as a JSON-encoded string. - /// - public string ParametersAsJsonString + /// + /// Gets the parameters of the command as a JSON-encoded string. + /// + public string ParametersAsJsonString + { + get { - get + if (this.Parameters != null && this.Parameters.Count > 0) + { + return JsonSerializer.Serialize(this.Parameters, s_jsonSerializerOptions); + } + else { - if (this.Parameters != null && this.Parameters.Count > 0) - { - return JsonSerializer.Serialize(this.Parameters, s_jsonSerializerOptions); - } - else - { - return "{}"; - } + return "{}"; } } + } - /// - /// Returns a string of the Command object - /// - /// A string representation of the Command Object - public override string ToString() - { - return string.Concat("[", this.SessionId, "]: ", this.Name, " ", this.ParametersAsJsonString); - } + /// + /// Returns a string of the Command object + /// + /// A string representation of the Command Object + public override string ToString() + { + return string.Concat("[", this.SessionId, "]: ", this.Name, " ", this.ParametersAsJsonString); + } - /// - /// Gets the command parameters as a , with a string key, and an object value. - /// - /// The JSON-encoded string representing the command parameters. - /// A with a string keys, and an object value. - /// If is not a JSON object. - /// If is . - private static Dictionary? ConvertParametersFromJson(string value) - { - Dictionary? parameters = JsonSerializer.Deserialize>(value, CommandJsonSerializerContext.Default.DictionaryStringObject!); - return parameters; - } + /// + /// Gets the command parameters as a , with a string key, and an object value. + /// + /// The JSON-encoded string representing the command parameters. + /// A with a string keys, and an object value. + /// If is not a JSON object. + /// If is . + private static Dictionary? ConvertParametersFromJson(string value) + { + Dictionary? parameters = JsonSerializer.Deserialize>(value, CommandJsonSerializerContext.Default.DictionaryStringObject!); + return parameters; } +} - // Built-in types - [JsonSerializable(typeof(bool))] - [JsonSerializable(typeof(byte))] - [JsonSerializable(typeof(sbyte))] - [JsonSerializable(typeof(char))] - [JsonSerializable(typeof(decimal))] - [JsonSerializable(typeof(double))] - [JsonSerializable(typeof(float))] - [JsonSerializable(typeof(int))] - [JsonSerializable(typeof(uint))] - [JsonSerializable(typeof(nint))] - [JsonSerializable(typeof(nuint))] - [JsonSerializable(typeof(long))] - [JsonSerializable(typeof(ulong))] - [JsonSerializable(typeof(short))] - [JsonSerializable(typeof(ushort))] - [JsonSerializable(typeof(string))] +// Built-in types +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(byte))] +[JsonSerializable(typeof(sbyte))] +[JsonSerializable(typeof(char))] +[JsonSerializable(typeof(decimal))] +[JsonSerializable(typeof(double))] +[JsonSerializable(typeof(float))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(uint))] +[JsonSerializable(typeof(nint))] +[JsonSerializable(typeof(nuint))] +[JsonSerializable(typeof(long))] +[JsonSerializable(typeof(ulong))] +[JsonSerializable(typeof(short))] +[JsonSerializable(typeof(ushort))] +[JsonSerializable(typeof(string))] - // Selenium WebDriver types - [JsonSerializable(typeof(char[]))] - [JsonSerializable(typeof(byte[]))] - [JsonSerializable(typeof(Chromium.ChromiumNetworkConditions))] - [JsonSerializable(typeof(Cookie))] - [JsonSerializable(typeof(ReturnedCookie))] - [JsonSerializable(typeof(Proxy))] +// Selenium WebDriver types +[JsonSerializable(typeof(char[]))] +[JsonSerializable(typeof(byte[]))] +[JsonSerializable(typeof(Chromium.ChromiumNetworkConditions))] +[JsonSerializable(typeof(Cookie))] +[JsonSerializable(typeof(ReturnedCookie))] +[JsonSerializable(typeof(Proxy))] - // Selenium Dictionaries, primarily used in Capabilities - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSerializable(typeof(Dictionary))] - [JsonSourceGenerationOptions(Converters = [typeof(ResponseValueJsonConverter)])] - internal partial class CommandJsonSerializerContext : JsonSerializerContext; -} +// Selenium Dictionaries, primarily used in Capabilities +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(Dictionary))] +[JsonSourceGenerationOptions(Converters = [typeof(ResponseValueJsonConverter)])] +internal partial class CommandJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/CommandInfo.cs b/dotnet/src/webdriver/CommandInfo.cs index 7e8958aade11f..c3e4784ea4753 100644 --- a/dotnet/src/webdriver/CommandInfo.cs +++ b/dotnet/src/webdriver/CommandInfo.cs @@ -19,97 +19,96 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the information about a command. +/// +public abstract class CommandInfo : IEquatable { /// - /// Represents the information about a command. + /// Gets the unique identifier for this command within the scope of its protocol definition + /// + public abstract string CommandIdentifier { get; } + + /// + /// Returns the hash code for this object. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return this.CommandIdentifier.GetHashCode(); + } + + /// + /// Determines whether this instance and a specified object, which must also be a object, have the same value. /// - public abstract class CommandInfo : IEquatable + /// The to compare to this instance. + /// if is a and its value is the same as this instance; otherwise, . If is , the method returns . + public override bool Equals(object? obj) { - /// - /// Gets the unique identifier for this command within the scope of its protocol definition - /// - public abstract string CommandIdentifier { get; } + return this.Equals(obj as CommandInfo); + } - /// - /// Returns the hash code for this object. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() + /// + /// Determines whether this instance and another specified object have the same value. + /// + /// The to compare to this instance. + /// if the value of the parameter is the same as this instance; otherwise, . If is , the method returns . + public bool Equals(CommandInfo? other) + { + if (other is null) { - return this.CommandIdentifier.GetHashCode(); + return false; } - /// - /// Determines whether this instance and a specified object, which must also be a object, have the same value. - /// - /// The to compare to this instance. - /// if is a and its value is the same as this instance; otherwise, . If is , the method returns . - public override bool Equals(object? obj) + // Optimization for a common success case. + if (object.ReferenceEquals(this, other)) { - return this.Equals(obj as CommandInfo); + return true; } - /// - /// Determines whether this instance and another specified object have the same value. - /// - /// The to compare to this instance. - /// if the value of the parameter is the same as this instance; otherwise, . If is , the method returns . - public bool Equals(CommandInfo? other) + // If run-time types are not exactly the same, return false. + if (this.GetType() != other.GetType()) { - if (other is null) - { - return false; - } - - // Optimization for a common success case. - if (object.ReferenceEquals(this, other)) - { - return true; - } - - // If run-time types are not exactly the same, return false. - if (this.GetType() != other.GetType()) - { - return false; - } - - // Return true if the fields match. - // Note that the base class is not invoked because it is - // System.Object, which defines Equals as reference equality. - return this.CommandIdentifier == other.CommandIdentifier; + return false; } - /// - /// Determines whether two specified objects have the same value. - /// - /// The first object to compare. - /// The second object to compare. - /// if the value of is the same as the value of ; otherwise, . - public static bool operator ==(CommandInfo? left, CommandInfo? right) + // Return true if the fields match. + // Note that the base class is not invoked because it is + // System.Object, which defines Equals as reference equality. + return this.CommandIdentifier == other.CommandIdentifier; + } + + /// + /// Determines whether two specified objects have the same value. + /// + /// The first object to compare. + /// The second object to compare. + /// if the value of is the same as the value of ; otherwise, . + public static bool operator ==(CommandInfo? left, CommandInfo? right) + { + if (left is null) { - if (left is null) + if (right is null) { - if (right is null) - { - return true; - } - - return false; + return true; } - return left.Equals(right); + return false; } - /// - /// Determines whether two specified objects have different values. - /// - /// The first object to compare. - /// The second object to compare. - /// if the value of is different from the value of ; otherwise, . - public static bool operator !=(CommandInfo? left, CommandInfo? right) - { - return !(left == right); - } + return left.Equals(right); + } + + /// + /// Determines whether two specified objects have different values. + /// + /// The first object to compare. + /// The second object to compare. + /// if the value of is different from the value of ; otherwise, . + public static bool operator !=(CommandInfo? left, CommandInfo? right) + { + return !(left == right); } } diff --git a/dotnet/src/webdriver/CommandInfoRepository.cs b/dotnet/src/webdriver/CommandInfoRepository.cs index 39bd704247545..06dd4fdfe2f4f 100644 --- a/dotnet/src/webdriver/CommandInfoRepository.cs +++ b/dotnet/src/webdriver/CommandInfoRepository.cs @@ -21,129 +21,128 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Holds the information about all commands specified by the JSON wire protocol. +/// This class cannot be inherited, as it is intended to be a singleton, and +/// allowing subclasses introduces the possibility of multiple instances. +/// +public abstract class CommandInfoRepository { + private readonly Dictionary commandDictionary; + /// - /// Holds the information about all commands specified by the JSON wire protocol. - /// This class cannot be inherited, as it is intended to be a singleton, and - /// allowing subclasses introduces the possibility of multiple instances. + /// Initializes a new instance of the class. + /// Protected accessibility prevents a default instance from being created. /// - public abstract class CommandInfoRepository + protected CommandInfoRepository() { - private readonly Dictionary commandDictionary; + this.commandDictionary = new Dictionary(); + } - /// - /// Initializes a new instance of the class. - /// Protected accessibility prevents a default instance from being created. - /// - protected CommandInfoRepository() - { - this.commandDictionary = new Dictionary(); - } + /// + /// Gets the level of the W3C WebDriver specification that this repository supports. + /// + public abstract int SpecificationLevel { get; } - /// - /// Gets the level of the W3C WebDriver specification that this repository supports. - /// - public abstract int SpecificationLevel { get; } - - /// - /// Gets the that is valid for this - /// - protected abstract Type RepositoryCommandInfoType { get; } - - /// - /// Gets a value indicating whether a command with a given name has been defined. - /// - /// The name of the command to check. - /// if the command name is defined - /// If is . - public bool IsCommandNameDefined(string commandName) - { - return this.commandDictionary.ContainsKey(commandName); - } + /// + /// Gets the that is valid for this + /// + protected abstract Type RepositoryCommandInfoType { get; } - /// - /// Finds a command name for a given . - /// - /// The object for which to find the command name. - /// The name of the command defined by the command info, or if the command is not defined. - public string? FindCommandName(CommandInfo commandInfo) + /// + /// Gets a value indicating whether a command with a given name has been defined. + /// + /// The name of the command to check. + /// if the command name is defined + /// If is . + public bool IsCommandNameDefined(string commandName) + { + return this.commandDictionary.ContainsKey(commandName); + } + + /// + /// Finds a command name for a given . + /// + /// The object for which to find the command name. + /// The name of the command defined by the command info, or if the command is not defined. + public string? FindCommandName(CommandInfo commandInfo) + { + foreach (KeyValuePair pair in this.commandDictionary) { - foreach (KeyValuePair pair in this.commandDictionary) + if (pair.Value == commandInfo) { - if (pair.Value == commandInfo) - { - return pair.Key; - } + return pair.Key; } - - return null; } - /// - /// Gets the for a . - /// - /// The for which to get the information. - /// The for the specified command, or if not found or value is not . - /// If is . - public T? GetCommandInfo(string commandName) where T : CommandInfo - { - T? toReturn = default; - if (this.commandDictionary.TryGetValue(commandName, out CommandInfo? info)) - { - toReturn = info as T; - } + return null; + } - return toReturn; + /// + /// Gets the for a . + /// + /// The for which to get the information. + /// The for the specified command, or if not found or value is not . + /// If is . + public T? GetCommandInfo(string commandName) where T : CommandInfo + { + T? toReturn = default; + if (this.commandDictionary.TryGetValue(commandName, out CommandInfo? info)) + { + toReturn = info as T; } - /// - /// Tries to add a command to the list of known commands. - /// - /// Name of the command. - /// The command information. - /// if the new command has been added successfully; otherwise, . - /// - /// This method is used by WebDriver implementations to add additional custom driver-specific commands. - /// This method will not overwrite existing commands for a specific name, and will return - /// in that case. - /// - /// - /// If is or . - /// -or- - /// If is . - /// - /// If is not a valid command type for this repository. - public bool TryAddCommand(string commandName, T commandInfo) where T : CommandInfo - { - if (string.IsNullOrEmpty(commandName)) - { - throw new ArgumentNullException(nameof(commandName), "The name of the command cannot be null or the empty string."); - } + return toReturn; + } - if (commandInfo == null) - { - throw new ArgumentNullException(nameof(commandInfo), "The command information object cannot be null."); - } + /// + /// Tries to add a command to the list of known commands. + /// + /// Name of the command. + /// The command information. + /// if the new command has been added successfully; otherwise, . + /// + /// This method is used by WebDriver implementations to add additional custom driver-specific commands. + /// This method will not overwrite existing commands for a specific name, and will return + /// in that case. + /// + /// + /// If is or . + /// -or- + /// If is . + /// + /// If is not a valid command type for this repository. + public bool TryAddCommand(string commandName, T commandInfo) where T : CommandInfo + { + if (string.IsNullOrEmpty(commandName)) + { + throw new ArgumentNullException(nameof(commandName), "The name of the command cannot be null or the empty string."); + } - if (!typeof(T).IsAssignableFrom(this.RepositoryCommandInfoType)) - { - string message = string.Format(CultureInfo.InvariantCulture, "{0} is not a valid command type for this repository; command info must be of type {1}", typeof(T), this.RepositoryCommandInfoType); - throw new ArgumentException(message, nameof(commandInfo)); - } + if (commandInfo == null) + { + throw new ArgumentNullException(nameof(commandInfo), "The command information object cannot be null."); + } - if (this.commandDictionary.ContainsKey(commandName)) - { - return false; - } + if (!typeof(T).IsAssignableFrom(this.RepositoryCommandInfoType)) + { + string message = string.Format(CultureInfo.InvariantCulture, "{0} is not a valid command type for this repository; command info must be of type {1}", typeof(T), this.RepositoryCommandInfoType); + throw new ArgumentException(message, nameof(commandInfo)); + } - this.commandDictionary.Add(commandName, commandInfo); - return true; + if (this.commandDictionary.ContainsKey(commandName)) + { + return false; } - /// - /// Initializes the dictionary of commands for the CommandInfoRepository - /// - protected abstract void InitializeCommandDictionary(); + this.commandDictionary.Add(commandName, commandInfo); + return true; } + + /// + /// Initializes the dictionary of commands for the CommandInfoRepository + /// + protected abstract void InitializeCommandDictionary(); } diff --git a/dotnet/src/webdriver/Cookie.cs b/dotnet/src/webdriver/Cookie.cs index ee1a685cbbad3..292757bf7652f 100644 --- a/dotnet/src/webdriver/Cookie.cs +++ b/dotnet/src/webdriver/Cookie.cs @@ -24,361 +24,360 @@ using System.Linq; using System.Text.Json.Serialization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents a cookie in the browser. +/// +[Serializable] +public class Cookie { + private readonly string cookieName; + private readonly string cookieValue; + private readonly string? cookiePath; + private readonly string? cookieDomain; + private readonly string? sameSite; + private readonly bool isHttpOnly; + private readonly bool secure; + private readonly DateTime? cookieExpiry; + private readonly HashSet sameSiteValues = new HashSet() + { + "Strict", + "Lax", + "None" + }; + /// - /// Represents a cookie in the browser. + /// Initializes a new instance of the class with a specific name and value. /// - [Serializable] - public class Cookie + /// The name of the cookie. + /// The value of the cookie. + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value is . + public Cookie(string name, string value) + : this(name, value, null) { - private readonly string cookieName; - private readonly string cookieValue; - private readonly string? cookiePath; - private readonly string? cookieDomain; - private readonly string? sameSite; - private readonly bool isHttpOnly; - private readonly bool secure; - private readonly DateTime? cookieExpiry; - private readonly HashSet sameSiteValues = new HashSet() + } + + /// + /// Initializes a new instance of the class with a specific name, + /// value, and path. + /// + /// The name of the cookie. + /// The value of the cookie. + /// The path of the cookie. + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value is . + public Cookie(string name, string value, string? path) + : this(name, value, path, null) + { + } + + /// + /// Initializes a new instance of the class with a specific name, + /// value, path and expiration date. + /// + /// The name of the cookie. + /// The value of the cookie. + /// The path of the cookie. + /// The expiration date of the cookie. + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value is . + public Cookie(string name, string value, string? path, DateTime? expiry) + : this(name, value, null, path, expiry) + { + } + + /// + /// Initializes a new instance of the class with a specific name, + /// value, domain, path and expiration date. + /// + /// The name of the cookie. + /// The value of the cookie. + /// The domain of the cookie. + /// The path of the cookie. + /// The expiration date of the cookie. + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value is . + public Cookie(string name, string value, string? domain, string? path, DateTime? expiry) + : this(name, value, domain, path, expiry, false, false, null) + { + } + + /// + /// Initializes a new instance of the class with a specific name, + /// value, domain, path and expiration date. + /// + /// The name of the cookie. + /// The value of the cookie. + /// The domain of the cookie. + /// The path of the cookie. + /// The expiration date of the cookie. + /// if the cookie is secure; otherwise + /// if the cookie is an HTTP-only cookie; otherwise + /// The SameSite value of cookie. + /// If the name and value are both an empty string, + /// if the name contains a semi-colon, or if same site value is not valid. + /// If the name, value or currentUrl is . + public Cookie(string name, string value, string? domain, string? path, DateTime? expiry, bool secure, bool isHttpOnly, string? sameSite) + { + if (name == null) { - "Strict", - "Lax", - "None" - }; - - /// - /// Initializes a new instance of the class with a specific name and value. - /// - /// The name of the cookie. - /// The value of the cookie. - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value is . - public Cookie(string name, string value) - : this(name, value, null) + throw new ArgumentNullException(nameof(value), "Cookie name cannot be null"); + } + + if (value == null) { + throw new ArgumentNullException(nameof(value), "Cookie value cannot be null"); } - /// - /// Initializes a new instance of the class with a specific name, - /// value, and path. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The path of the cookie. - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value is . - public Cookie(string name, string value, string? path) - : this(name, value, path, null) + if (name == string.Empty && value == string.Empty) { + throw new ArgumentException("Cookie name and value cannot both be empty string"); } - /// - /// Initializes a new instance of the class with a specific name, - /// value, path and expiration date. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The path of the cookie. - /// The expiration date of the cookie. - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value is . - public Cookie(string name, string value, string? path, DateTime? expiry) - : this(name, value, null, path, expiry) + if (name.Contains(';')) { + throw new ArgumentException("Cookie names cannot contain a ';': " + name, nameof(name)); } - /// - /// Initializes a new instance of the class with a specific name, - /// value, domain, path and expiration date. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The domain of the cookie. - /// The path of the cookie. - /// The expiration date of the cookie. - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value is . - public Cookie(string name, string value, string? domain, string? path, DateTime? expiry) - : this(name, value, domain, path, expiry, false, false, null) + this.cookieName = name; + this.cookieValue = value; + if (!string.IsNullOrEmpty(path)) { + this.cookiePath = path; } - /// - /// Initializes a new instance of the class with a specific name, - /// value, domain, path and expiration date. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The domain of the cookie. - /// The path of the cookie. - /// The expiration date of the cookie. - /// if the cookie is secure; otherwise - /// if the cookie is an HTTP-only cookie; otherwise - /// The SameSite value of cookie. - /// If the name and value are both an empty string, - /// if the name contains a semi-colon, or if same site value is not valid. - /// If the name, value or currentUrl is . - public Cookie(string name, string value, string? domain, string? path, DateTime? expiry, bool secure, bool isHttpOnly, string? sameSite) + this.cookieDomain = StripPort(domain); + + if (expiry != null) { - if (name == null) - { - throw new ArgumentNullException(nameof(value), "Cookie name cannot be null"); - } + this.cookieExpiry = expiry; + } - if (value == null) - { - throw new ArgumentNullException(nameof(value), "Cookie value cannot be null"); - } + this.isHttpOnly = isHttpOnly; + this.secure = secure; - if (name == string.Empty && value == string.Empty) + if (!string.IsNullOrEmpty(sameSite)) + { + if (!sameSiteValues.Contains(sameSite)) { - throw new ArgumentException("Cookie name and value cannot both be empty string"); + throw new ArgumentException("Invalid sameSite cookie value. It should either \"Lax\", \"Strict\" or \"None\" ", nameof(sameSite)); } - if (name.Contains(';')) - { - throw new ArgumentException("Cookie names cannot contain a ';': " + name, nameof(name)); - } + this.sameSite = sameSite; + } + } - this.cookieName = name; - this.cookieValue = value; - if (!string.IsNullOrEmpty(path)) - { - this.cookiePath = path; - } + /// + /// Gets the name of the cookie. + /// + [JsonPropertyName("name")] + public string Name => this.cookieName; - this.cookieDomain = StripPort(domain); + /// + /// Gets the value of the cookie. + /// + [JsonPropertyName("value")] + public string Value => this.cookieValue; - if (expiry != null) - { - this.cookieExpiry = expiry; - } + /// + /// Gets the domain of the cookie. + /// + [JsonPropertyName("domain")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Domain => this.cookieDomain; - this.isHttpOnly = isHttpOnly; - this.secure = secure; + /// + /// Gets the path of the cookie. + /// + [JsonPropertyName("path")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public virtual string? Path => this.cookiePath; - if (!string.IsNullOrEmpty(sameSite)) - { - if (!sameSiteValues.Contains(sameSite)) - { - throw new ArgumentException("Invalid sameSite cookie value. It should either \"Lax\", \"Strict\" or \"None\" ", nameof(sameSite)); - } + /// + /// Gets a value indicating whether the cookie is secure. + /// + [JsonPropertyName("secure")] + public virtual bool Secure => this.secure; - this.sameSite = sameSite; - } - } + /// + /// Gets a value indicating whether the cookie is an HTTP-only cookie. + /// + [JsonPropertyName("httpOnly")] + public virtual bool IsHttpOnly => this.isHttpOnly; - /// - /// Gets the name of the cookie. - /// - [JsonPropertyName("name")] - public string Name => this.cookieName; - - /// - /// Gets the value of the cookie. - /// - [JsonPropertyName("value")] - public string Value => this.cookieValue; - - /// - /// Gets the domain of the cookie. - /// - [JsonPropertyName("domain")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Domain => this.cookieDomain; - - /// - /// Gets the path of the cookie. - /// - [JsonPropertyName("path")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public virtual string? Path => this.cookiePath; - - /// - /// Gets a value indicating whether the cookie is secure. - /// - [JsonPropertyName("secure")] - public virtual bool Secure => this.secure; - - /// - /// Gets a value indicating whether the cookie is an HTTP-only cookie. - /// - [JsonPropertyName("httpOnly")] - public virtual bool IsHttpOnly => this.isHttpOnly; - - /// - /// Gets the SameSite setting for the cookie. - /// - [JsonPropertyName("sameSite")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public virtual string? SameSite => this.sameSite; - - /// - /// Gets the expiration date of the cookie. - /// - [JsonIgnore] - public DateTime? Expiry => this.cookieExpiry; - - /// - /// Gets the cookie expiration date in seconds from the defined zero date (01 January 1970 00:00:00 UTC). - /// - /// This property only exists so that the JSON serializer can serialize a - /// cookie without resorting to a custom converter. - [JsonPropertyName("expiry")] - [JsonInclude] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - internal long? ExpirySeconds - { - get - { - if (this.cookieExpiry == null) - { - return null; - } - - DateTime zeroDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - TimeSpan span = this.cookieExpiry.Value.ToUniversalTime().Subtract(zeroDate); - long totalSeconds = Convert.ToInt64(span.TotalSeconds); - return totalSeconds; - } - } + /// + /// Gets the SameSite setting for the cookie. + /// + [JsonPropertyName("sameSite")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public virtual string? SameSite => this.sameSite; - /// - /// Converts a Dictionary to a Cookie. - /// - /// The Dictionary object containing the cookie parameters. - /// A object with the proper parameters set. - public static Cookie FromDictionary(Dictionary rawCookie) - { - if (rawCookie == null) - { - throw new ArgumentNullException(nameof(rawCookie)); - } + /// + /// Gets the expiration date of the cookie. + /// + [JsonIgnore] + public DateTime? Expiry => this.cookieExpiry; - string name = rawCookie["name"]!.ToString()!; - string value = string.Empty; - if (rawCookie.TryGetValue("value", out object? valueObj)) + /// + /// Gets the cookie expiration date in seconds from the defined zero date (01 January 1970 00:00:00 UTC). + /// + /// This property only exists so that the JSON serializer can serialize a + /// cookie without resorting to a custom converter. + [JsonPropertyName("expiry")] + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + internal long? ExpirySeconds + { + get + { + if (this.cookieExpiry == null) { - value = valueObj!.ToString()!; + return null; } - string path = "/"; - if (rawCookie.TryGetValue("path", out object? pathObj) && pathObj != null) - { - path = pathObj.ToString()!; - } + DateTime zeroDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + TimeSpan span = this.cookieExpiry.Value.ToUniversalTime().Subtract(zeroDate); + long totalSeconds = Convert.ToInt64(span.TotalSeconds); + return totalSeconds; + } + } - string domain = string.Empty; - if (rawCookie.TryGetValue("domain", out object? domainObj) && domainObj != null) - { - domain = domainObj.ToString()!; - } + /// + /// Converts a Dictionary to a Cookie. + /// + /// The Dictionary object containing the cookie parameters. + /// A object with the proper parameters set. + public static Cookie FromDictionary(Dictionary rawCookie) + { + if (rawCookie == null) + { + throw new ArgumentNullException(nameof(rawCookie)); + } - DateTime? expires = null; - if (rawCookie.TryGetValue("expiry", out object? expiryObj) && expiryObj != null) - { - expires = ConvertExpirationTime(expiryObj.ToString()!); - } + string name = rawCookie["name"]!.ToString()!; + string value = string.Empty; + if (rawCookie.TryGetValue("value", out object? valueObj)) + { + value = valueObj!.ToString()!; + } - bool secure = false; - if (rawCookie.TryGetValue("secure", out object? secureObj) && secureObj != null) - { - secure = bool.Parse(secureObj.ToString()!); - } + string path = "/"; + if (rawCookie.TryGetValue("path", out object? pathObj) && pathObj != null) + { + path = pathObj.ToString()!; + } - bool isHttpOnly = false; - if (rawCookie.TryGetValue("httpOnly", out object? httpOnlyObj) && httpOnlyObj != null) - { - isHttpOnly = bool.Parse(httpOnlyObj.ToString()!); - } + string domain = string.Empty; + if (rawCookie.TryGetValue("domain", out object? domainObj) && domainObj != null) + { + domain = domainObj.ToString()!; + } - string? sameSite = null; - if (rawCookie.TryGetValue("sameSite", out object? sameSiteObj)) - { - sameSite = sameSiteObj?.ToString(); - } + DateTime? expires = null; + if (rawCookie.TryGetValue("expiry", out object? expiryObj) && expiryObj != null) + { + expires = ConvertExpirationTime(expiryObj.ToString()!); + } - return new ReturnedCookie(name, value, domain, path, expires, secure, isHttpOnly, sameSite); + bool secure = false; + if (rawCookie.TryGetValue("secure", out object? secureObj) && secureObj != null) + { + secure = bool.Parse(secureObj.ToString()!); } - /// - /// Creates and returns a string representation of the cookie. - /// - /// A string representation of the cookie. - public override string ToString() + bool isHttpOnly = false; + if (rawCookie.TryGetValue("httpOnly", out object? httpOnlyObj) && httpOnlyObj != null) { - return this.cookieName + "=" + this.cookieValue - + (this.cookieExpiry == null ? string.Empty : "; expires=" + this.cookieExpiry.Value.ToUniversalTime().ToString("ddd MM dd yyyy hh:mm:ss UTC", CultureInfo.InvariantCulture)) - + (string.IsNullOrEmpty(this.cookiePath) ? string.Empty : "; path=" + this.cookiePath) - + (string.IsNullOrEmpty(this.cookieDomain) ? string.Empty : "; domain=" + this.cookieDomain) - + "; isHttpOnly= " + this.isHttpOnly + "; secure= " + this.secure + (string.IsNullOrEmpty(this.sameSite) ? string.Empty : "; sameSite=" + this.sameSite); + isHttpOnly = bool.Parse(httpOnlyObj.ToString()!); } - /// - /// Determines whether the specified Object is equal - /// to the current Object. - /// - /// The Object to compare with the - /// current Object. - /// if the specified Object - /// is equal to the current Object; otherwise, - /// . - public override bool Equals(object? obj) + string? sameSite = null; + if (rawCookie.TryGetValue("sameSite", out object? sameSiteObj)) { - // Two cookies are equal if the name and value match - if (this == obj) - { - return true; - } + sameSite = sameSiteObj?.ToString(); + } - if (obj is not Cookie cookie) - { - return false; - } + return new ReturnedCookie(name, value, domain, path, expires, secure, isHttpOnly, sameSite); + } - if (!this.cookieName.Equals(cookie.cookieName)) - { - return false; - } + /// + /// Creates and returns a string representation of the cookie. + /// + /// A string representation of the cookie. + public override string ToString() + { + return this.cookieName + "=" + this.cookieValue + + (this.cookieExpiry == null ? string.Empty : "; expires=" + this.cookieExpiry.Value.ToUniversalTime().ToString("ddd MM dd yyyy hh:mm:ss UTC", CultureInfo.InvariantCulture)) + + (string.IsNullOrEmpty(this.cookiePath) ? string.Empty : "; path=" + this.cookiePath) + + (string.IsNullOrEmpty(this.cookieDomain) ? string.Empty : "; domain=" + this.cookieDomain) + + "; isHttpOnly= " + this.isHttpOnly + "; secure= " + this.secure + (string.IsNullOrEmpty(this.sameSite) ? string.Empty : "; sameSite=" + this.sameSite); + } - return string.Equals(this.cookieValue, cookie.cookieValue); + /// + /// Determines whether the specified Object is equal + /// to the current Object. + /// + /// The Object to compare with the + /// current Object. + /// if the specified Object + /// is equal to the current Object; otherwise, + /// . + public override bool Equals(object? obj) + { + // Two cookies are equal if the name and value match + if (this == obj) + { + return true; } - /// - /// Serves as a hash function for a particular type. - /// - /// A hash code for the current Object. - public override int GetHashCode() + if (obj is not Cookie cookie) { - return this.cookieName.GetHashCode(); + return false; } - private static string? StripPort(string? domain) + if (!this.cookieName.Equals(cookie.cookieName)) { - return string.IsNullOrEmpty(domain) ? null : domain!.Split(':')[0]; + return false; } - private static DateTime? ConvertExpirationTime(string expirationTime) + return string.Equals(this.cookieValue, cookie.cookieValue); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// A hash code for the current Object. + public override int GetHashCode() + { + return this.cookieName.GetHashCode(); + } + + private static string? StripPort(string? domain) + { + return string.IsNullOrEmpty(domain) ? null : domain!.Split(':')[0]; + } + + private static DateTime? ConvertExpirationTime(string expirationTime) + { + DateTime? expires = null; + if (double.TryParse(expirationTime, NumberStyles.Number, CultureInfo.InvariantCulture, out double seconds)) { - DateTime? expires = null; - if (double.TryParse(expirationTime, NumberStyles.Number, CultureInfo.InvariantCulture, out double seconds)) + try { - try - { - expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds).ToLocalTime(); - } - catch (ArgumentOutOfRangeException) - { - expires = DateTime.MaxValue.ToLocalTime(); - } + expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds).ToLocalTime(); + } + catch (ArgumentOutOfRangeException) + { + expires = DateTime.MaxValue.ToLocalTime(); } - - return expires; } + + return expires; } } diff --git a/dotnet/src/webdriver/CookieJar.cs b/dotnet/src/webdriver/CookieJar.cs index 475fc8fdbd06c..39e405559964e 100644 --- a/dotnet/src/webdriver/CookieJar.cs +++ b/dotnet/src/webdriver/CookieJar.cs @@ -21,116 +21,115 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +internal sealed class CookieJar(WebDriver driver) : ICookieJar { - internal sealed class CookieJar(WebDriver driver) : ICookieJar + /// + /// Gets all cookies defined for the current page. + /// + public ReadOnlyCollection AllCookies { - /// - /// Gets all cookies defined for the current page. - /// - public ReadOnlyCollection AllCookies + get { - get - { - Response response = driver.Execute(DriverCommand.GetAllCookies, new Dictionary()); + Response response = driver.Execute(DriverCommand.GetAllCookies, new Dictionary()); - List toReturn = new List(); - if (response.Value is object?[] cookies) + List toReturn = new List(); + if (response.Value is object?[] cookies) + { + foreach (object? rawCookie in cookies) { - foreach (object? rawCookie in cookies) + if (rawCookie != null) { - if (rawCookie != null) - { - Cookie newCookie = Cookie.FromDictionary((Dictionary)rawCookie); - toReturn.Add(newCookie); - } + Cookie newCookie = Cookie.FromDictionary((Dictionary)rawCookie); + toReturn.Add(newCookie); } } - - return new ReadOnlyCollection(toReturn); } + + return new ReadOnlyCollection(toReturn); } + } - /// - /// Method for creating a cookie in the browser - /// - /// that represents a cookie in the browser - /// If is . - public void AddCookie(Cookie cookie) + /// + /// Method for creating a cookie in the browser + /// + /// that represents a cookie in the browser + /// If is . + public void AddCookie(Cookie cookie) + { + if (cookie is null) { - if (cookie is null) - { - throw new ArgumentNullException(nameof(cookie)); - } - - Dictionary parameters = new Dictionary(); - parameters.Add("cookie", cookie); - driver.Execute(DriverCommand.AddCookie, parameters); + throw new ArgumentNullException(nameof(cookie)); } - /// - /// Delete the cookie by passing in the name of the cookie - /// - /// The name of the cookie that is in the browser - /// If is or . - public void DeleteCookieNamed(string name) + Dictionary parameters = new Dictionary(); + parameters.Add("cookie", cookie); + driver.Execute(DriverCommand.AddCookie, parameters); + } + + /// + /// Delete the cookie by passing in the name of the cookie + /// + /// The name of the cookie that is in the browser + /// If is or . + public void DeleteCookieNamed(string name) + { + if (string.IsNullOrWhiteSpace(name)) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("Cookie name cannot be null or empty", nameof(name)); - } + throw new ArgumentException("Cookie name cannot be null or empty", nameof(name)); + } - Dictionary parameters = new() { { "name", name } }; + Dictionary parameters = new() { { "name", name } }; - driver.Execute(DriverCommand.DeleteCookie, parameters); - } + driver.Execute(DriverCommand.DeleteCookie, parameters); + } - /// - /// Delete a cookie in the browser by passing in a copy of a cookie - /// - /// An object that represents a copy of the cookie that needs to be deleted - /// If is . - public void DeleteCookie(Cookie cookie) + /// + /// Delete a cookie in the browser by passing in a copy of a cookie + /// + /// An object that represents a copy of the cookie that needs to be deleted + /// If is . + public void DeleteCookie(Cookie cookie) + { + if (cookie is null) { - if (cookie is null) - { - throw new ArgumentNullException(nameof(cookie)); - } - - this.DeleteCookieNamed(cookie.Name); + throw new ArgumentNullException(nameof(cookie)); } - /// - /// Delete All Cookies that are present in the browser - /// - public void DeleteAllCookies() + this.DeleteCookieNamed(cookie.Name); + } + + /// + /// Delete All Cookies that are present in the browser + /// + public void DeleteAllCookies() + { + driver.Execute(DriverCommand.DeleteAllCookies, null); + } + + /// + /// Method for returning a getting a cookie by name + /// + /// name of the cookie that needs to be returned + /// A Cookie from the name; or if not found. + /// If is or . + public Cookie? GetCookieNamed(string name) + { + if (string.IsNullOrWhiteSpace(name)) { - driver.Execute(DriverCommand.DeleteAllCookies, null); + throw new ArgumentException("Cookie name cannot be null or empty", nameof(name)); } - /// - /// Method for returning a getting a cookie by name - /// - /// name of the cookie that needs to be returned - /// A Cookie from the name; or if not found. - /// If is or . - public Cookie? GetCookieNamed(string name) + try { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("Cookie name cannot be null or empty", nameof(name)); - } + var rawCookie = driver.Execute(DriverCommand.GetCookie, new() { { "name", name } }).Value; - try - { - var rawCookie = driver.Execute(DriverCommand.GetCookie, new() { { "name", name } }).Value; - - return Cookie.FromDictionary((Dictionary)rawCookie!); - } - catch (NoSuchCookieException) - { - return null; - } + return Cookie.FromDictionary((Dictionary)rawCookie!); + } + catch (NoSuchCookieException) + { + return null; } } } diff --git a/dotnet/src/webdriver/DefaultFileDetector.cs b/dotnet/src/webdriver/DefaultFileDetector.cs index f25ef33720548..79f028551ea89 100644 --- a/dotnet/src/webdriver/DefaultFileDetector.cs +++ b/dotnet/src/webdriver/DefaultFileDetector.cs @@ -19,23 +19,22 @@ using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the default file detector for determining whether a file +/// must be uploaded to a remote server. +/// +public class DefaultFileDetector : IFileDetector { /// - /// Represents the default file detector for determining whether a file - /// must be uploaded to a remote server. + /// Returns a value indicating whether a specified key sequence represents + /// a file name and path. /// - public class DefaultFileDetector : IFileDetector + /// The sequence to test for file existence. + /// This method always returns in this implementation. + public bool IsFile([NotNullWhen(true)] string? keySequence) { - /// - /// Returns a value indicating whether a specified key sequence represents - /// a file name and path. - /// - /// The sequence to test for file existence. - /// This method always returns in this implementation. - public bool IsFile([NotNullWhen(true)] string? keySequence) - { - return false; - } + return false; } } diff --git a/dotnet/src/webdriver/DetachedShadowRootException.cs b/dotnet/src/webdriver/DetachedShadowRootException.cs index 1bab7cfce1adf..8fed89bc7edd0 100644 --- a/dotnet/src/webdriver/DetachedShadowRootException.cs +++ b/dotnet/src/webdriver/DetachedShadowRootException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a frame is not found. +/// +[Serializable] +public class DetachedShadowRootException : NotFoundException { /// - /// The exception that is thrown when a frame is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class DetachedShadowRootException : NotFoundException + public DetachedShadowRootException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public DetachedShadowRootException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public DetachedShadowRootException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public DetachedShadowRootException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public DetachedShadowRootException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public DetachedShadowRootException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/DevTools/AuthRequiredEventArgs.cs b/dotnet/src/webdriver/DevTools/AuthRequiredEventArgs.cs index 6d6a8677c2e59..489029a3521fd 100644 --- a/dotnet/src/webdriver/DevTools/AuthRequiredEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/AuthRequiredEventArgs.cs @@ -19,32 +19,31 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the AuthRequired event is raised. +/// +public class AuthRequiredEventArgs : EventArgs { /// - /// Event arguments present when the AuthRequired event is raised. + /// Initializes a new instance of the type. /// - public class AuthRequiredEventArgs : EventArgs + /// The request ID of the request raised the event. + /// The URI for which the event is raised. + public AuthRequiredEventArgs(string requestId, string uri) { - /// - /// Initializes a new instance of the type. - /// - /// The request ID of the request raised the event. - /// The URI for which the event is raised. - public AuthRequiredEventArgs(string requestId, string uri) - { - Uri = uri; - RequestId = requestId; - } + Uri = uri; + RequestId = requestId; + } - /// - /// Gets the URI for which the event is raised. - /// - public string Uri { get; } + /// + /// Gets the URI for which the event is raised. + /// + public string Uri { get; } - /// - /// Gets the request ID of the request raising the event. - /// - public string RequestId { get; } - } + /// + /// Gets the request ID of the request raising the event. + /// + public string RequestId { get; } } diff --git a/dotnet/src/webdriver/DevTools/BindingCalledEventArgs.cs b/dotnet/src/webdriver/DevTools/BindingCalledEventArgs.cs index d62553cb76d4b..8205a315b76c8 100644 --- a/dotnet/src/webdriver/DevTools/BindingCalledEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/BindingCalledEventArgs.cs @@ -19,39 +19,38 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the BindingCalled event is raised. +/// +public class BindingCalledEventArgs : EventArgs { /// - /// Event arguments present when the BindingCalled event is raised. + /// Initializes a new instance of the type. /// - public class BindingCalledEventArgs : EventArgs + /// The execution ID of the call to the binding. + /// The name of the call to the binding. + /// The payload of the call to the binding. + public BindingCalledEventArgs(long executionContextId, string name, string payload) { - /// - /// Initializes a new instance of the type. - /// - /// The execution ID of the call to the binding. - /// The name of the call to the binding. - /// The payload of the call to the binding. - public BindingCalledEventArgs(long executionContextId, string name, string payload) - { - this.ExecutionContextId = executionContextId; - this.Name = name; - this.Payload = payload; - } + this.ExecutionContextId = executionContextId; + this.Name = name; + this.Payload = payload; + } - /// - /// Gets the execution context ID of the call to the binding. - /// - public long ExecutionContextId { get; } + /// + /// Gets the execution context ID of the call to the binding. + /// + public long ExecutionContextId { get; } - /// - /// Gets the name of the call to the binding. - /// - public string Name { get; } + /// + /// Gets the name of the call to the binding. + /// + public string Name { get; } - /// - /// Gets the payload of the call to the binding. - /// - public string Payload { get; } - } + /// + /// Gets the payload of the call to the binding. + /// + public string Payload { get; } } diff --git a/dotnet/src/webdriver/DevTools/CommandResponseException.cs b/dotnet/src/webdriver/DevTools/CommandResponseException.cs index cfda351efc23d..88680cf779f74 100644 --- a/dotnet/src/webdriver/DevTools/CommandResponseException.cs +++ b/dotnet/src/webdriver/DevTools/CommandResponseException.cs @@ -19,44 +19,43 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents an error generated by a command. +/// +[Serializable] +public class CommandResponseException : WebDriverException { /// - /// Represents an error generated by a command. + /// Initializes a new instance of the class. /// - [Serializable] - public class CommandResponseException : WebDriverException + public CommandResponseException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public CommandResponseException() - : base() - { - } - - /// - /// Initializes a new instance of the class with the specified message. - /// - /// The message of the exception. - public CommandResponseException(string? message) - : base(message) - { - } + } - /// - /// Initializes a new instance of the class with the specified message and inner exception. - /// - /// The message of the exception. - /// The inner exception for this exception. - public CommandResponseException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with the specified message. + /// + /// The message of the exception. + public CommandResponseException(string? message) + : base(message) + { + } - /// - /// Gets the numeric error code of the exception. - /// - public long Code { get; set; } + /// + /// Initializes a new instance of the class with the specified message and inner exception. + /// + /// The message of the exception. + /// The inner exception for this exception. + public CommandResponseException(string? message, Exception? innerException) + : base(message, innerException) + { } + + /// + /// Gets the numeric error code of the exception. + /// + public long Code { get; set; } } diff --git a/dotnet/src/webdriver/DevTools/CommandResponseExtensions.cs b/dotnet/src/webdriver/DevTools/CommandResponseExtensions.cs index 2c6f5f574ad38..142a6a2ec4009 100644 --- a/dotnet/src/webdriver/DevTools/CommandResponseExtensions.cs +++ b/dotnet/src/webdriver/DevTools/CommandResponseExtensions.cs @@ -17,23 +17,22 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides extension methods for command responses. +/// +public static class ICommandResponseExtensions { /// - /// Provides extension methods for command responses. + /// Returns the strongly-typed response for an object impelementing the interface. /// - public static class ICommandResponseExtensions + /// The concrete implementation type of command response expected. + /// The object to convert to the implementation type + /// The concrete implementation of the command response, or if is not the right type. + public static TCommandResponse? GetResponse(this ICommandResponse response) + where TCommandResponse : class, ICommandResponse { - /// - /// Returns the strongly-typed response for an object impelementing the interface. - /// - /// The concrete implementation type of command response expected. - /// The object to convert to the implementation type - /// The concrete implementation of the command response, or if is not the right type. - public static TCommandResponse? GetResponse(this ICommandResponse response) - where TCommandResponse : class, ICommandResponse - { - return response as TCommandResponse; - } + return response as TCommandResponse; } } diff --git a/dotnet/src/webdriver/DevTools/CommandResponseTypeMap.cs b/dotnet/src/webdriver/DevTools/CommandResponseTypeMap.cs index 0d467fbc8dd76..bd8f56a11cdcc 100644 --- a/dotnet/src/webdriver/DevTools/CommandResponseTypeMap.cs +++ b/dotnet/src/webdriver/DevTools/CommandResponseTypeMap.cs @@ -21,60 +21,59 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class that maps a DevTools Protocol command's type to the type of object returned by the command. +/// +public class CommandResponseTypeMap { + private readonly IDictionary commandResponseTypeDictionary = new Dictionary(); + /// - /// Class that maps a DevTools Protocol command's type to the type of object returned by the command. + /// Adds mapping to a response type for a specified command type. /// - public class CommandResponseTypeMap + /// The type of command to add the mapping for. + /// The type of response object corresponding to the command. + /// If or are . + public void AddCommandResponseType(Type commandSettingsType, Type commandResponseType) { - private readonly IDictionary commandResponseTypeDictionary = new Dictionary(); - - /// - /// Adds mapping to a response type for a specified command type. - /// - /// The type of command to add the mapping for. - /// The type of response object corresponding to the command. - /// If or are . - public void AddCommandResponseType(Type commandSettingsType, Type commandResponseType) + if (commandSettingsType is null) { - if (commandSettingsType is null) - { - throw new ArgumentNullException(nameof(commandSettingsType)); - } - - if (commandResponseType is null) - { - throw new ArgumentNullException(nameof(commandResponseType)); - } - - if (!commandResponseTypeDictionary.ContainsKey(commandSettingsType)) - { - commandResponseTypeDictionary.Add(commandSettingsType, commandResponseType); - } + throw new ArgumentNullException(nameof(commandSettingsType)); } - /// - /// Gets the command response type corresponding to the specified command type. - /// - /// The type of command for which to retrieve the response type. - /// The returned response type. - /// if the specified command type has a mapped response type; otherwise, . - public bool TryGetCommandResponseType([NotNullWhen(true)] out Type? commandResponseType) - where T : ICommand + if (commandResponseType is null) { - return commandResponseTypeDictionary.TryGetValue(typeof(T), out commandResponseType); + throw new ArgumentNullException(nameof(commandResponseType)); } - /// - /// Gets the command response type corresponding to the specified command type. - /// - /// The type of command for which to retrieve the response type. - /// The returned response type. - /// if the specified command type has a mapped response type; otherwise, . - public bool TryGetCommandResponseType(ICommand command, [NotNullWhen(true)] out Type? commandResponseType) + if (!commandResponseTypeDictionary.ContainsKey(commandSettingsType)) { - return commandResponseTypeDictionary.TryGetValue(command.GetType(), out commandResponseType); + commandResponseTypeDictionary.Add(commandSettingsType, commandResponseType); } } + + /// + /// Gets the command response type corresponding to the specified command type. + /// + /// The type of command for which to retrieve the response type. + /// The returned response type. + /// if the specified command type has a mapped response type; otherwise, . + public bool TryGetCommandResponseType([NotNullWhen(true)] out Type? commandResponseType) + where T : ICommand + { + return commandResponseTypeDictionary.TryGetValue(typeof(T), out commandResponseType); + } + + /// + /// Gets the command response type corresponding to the specified command type. + /// + /// The type of command for which to retrieve the response type. + /// The returned response type. + /// if the specified command type has a mapped response type; otherwise, . + public bool TryGetCommandResponseType(ICommand command, [NotNullWhen(true)] out Type? commandResponseType) + { + return commandResponseTypeDictionary.TryGetValue(command.GetType(), out commandResponseType); + } } diff --git a/dotnet/src/webdriver/DevTools/ConsoleApiArgument.cs b/dotnet/src/webdriver/DevTools/ConsoleApiArgument.cs index 18b9cc445ec58..f7c619d70878d 100644 --- a/dotnet/src/webdriver/DevTools/ConsoleApiArgument.cs +++ b/dotnet/src/webdriver/DevTools/ConsoleApiArgument.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents information about a an argument in call to the browser's console API. +/// +public class ConsoleApiArgument { /// - /// Represents information about a an argument in call to the browser's console API. + /// Initializes a new instance of the type. /// - public class ConsoleApiArgument + /// The type of the argument in the call to the browser's console API. + /// The value of the argument in the call to the browser's console API. + /// If is . + public ConsoleApiArgument(string type, string? value) { - /// - /// Initializes a new instance of the type. - /// - /// The type of the argument in the call to the browser's console API. - /// The value of the argument in the call to the browser's console API. - /// If is . - public ConsoleApiArgument(string type, string? value) - { - Type = type ?? throw new ArgumentNullException(nameof(type)); - Value = value; - } + Type = type ?? throw new ArgumentNullException(nameof(type)); + Value = value; + } - /// - /// Gets the type of the argument in the call to the browser's console API. - /// - public string Type { get; } + /// + /// Gets the type of the argument in the call to the browser's console API. + /// + public string Type { get; } - /// - /// Gets the value of the argument in the call to the browser's console API. - /// - public string? Value { get; } - } + /// + /// Gets the value of the argument in the call to the browser's console API. + /// + public string? Value { get; } } diff --git a/dotnet/src/webdriver/DevTools/ConsoleApiCalledEventArgs.cs b/dotnet/src/webdriver/DevTools/ConsoleApiCalledEventArgs.cs index 86a3ee8c35b2d..46ac9fe4b814a 100644 --- a/dotnet/src/webdriver/DevTools/ConsoleApiCalledEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/ConsoleApiCalledEventArgs.cs @@ -20,40 +20,39 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the ConsoleApiCalled event is raised. +/// +public class ConsoleApiCalledEventArgs : EventArgs { /// - /// Event arguments present when the ConsoleApiCalled event is raised. + /// Initializes a new instance of the type. /// - public class ConsoleApiCalledEventArgs : EventArgs + /// The time stanp when the browser's console API is called. + /// The type of message when the browser's console API is called. + /// The arguments of the call to the browser's console API. + /// If is . + public ConsoleApiCalledEventArgs(DateTime timestamp, string type, ReadOnlyCollection arguments) { - /// - /// Initializes a new instance of the type. - /// - /// The time stanp when the browser's console API is called. - /// The type of message when the browser's console API is called. - /// The arguments of the call to the browser's console API. - /// If is . - public ConsoleApiCalledEventArgs(DateTime timestamp, string type, ReadOnlyCollection arguments) - { - Timestamp = timestamp; - Type = type; - Arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); - } + Timestamp = timestamp; + Type = type; + Arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); + } - /// - /// Gets the time stanp when the browser's console API is called. - /// - public DateTime Timestamp { get; } + /// + /// Gets the time stanp when the browser's console API is called. + /// + public DateTime Timestamp { get; } - /// - /// Gets the type of message when the browser's console API is called. - /// - public string Type { get; } + /// + /// Gets the type of message when the browser's console API is called. + /// + public string Type { get; } - /// - /// Gets the arguments of the call to the browser's console API. - /// - public ReadOnlyCollection Arguments { get; } - } + /// + /// Gets the arguments of the call to the browser's console API. + /// + public ReadOnlyCollection Arguments { get; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsCommandData.cs b/dotnet/src/webdriver/DevTools/DevToolsCommandData.cs index aaf0531bc375f..193d14b286601 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsCommandData.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsCommandData.cs @@ -23,83 +23,82 @@ using System.Text.Json.Serialization; using System.Threading; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// The information for each DevTools command +/// +public class DevToolsCommandData { /// - /// The information for each DevTools command + /// Initializes a new instance of the DevToolsCommandData class. /// - public class DevToolsCommandData + /// The ID of the command execution. + /// The method name of the DevTools command. + /// The parameters of the DevTools command. + /// If is . + public DevToolsCommandData(long commandId, string commandName, JsonNode commandParameters) + : this(commandId, null, commandName, commandParameters) { - /// - /// Initializes a new instance of the DevToolsCommandData class. - /// - /// The ID of the command execution. - /// The method name of the DevTools command. - /// The parameters of the DevTools command. - /// If is . - public DevToolsCommandData(long commandId, string commandName, JsonNode commandParameters) - : this(commandId, null, commandName, commandParameters) - { - } + } - /// - /// Initializes a new instance of the DevToolsCommandData class. - /// - /// The ID of the command execution. - /// The session ID of the current command execution. - /// The method name of the DevTools command. - /// The parameters of the DevTools command. - /// If is . - public DevToolsCommandData(long commandId, string? sessionId, string commandName, JsonNode commandParameters) - { - CommandId = commandId; - SessionId = sessionId; - CommandName = commandName ?? throw new ArgumentNullException(nameof(commandName)); - CommandParameters = commandParameters; - SyncEvent = new ManualResetEventSlim(false); - } + /// + /// Initializes a new instance of the DevToolsCommandData class. + /// + /// The ID of the command execution. + /// The session ID of the current command execution. + /// The method name of the DevTools command. + /// The parameters of the DevTools command. + /// If is . + public DevToolsCommandData(long commandId, string? sessionId, string commandName, JsonNode commandParameters) + { + CommandId = commandId; + SessionId = sessionId; + CommandName = commandName ?? throw new ArgumentNullException(nameof(commandName)); + CommandParameters = commandParameters; + SyncEvent = new ManualResetEventSlim(false); + } - /// - /// Gets the session ID of the command. - /// - [JsonPropertyName("sessionId")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? SessionId { get; } + /// + /// Gets the session ID of the command. + /// + [JsonPropertyName("sessionId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? SessionId { get; } - /// - /// Gets the numeric ID of the command execution. - /// - [JsonPropertyName("id")] - public long CommandId { get; } + /// + /// Gets the numeric ID of the command execution. + /// + [JsonPropertyName("id")] + public long CommandId { get; } - /// - /// Gets the method name of the command. - /// - [JsonPropertyName("method")] - public string CommandName { get; } + /// + /// Gets the method name of the command. + /// + [JsonPropertyName("method")] + public string CommandName { get; } - /// - /// Gets the parameters for the command. - /// - [JsonPropertyName("params")] - public JsonNode CommandParameters { get; } + /// + /// Gets the parameters for the command. + /// + [JsonPropertyName("params")] + public JsonNode CommandParameters { get; } - /// - /// Gets a ManualResetEventSlim on which execution of the command can be synchronized. - /// - [JsonIgnore] - public ManualResetEventSlim SyncEvent { get; } + /// + /// Gets a ManualResetEventSlim on which execution of the command can be synchronized. + /// + [JsonIgnore] + public ManualResetEventSlim SyncEvent { get; } - /// - /// Get or sets the result of the command execution. - /// - [JsonIgnore] - public JsonElement Result { get; set; } + /// + /// Get or sets the result of the command execution. + /// + [JsonIgnore] + public JsonElement Result { get; set; } - /// - /// Gets or sets a value indicating whether the command resulted in an error response. - /// - [JsonIgnore] - public bool IsError { get; set; } - } + /// + /// Gets or sets a value indicating whether the command resulted in an error response. + /// + [JsonIgnore] + public bool IsError { get; set; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index 2bc67741fecf7..2c74de237eb26 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -20,117 +20,116 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Interface providing version-independent implementations of operations available using the DevTools Protocol. +/// +public abstract class DevToolsDomains { + // By default, we will look for a supported version within this + // number of versions, as that will most likely still work. + private const int DefaultVersionRange = 5; + + // This is the list of known supported DevTools version implementation. + // When new versions are implemented for support, new types must be + // added to this array and to the method below. + private static int[] SupportedDevToolsVersions => + [ + 133, + 135, + 134, + ]; + + private static DevToolsDomains? CreateDevToolsDomain(int protocolVersion, DevToolsSession session) => protocolVersion switch + { + 133 => new V133.V133Domains(session), + 135 => new V135.V135Domains(session), + 134 => new V134.V134Domains(session), + _ => null + }; + + /// + /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. + /// + public abstract DevToolsSessionDomains VersionSpecificDomains { get; } + + /// + /// Gets the object used for manipulating network information in the browser. + /// + public abstract Network Network { get; } + + /// + /// Gets the object used for manipulating the browser's JavaScript execution. + /// + public abstract JavaScript JavaScript { get; } + + /// + /// Gets the object used for manipulating DevTools Protocol targets. + /// + public abstract Target Target { get; } + + /// + /// Gets the object used for manipulating the browser's logs. + /// + public abstract Log Log { get; } + /// - /// Interface providing version-independent implementations of operations available using the DevTools Protocol. + /// Initializes the supplied DevTools session's domains for the specified browser version. /// - public abstract class DevToolsDomains + /// The version of the DevTools Protocol to use. + /// The for which to initialize the domains. + /// The object containing the version-specific domains. + /// If is negative. + /// If the desired protocol version is not supported. + public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSession session) { - // By default, we will look for a supported version within this - // number of versions, as that will most likely still work. - private const int DefaultVersionRange = 5; - - // This is the list of known supported DevTools version implementation. - // When new versions are implemented for support, new types must be - // added to this array and to the method below. - private static int[] SupportedDevToolsVersions => - [ - 133, - 135, - 134, - ]; - - private static DevToolsDomains? CreateDevToolsDomain(int protocolVersion, DevToolsSession session) => protocolVersion switch - { - 133 => new V133.V133Domains(session), - 135 => new V135.V135Domains(session), - 134 => new V134.V134Domains(session), - _ => null - }; - - /// - /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. - /// - public abstract DevToolsSessionDomains VersionSpecificDomains { get; } - - /// - /// Gets the object used for manipulating network information in the browser. - /// - public abstract Network Network { get; } - - /// - /// Gets the object used for manipulating the browser's JavaScript execution. - /// - public abstract JavaScript JavaScript { get; } - - /// - /// Gets the object used for manipulating DevTools Protocol targets. - /// - public abstract Target Target { get; } - - /// - /// Gets the object used for manipulating the browser's logs. - /// - public abstract Log Log { get; } - - /// - /// Initializes the supplied DevTools session's domains for the specified browser version. - /// - /// The version of the DevTools Protocol to use. - /// The for which to initialize the domains. - /// The object containing the version-specific domains. - /// If is negative. - /// If the desired protocol version is not supported. - public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSession session) + return InitializeDomains(protocolVersion, session, DefaultVersionRange); + } + + /// + /// Initializes the supplied DevTools session's domains for the specified browser version within the specified number of versions. + /// + /// The version of the DevTools Protocol to use. + /// The for which to initialize the domains. + /// The range of versions within which to match the provided version number. Defaults to 5 versions. + /// The object containing the version-specific domains. + /// If is negative. + /// If the desired protocol version is not in the supported range. + public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSession session, int versionRange) + { + if (versionRange < 0) { - return InitializeDomains(protocolVersion, session, DefaultVersionRange); + throw new ArgumentException("Version range must be positive", nameof(versionRange)); } - /// - /// Initializes the supplied DevTools session's domains for the specified browser version within the specified number of versions. - /// - /// The version of the DevTools Protocol to use. - /// The for which to initialize the domains. - /// The range of versions within which to match the provided version number. Defaults to 5 versions. - /// The object containing the version-specific domains. - /// If is negative. - /// If the desired protocol version is not in the supported range. - public static DevToolsDomains InitializeDomains(int protocolVersion, DevToolsSession session, int versionRange) + // Return fast on an exact match + DevToolsDomains? domains = CreateDevToolsDomain(protocolVersion, session); + if (domains is not null) { - if (versionRange < 0) - { - throw new ArgumentException("Version range must be positive", nameof(versionRange)); - } + return domains; + } - // Return fast on an exact match - DevToolsDomains? domains = CreateDevToolsDomain(protocolVersion, session); - if (domains is not null) - { - return domains; - } + return CreateFallbackDomain(protocolVersion, session, versionRange); + } - return CreateFallbackDomain(protocolVersion, session, versionRange); - } + private static DevToolsDomains CreateFallbackDomain(int desiredVersion, DevToolsSession session, int versionRange) + { + // Get the list of supported versions and sort descending + List supportedVersions = new List(SupportedDevToolsVersions); + supportedVersions.Sort((first, second) => second.CompareTo(first)); - private static DevToolsDomains CreateFallbackDomain(int desiredVersion, DevToolsSession session, int versionRange) + foreach (int supportedVersion in supportedVersions) { - // Get the list of supported versions and sort descending - List supportedVersions = new List(SupportedDevToolsVersions); - supportedVersions.Sort((first, second) => second.CompareTo(first)); - - foreach (int supportedVersion in supportedVersions) + // Match the version with the desired version within the + // version range, using "The Price Is Right" style matching + // (that is, closest without going over). + if (desiredVersion >= supportedVersion && desiredVersion - supportedVersion < versionRange) { - // Match the version with the desired version within the - // version range, using "The Price Is Right" style matching - // (that is, closest without going over). - if (desiredVersion >= supportedVersion && desiredVersion - supportedVersion < versionRange) - { - return CreateDevToolsDomain(supportedVersion, session)!; - } + return CreateDevToolsDomain(supportedVersion, session)!; } - - throw new WebDriverException($"DevTools version is not in the supported range. Desired version={desiredVersion}, range={versionRange}. Supported versions: {string.Join(", ", supportedVersions)}"); } + + throw new WebDriverException($"DevTools version is not in the supported range. Desired version={desiredVersion}, range={versionRange}. Supported versions: {string.Join(", ", supportedVersions)}"); } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsEventData.cs b/dotnet/src/webdriver/DevTools/DevToolsEventData.cs index a44bd800de9c5..14b0eab54a473 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsEventData.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsEventData.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class containing the data used for an event raised by the DevTools session. +/// +public class DevToolsEventData { /// - /// Class containing the data used for an event raised by the DevTools session. + /// Initializes a new instance of the DevToolsEventData class. /// - public class DevToolsEventData + /// The type of the event args for the event to be raised. + /// The method that will be used to invoke the event. + /// If or is . + public DevToolsEventData(Type eventArgsType, Action invoker) { - /// - /// Initializes a new instance of the DevToolsEventData class. - /// - /// The type of the event args for the event to be raised. - /// The method that will be used to invoke the event. - /// If or is . - public DevToolsEventData(Type eventArgsType, Action invoker) - { - EventArgsType = eventArgsType ?? throw new ArgumentNullException(nameof(eventArgsType)); - EventInvoker = invoker ?? throw new ArgumentNullException(nameof(invoker)); - } + EventArgsType = eventArgsType ?? throw new ArgumentNullException(nameof(eventArgsType)); + EventInvoker = invoker ?? throw new ArgumentNullException(nameof(invoker)); + } - /// - /// Gets the type of the event args object for the event. - /// - public Type EventArgsType { get; } + /// + /// Gets the type of the event args object for the event. + /// + public Type EventArgsType { get; } - /// - /// The method to called to raise the event. - /// - public Action EventInvoker { get; } - } + /// + /// The method to called to raise the event. + /// + public Action EventInvoker { get; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsExtensionMethods.cs b/dotnet/src/webdriver/DevTools/DevToolsExtensionMethods.cs index 21711f1822b75..a583370e4682e 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsExtensionMethods.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsExtensionMethods.cs @@ -17,70 +17,69 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides extension methods for translation to and from the Chrome DevTools Protocol data structures. +/// +public static class DevToolsExtensionMethods { - /// - /// Provides extension methods for translation to and from the Chrome DevTools Protocol data structures. - /// - public static class DevToolsExtensionMethods - { - ///// - ///// Translates a to the format to use with the Chrome DevTools Protocol cookie - ///// manipulation methods. - ///// - ///// The to translate. - ///// A command settings object suitable for use with the Chromium DevTools Protocol manipulation methods. - //public static Network.SetCookieCommandSettings ToDevToolsSetCookieCommandSettings(this Cookie cookie) - //{ - // Network.SetCookieCommandSettings commandSettings = new Network.SetCookieCommandSettings(); - // commandSettings.Name = cookie.Name; - // commandSettings.Value = cookie.Value; - // commandSettings.Domain = cookie.Domain; - // commandSettings.Path = cookie.Path; - // commandSettings.HttpOnly = cookie.IsHttpOnly; - // commandSettings.Secure = cookie.Secure; - // commandSettings.Expires = cookie.ExpirySeconds; - // return commandSettings; - //} + ///// + ///// Translates a to the format to use with the Chrome DevTools Protocol cookie + ///// manipulation methods. + ///// + ///// The to translate. + ///// A command settings object suitable for use with the Chromium DevTools Protocol manipulation methods. + //public static Network.SetCookieCommandSettings ToDevToolsSetCookieCommandSettings(this Cookie cookie) + //{ + // Network.SetCookieCommandSettings commandSettings = new Network.SetCookieCommandSettings(); + // commandSettings.Name = cookie.Name; + // commandSettings.Value = cookie.Value; + // commandSettings.Domain = cookie.Domain; + // commandSettings.Path = cookie.Path; + // commandSettings.HttpOnly = cookie.IsHttpOnly; + // commandSettings.Secure = cookie.Secure; + // commandSettings.Expires = cookie.ExpirySeconds; + // return commandSettings; + //} - ///// - ///// Converts an array of Chrome DevTools Protocol cookie objects to a list of Selenium objects. - ///// - ///// The array of Chrome DevTools Protocol cookies to convert. - ///// A ReadOnlyCollection of objects. - //public static ReadOnlyCollection ToSeleniumCookies(this Network.Cookie[] cookies) - //{ - // List seleniumCookies = new List(); - // foreach(var cookie in cookies) - // { - // seleniumCookies.Add(cookie.ToSeleniumCookie()); - // } + ///// + ///// Converts an array of Chrome DevTools Protocol cookie objects to a list of Selenium objects. + ///// + ///// The array of Chrome DevTools Protocol cookies to convert. + ///// A ReadOnlyCollection of objects. + //public static ReadOnlyCollection ToSeleniumCookies(this Network.Cookie[] cookies) + //{ + // List seleniumCookies = new List(); + // foreach(var cookie in cookies) + // { + // seleniumCookies.Add(cookie.ToSeleniumCookie()); + // } - // return seleniumCookies.AsReadOnly(); - //} + // return seleniumCookies.AsReadOnly(); + //} - ///// - ///// Converts a Chrome DevTools Protocol cookie object to a Selenium objects. - ///// - ///// The Chrome DevTools Protocol cookie to convert. - ///// A Selenium object. - //public static Cookie ToSeleniumCookie(this Network.Cookie cookie) - //{ - // Dictionary cookieValues = new Dictionary(); - // cookieValues["name"] = cookie.Name; - // cookieValues["value"] = cookie.Value; - // cookieValues["domain"] = cookie.Domain; - // cookieValues["path"] = cookie.Path; - // cookieValues["httpOnly"] = cookie.HttpOnly; - // cookieValues["secure"] = cookie.Secure; - // DateTime ? expires = null; - // if (!cookie.Secure) - // { - // cookieValues["expiry"] = cookie.Expires; - // } + ///// + ///// Converts a Chrome DevTools Protocol cookie object to a Selenium objects. + ///// + ///// The Chrome DevTools Protocol cookie to convert. + ///// A Selenium object. + //public static Cookie ToSeleniumCookie(this Network.Cookie cookie) + //{ + // Dictionary cookieValues = new Dictionary(); + // cookieValues["name"] = cookie.Name; + // cookieValues["value"] = cookie.Value; + // cookieValues["domain"] = cookie.Domain; + // cookieValues["path"] = cookie.Path; + // cookieValues["httpOnly"] = cookie.HttpOnly; + // cookieValues["secure"] = cookie.Secure; + // DateTime ? expires = null; + // if (!cookie.Secure) + // { + // cookieValues["expiry"] = cookie.Expires; + // } - // Cookie seleniumCookie = Cookie.FromDictionary(cookieValues); - // return seleniumCookie; - //} - } + // Cookie seleniumCookie = Cookie.FromDictionary(cookieValues); + // return seleniumCookie; + //} } diff --git a/dotnet/src/webdriver/DevTools/DevToolsOptions.cs b/dotnet/src/webdriver/DevTools/DevToolsOptions.cs index e0b5f365261a7..6a884ebffc823 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsOptions.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsOptions.cs @@ -17,22 +17,21 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Contains options configuring the DevTools session. +/// +public class DevToolsOptions { /// - /// Contains options configuring the DevTools session. + /// Enables or disables waiting for the debugger when creating a new target. By default WaitForDebuggerOnStart is disabled. + /// If enabled, all targets will be halted until the runtime.runIfWaitingForDebugger is invoked. + /// + public bool WaitForDebuggerOnStart { get; set; } + /// + /// The specific version of the Developer Tools debugging protocol to use. + /// If left NULL the protocol version will be determined automatically based on the browser version. /// - public class DevToolsOptions - { - /// - /// Enables or disables waiting for the debugger when creating a new target. By default WaitForDebuggerOnStart is disabled. - /// If enabled, all targets will be halted until the runtime.runIfWaitingForDebugger is invoked. - /// - public bool WaitForDebuggerOnStart { get; set; } - /// - /// The specific version of the Developer Tools debugging protocol to use. - /// If left NULL the protocol version will be determined automatically based on the browser version. - /// - public int? ProtocolVersion { get; set; } - } + public int? ProtocolVersion { get; set; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index 9e538d8fea21f..b04128d6a25fa 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -28,643 +28,642 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents a WebSocket connection to a running DevTools instance that can be used to send +/// commands and receive events. +/// +[RequiresUnreferencedCode(CDP_AOTIncompatibilityMessage)] +[RequiresDynamicCode(CDP_AOTIncompatibilityMessage)] +public class DevToolsSession : IDevToolsSession { + internal const string CDP_AOTIncompatibilityMessage = "CDP is not compatible with trimming or AOT."; + /// - /// Represents a WebSocket connection to a running DevTools instance that can be used to send - /// commands and receive events. - /// - [RequiresUnreferencedCode(CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(CDP_AOTIncompatibilityMessage)] - public class DevToolsSession : IDevToolsSession - { - internal const string CDP_AOTIncompatibilityMessage = "CDP is not compatible with trimming or AOT."; - - /// - /// A value indicating that the version of the DevTools protocol in use - /// by the browser should be automatically detected. - /// - public const int AutoDetectDevToolsProtocolVersion = 0; - - private readonly string debuggerEndpoint; - private readonly TimeSpan openConnectionWaitTimeSpan = TimeSpan.FromSeconds(30); - private readonly TimeSpan closeConnectionWaitTimeSpan = TimeSpan.FromSeconds(2); - - private bool isDisposed = false; - private string? attachedTargetId; - - private WebSocketConnection? connection; - private ConcurrentDictionary pendingCommands = new ConcurrentDictionary(); - private readonly BlockingCollection messageQueue = new BlockingCollection(); - private readonly Task messageQueueMonitorTask; - private long currentCommandId = 0; - private readonly DevToolsOptions options; - - private readonly static ILogger logger = Internal.Logging.Log.GetLogger(); - - /// - /// Initializes a new instance of the DevToolsSession class, using the specified WebSocket endpoint. - /// - /// - [Obsolete("Use DevToolsSession(string endpointAddress, DevToolsOptions options)")] - public DevToolsSession(string endpointAddress) : this(endpointAddress, new DevToolsOptions()) { } - - /// - /// Initializes a new instance of the DevToolsSession class, using the specified WebSocket endpoint and specified DevTools options. - /// - /// - /// - /// If is , or is or . - public DevToolsSession(string endpointAddress, DevToolsOptions options) - { - if (string.IsNullOrWhiteSpace(endpointAddress)) - { - throw new ArgumentNullException(nameof(endpointAddress)); - } + /// A value indicating that the version of the DevTools protocol in use + /// by the browser should be automatically detected. + /// + public const int AutoDetectDevToolsProtocolVersion = 0; - this.options = options ?? throw new ArgumentNullException(nameof(options)); - this.CommandTimeout = TimeSpan.FromSeconds(30); - this.debuggerEndpoint = endpointAddress; - if (endpointAddress.StartsWith("ws", StringComparison.InvariantCultureIgnoreCase)) - { - this.EndpointAddress = endpointAddress; - } - this.messageQueueMonitorTask = Task.Run(() => this.MonitorMessageQueue()); - } - - /// - /// Event raised when the DevToolsSession logs informational messages. - /// - public event EventHandler? LogMessage; - - /// - /// Event raised an event notification is received from the DevTools session. - /// - public event EventHandler? DevToolsEventReceived; - - /// - /// Gets or sets the time to wait for a command to complete. Default is 30 seconds. - /// - public TimeSpan CommandTimeout { get; set; } - - /// - /// Gets or sets the active session ID of the connection. - /// - public string? ActiveSessionId { get; private set; } - - /// - /// Gets the endpoint address of the session. - /// - public string? EndpointAddress { get; private set; } - - /// - /// Gets the version-independent domain implementation for this Developer Tools connection - /// - public DevToolsDomains Domains { get; private set; } = null!; // TODO handle nullability for this - - /// - /// Gets the version-specific implementation of domains for this DevTools session. - /// - /// - /// A object containing the version-specific DevTools Protocol domain implementations. - /// The version-specific DevTools Protocol domain implementation. - /// If the provided is not the right protocol version which is running. - public T GetVersionSpecificDomains() where T : DevToolsSessionDomains - { - if (this.Domains.VersionSpecificDomains is not T versionSpecificDomains) - { - string errorTemplate = "The type is invalid for conversion. You requested domains of type '{0}', but the version-specific domains for this session are '{1}'"; - string exceptionMessage = string.Format(CultureInfo.InvariantCulture, errorTemplate, typeof(T).ToString(), this.Domains.GetType().ToString()); - throw new InvalidOperationException(exceptionMessage); - } + private readonly string debuggerEndpoint; + private readonly TimeSpan openConnectionWaitTimeSpan = TimeSpan.FromSeconds(30); + private readonly TimeSpan closeConnectionWaitTimeSpan = TimeSpan.FromSeconds(2); - return versionSpecificDomains; + private bool isDisposed = false; + private string? attachedTargetId; + + private WebSocketConnection? connection; + private ConcurrentDictionary pendingCommands = new ConcurrentDictionary(); + private readonly BlockingCollection messageQueue = new BlockingCollection(); + private readonly Task messageQueueMonitorTask; + private long currentCommandId = 0; + private readonly DevToolsOptions options; + + private readonly static ILogger logger = Internal.Logging.Log.GetLogger(); + + /// + /// Initializes a new instance of the DevToolsSession class, using the specified WebSocket endpoint. + /// + /// + [Obsolete("Use DevToolsSession(string endpointAddress, DevToolsOptions options)")] + public DevToolsSession(string endpointAddress) : this(endpointAddress, new DevToolsOptions()) { } + + /// + /// Initializes a new instance of the DevToolsSession class, using the specified WebSocket endpoint and specified DevTools options. + /// + /// + /// + /// If is , or is or . + public DevToolsSession(string endpointAddress, DevToolsOptions options) + { + if (string.IsNullOrWhiteSpace(endpointAddress)) + { + throw new ArgumentNullException(nameof(endpointAddress)); } - /// - /// Sends the specified command and returns the associated command response. - /// - /// A command object implementing the interface. - /// The command to be sent. - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - public async Task?> SendCommand(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) - where TCommand : ICommand + this.options = options ?? throw new ArgumentNullException(nameof(options)); + this.CommandTimeout = TimeSpan.FromSeconds(30); + this.debuggerEndpoint = endpointAddress; + if (endpointAddress.StartsWith("ws", StringComparison.InvariantCultureIgnoreCase)) { - if (command == null) - { - throw new ArgumentNullException(nameof(command)); - } + this.EndpointAddress = endpointAddress; + } + this.messageQueueMonitorTask = Task.Run(() => this.MonitorMessageQueue()); + } - JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); - var result = await SendCommand(command.CommandName, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); + /// + /// Event raised when the DevToolsSession logs informational messages. + /// + public event EventHandler? LogMessage; - if (result == null) - { - return null; - } + /// + /// Event raised an event notification is received from the DevTools session. + /// + public event EventHandler? DevToolsEventReceived; - if (!this.Domains.VersionSpecificDomains.ResponseTypeMap.TryGetCommandResponseType(out Type? commandResponseType)) - { - throw new InvalidOperationException($"Type {command.GetType()} does not correspond to a known command response type."); - } + /// + /// Gets or sets the time to wait for a command to complete. Default is 30 seconds. + /// + public TimeSpan CommandTimeout { get; set; } + + /// + /// Gets or sets the active session ID of the connection. + /// + public string? ActiveSessionId { get; private set; } + + /// + /// Gets the endpoint address of the session. + /// + public string? EndpointAddress { get; private set; } - return result.Value.Deserialize(commandResponseType) as ICommandResponse; + /// + /// Gets the version-independent domain implementation for this Developer Tools connection + /// + public DevToolsDomains Domains { get; private set; } = null!; // TODO handle nullability for this + + /// + /// Gets the version-specific implementation of domains for this DevTools session. + /// + /// + /// A object containing the version-specific DevTools Protocol domain implementations. + /// The version-specific DevTools Protocol domain implementation. + /// If the provided is not the right protocol version which is running. + public T GetVersionSpecificDomains() where T : DevToolsSessionDomains + { + if (this.Domains.VersionSpecificDomains is not T versionSpecificDomains) + { + string errorTemplate = "The type is invalid for conversion. You requested domains of type '{0}', but the version-specific domains for this session are '{1}'"; + string exceptionMessage = string.Format(CultureInfo.InvariantCulture, errorTemplate, typeof(T).ToString(), this.Domains.GetType().ToString()); + throw new InvalidOperationException(exceptionMessage); } - /// - /// Sends the specified command and returns the associated command response. - /// - /// A command object implementing the interface. - /// The command to be sent. - /// The target session of the command - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - public async Task?> SendCommand(TCommand command, string sessionId, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) - where TCommand : ICommand + return versionSpecificDomains; + } + + /// + /// Sends the specified command and returns the associated command response. + /// + /// A command object implementing the interface. + /// The command to be sent. + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + public async Task?> SendCommand(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + where TCommand : ICommand + { + if (command == null) { - if (command == null) - { - throw new ArgumentNullException(nameof(command)); - } + throw new ArgumentNullException(nameof(command)); + } - JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); - var result = await SendCommand(command.CommandName, sessionId, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); + JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); + var result = await SendCommand(command.CommandName, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); - if (result == null) - { - return null; - } + if (result == null) + { + return null; + } - if (!this.Domains.VersionSpecificDomains.ResponseTypeMap.TryGetCommandResponseType(command, out Type? commandResponseType)) - { - throw new InvalidOperationException($"Type {typeof(TCommand)} does not correspond to a known command response type."); - } + if (!this.Domains.VersionSpecificDomains.ResponseTypeMap.TryGetCommandResponseType(out Type? commandResponseType)) + { + throw new InvalidOperationException($"Type {command.GetType()} does not correspond to a known command response type."); + } - return result.Value.Deserialize(commandResponseType) as ICommandResponse; - } - - /// - /// Sends the specified command and returns the associated command response. - /// - /// A command object implementing the interface. - /// A response object implementing the interface. - /// The command to send. - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - public async Task SendCommand(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) - where TCommand : ICommand - where TCommandResponse : ICommandResponse - { - if (command == null) - { - throw new ArgumentNullException(nameof(command)); - } + return result.Value.Deserialize(commandResponseType) as ICommandResponse; + } - JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); - var result = await SendCommand(command.CommandName, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); + /// + /// Sends the specified command and returns the associated command response. + /// + /// A command object implementing the interface. + /// The command to be sent. + /// The target session of the command + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + public async Task?> SendCommand(TCommand command, string sessionId, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + where TCommand : ICommand + { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } - if (result == null) - { - return default; - } + JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); + var result = await SendCommand(command.CommandName, sessionId, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); + + if (result == null) + { + return null; + } - return result.Value.Deserialize(); + if (!this.Domains.VersionSpecificDomains.ResponseTypeMap.TryGetCommandResponseType(command, out Type? commandResponseType)) + { + throw new InvalidOperationException($"Type {typeof(TCommand)} does not correspond to a known command response type."); } - /// - /// Returns a JsonNode based on a command created with the specified command name and params. - /// - /// The name of the command to send. - /// The parameters of the command as a JsonNode object - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - public async Task SendCommand(string commandName, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + return result.Value.Deserialize(commandResponseType) as ICommandResponse; + } + + /// + /// Sends the specified command and returns the associated command response. + /// + /// A command object implementing the interface. + /// A response object implementing the interface. + /// The command to send. + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + public async Task SendCommand(TCommand command, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + where TCommand : ICommand + where TCommandResponse : ICommandResponse + { + if (command == null) { - if (this.attachedTargetId == null) - { - LogTrace("Session not currently attached to a target; reattaching"); - await this.InitializeSession().ConfigureAwait(false); - } + throw new ArgumentNullException(nameof(command)); + } - return await SendCommand(commandName, this.ActiveSessionId, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived); + JsonNode commandParameters = JsonSerializer.SerializeToNode(command) ?? throw new InvalidOperationException("Command serialized to \"null\"."); + var result = await SendCommand(command.CommandName, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived).ConfigureAwait(false); + + if (result == null) + { + return default; } - /// - /// Returns a JsonNode based on a command created with the specified command name and params. - /// - /// The name of the command to send. - /// The sessionId of the command. - /// The parameters of the command as a JsonNode object - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - public async Task SendCommand(string commandName, string? sessionId, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + return result.Value.Deserialize(); + } + + /// + /// Returns a JsonNode based on a command created with the specified command name and params. + /// + /// The name of the command to send. + /// The parameters of the command as a JsonNode object + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + public async Task SendCommand(string commandName, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + { + if (this.attachedTargetId == null) { - millisecondsTimeout ??= Convert.ToInt32(CommandTimeout.TotalMilliseconds); + LogTrace("Session not currently attached to a target; reattaching"); + await this.InitializeSession().ConfigureAwait(false); + } + + return await SendCommand(commandName, this.ActiveSessionId, commandParameters, cancellationToken, millisecondsTimeout, throwExceptionIfResponseNotReceived); + } - var message = new DevToolsCommandData(Interlocked.Increment(ref this.currentCommandId), sessionId, commandName, commandParameters); + /// + /// Returns a JsonNode based on a command created with the specified command name and params. + /// + /// The name of the command to send. + /// The sessionId of the command. + /// The parameters of the command as a JsonNode object + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + public async Task SendCommand(string commandName, string? sessionId, JsonNode commandParameters, CancellationToken cancellationToken = default, int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + { + millisecondsTimeout ??= Convert.ToInt32(CommandTimeout.TotalMilliseconds); - if (this.connection != null && this.connection.IsActive) + var message = new DevToolsCommandData(Interlocked.Increment(ref this.currentCommandId), sessionId, commandName, commandParameters); + + if (this.connection != null && this.connection.IsActive) + { + if (logger.IsEnabled(LogEventLevel.Trace)) { - if (logger.IsEnabled(LogEventLevel.Trace)) - { - logger.Trace($"CDP SND >> {message.CommandId} {message.CommandName}: {commandParameters.ToJsonString()}"); - } + logger.Trace($"CDP SND >> {message.CommandId} {message.CommandName}: {commandParameters.ToJsonString()}"); + } - LogTrace("Sending {0} {1}: {2}", message.CommandId, message.CommandName, commandParameters.ToString()); + LogTrace("Sending {0} {1}: {2}", message.CommandId, message.CommandName, commandParameters.ToString()); - string contents = JsonSerializer.Serialize(message); - this.pendingCommands.TryAdd(message.CommandId, message); - await this.connection.SendData(contents).ConfigureAwait(false); + string contents = JsonSerializer.Serialize(message); + this.pendingCommands.TryAdd(message.CommandId, message); + await this.connection.SendData(contents).ConfigureAwait(false); - var responseWasReceived = message.SyncEvent.Wait(millisecondsTimeout.Value, cancellationToken); + var responseWasReceived = message.SyncEvent.Wait(millisecondsTimeout.Value, cancellationToken); - if (!responseWasReceived && throwExceptionIfResponseNotReceived) - { - throw new InvalidOperationException($"A command response was not received: {commandName}, timeout: {millisecondsTimeout.Value}ms"); - } + if (!responseWasReceived && throwExceptionIfResponseNotReceived) + { + throw new InvalidOperationException($"A command response was not received: {commandName}, timeout: {millisecondsTimeout.Value}ms"); + } - if (this.pendingCommands.TryRemove(message.CommandId, out DevToolsCommandData? modified)) + if (this.pendingCommands.TryRemove(message.CommandId, out DevToolsCommandData? modified)) + { + if (modified.IsError) { - if (modified.IsError) + var errorMessage = modified.Result.GetProperty("message").GetString(); + var errorData = modified.Result.TryGetProperty("data", out var data) ? data.GetString() : null; + + var exceptionMessage = $"{commandName}: {errorMessage}"; + if (!string.IsNullOrWhiteSpace(errorData)) { - var errorMessage = modified.Result.GetProperty("message").GetString(); - var errorData = modified.Result.TryGetProperty("data", out var data) ? data.GetString() : null; - - var exceptionMessage = $"{commandName}: {errorMessage}"; - if (!string.IsNullOrWhiteSpace(errorData)) - { - exceptionMessage = $"{exceptionMessage} - {errorData}"; - } - - LogTrace("Received Error Response {0}: {1} {2}", modified.CommandId, message, errorData); - throw new CommandResponseException(exceptionMessage) - { - Code = modified.Result.TryGetProperty("code", out var code) ? code.GetInt64() : -1 - }; + exceptionMessage = $"{exceptionMessage} - {errorData}"; } - return modified.Result; + LogTrace("Received Error Response {0}: {1} {2}", modified.CommandId, message, errorData); + throw new CommandResponseException(exceptionMessage) + { + Code = modified.Result.TryGetProperty("code", out var code) ? code.GetInt64() : -1 + }; } - } - else - { - LogTrace("WebSocket is not connected; not sending {0}", message.CommandName); - } - return null; + return modified.Result; + } } - - /// - /// Releases all resources associated with this . - /// - public void Dispose() + else { - this.Dispose(true); - GC.SuppressFinalize(this); + LogTrace("WebSocket is not connected; not sending {0}", message.CommandName); } - /// - /// Asynchronously starts the session. - /// - /// A task that represents the asynchronous operation. - internal async Task StartSession() - { - int requestedProtocolVersion = options.ProtocolVersion ?? AutoDetectDevToolsProtocolVersion; - int protocolVersion = await InitializeProtocol(requestedProtocolVersion).ConfigureAwait(false); - this.Domains = DevToolsDomains.InitializeDomains(protocolVersion, this); + return null; + } - await this.InitializeSocketConnection().ConfigureAwait(false); - await this.InitializeSession().ConfigureAwait(false); - try - { - // Wrap this in a try-catch, because it's not the end of the - // world if clearing the log doesn't work. - await this.Domains.Log.Clear().ConfigureAwait(false); - LogTrace("Log cleared.", this.attachedTargetId); - } - catch (WebDriverException) - { - } + /// + /// Releases all resources associated with this . + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Asynchronously starts the session. + /// + /// A task that represents the asynchronous operation. + internal async Task StartSession() + { + int requestedProtocolVersion = options.ProtocolVersion ?? AutoDetectDevToolsProtocolVersion; + int protocolVersion = await InitializeProtocol(requestedProtocolVersion).ConfigureAwait(false); + this.Domains = DevToolsDomains.InitializeDomains(protocolVersion, this); + + await this.InitializeSocketConnection().ConfigureAwait(false); + await this.InitializeSession().ConfigureAwait(false); + try + { + // Wrap this in a try-catch, because it's not the end of the + // world if clearing the log doesn't work. + await this.Domains.Log.Clear().ConfigureAwait(false); + LogTrace("Log cleared.", this.attachedTargetId); + } + catch (WebDriverException) + { } + } - /// - /// Asynchronously stops the session. - /// - /// to manually detach the session - /// from its attached target; otherwise . - /// A task that represents the asynchronous operation. - internal async Task StopSession(bool manualDetach) + /// + /// Asynchronously stops the session. + /// + /// to manually detach the session + /// from its attached target; otherwise . + /// A task that represents the asynchronous operation. + internal async Task StopSession(bool manualDetach) + { + if (this.attachedTargetId != null) { - if (this.attachedTargetId != null) + this.Domains.Target.TargetDetached -= this.OnTargetDetached; + string? sessionId = this.ActiveSessionId; + this.ActiveSessionId = null; + if (manualDetach) { - this.Domains.Target.TargetDetached -= this.OnTargetDetached; - string? sessionId = this.ActiveSessionId; - this.ActiveSessionId = null; - if (manualDetach) - { - await this.Domains.Target.DetachFromTarget(sessionId, this.attachedTargetId).ConfigureAwait(false); - } - - this.attachedTargetId = null; + await this.Domains.Target.DetachFromTarget(sessionId, this.attachedTargetId).ConfigureAwait(false); } + + this.attachedTargetId = null; } + } - /// - /// Releases all resources associated with this . - /// - /// if the Dispose method was explicitly called; otherwise, . - protected void Dispose(bool disposing) + /// + /// Releases all resources associated with this . + /// + /// if the Dispose method was explicitly called; otherwise, . + protected void Dispose(bool disposing) + { + if (!this.isDisposed) { - if (!this.isDisposed) + if (disposing) { - if (disposing) - { - this.Domains.Target.TargetDetached -= this.OnTargetDetached; - this.pendingCommands.Clear(); - Task.Run(async () => await this.TerminateSocketConnection()).GetAwaiter().GetResult(); - } - - this.isDisposed = true; + this.Domains.Target.TargetDetached -= this.OnTargetDetached; + this.pendingCommands.Clear(); + Task.Run(async () => await this.TerminateSocketConnection()).GetAwaiter().GetResult(); } + + this.isDisposed = true; } + } - private async Task InitializeProtocol(int requestedProtocolVersion) + private async Task InitializeProtocol(int requestedProtocolVersion) + { + int protocolVersion = requestedProtocolVersion; + if (this.EndpointAddress == null) { - int protocolVersion = requestedProtocolVersion; - if (this.EndpointAddress == null) + string debuggerUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}/", this.debuggerEndpoint); + string rawVersionInfo; + using (HttpClient client = new HttpClient()) { - string debuggerUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}/", this.debuggerEndpoint); - string rawVersionInfo; - using (HttpClient client = new HttpClient()) - { - client.BaseAddress = new Uri(debuggerUrl); - rawVersionInfo = await client.GetStringAsync("/json/version").ConfigureAwait(false); - } + client.BaseAddress = new Uri(debuggerUrl); + rawVersionInfo = await client.GetStringAsync("/json/version").ConfigureAwait(false); + } - var versionInfo = JsonSerializer.Deserialize(rawVersionInfo) ?? throw new JsonException("/json/version endpoint returned null response"); - this.EndpointAddress = versionInfo.WebSocketDebuggerUrl; + var versionInfo = JsonSerializer.Deserialize(rawVersionInfo) ?? throw new JsonException("/json/version endpoint returned null response"); + this.EndpointAddress = versionInfo.WebSocketDebuggerUrl; - if (requestedProtocolVersion == AutoDetectDevToolsProtocolVersion) + if (requestedProtocolVersion == AutoDetectDevToolsProtocolVersion) + { + if (!int.TryParse(versionInfo.BrowserMajorVersion, out protocolVersion)) { - if (!int.TryParse(versionInfo.BrowserMajorVersion, out protocolVersion)) - { - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Unable to parse version number received from browser. Reported browser version string is '{0}'", versionInfo.Browser)); - } + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Unable to parse version number received from browser. Reported browser version string is '{0}'", versionInfo.Browser)); } } - else + } + else + { + if (protocolVersion == AutoDetectDevToolsProtocolVersion) { - if (protocolVersion == AutoDetectDevToolsProtocolVersion) - { - throw new WebDriverException("A WebSocket address for DevTools protocol has been detected, but the protocol version cannot be automatically detected. You must specify a protocol version."); - } + throw new WebDriverException("A WebSocket address for DevTools protocol has been detected, but the protocol version cannot be automatically detected. You must specify a protocol version."); } - - return protocolVersion; } - private async Task InitializeSession() + return protocolVersion; + } + + private async Task InitializeSession() + { + LogTrace("Creating session"); + if (this.attachedTargetId == null) { - LogTrace("Creating session"); - if (this.attachedTargetId == null) - { - // Set the attached target ID to a "pending connection" value - // (any non-null will do, so we choose the empty string), so - // that when getting the available targets, we won't - // recursively try to call InitializeSession. - this.attachedTargetId = ""; - var targets = await this.Domains.Target.GetTargets().ConfigureAwait(false); - foreach (var target in targets) + // Set the attached target ID to a "pending connection" value + // (any non-null will do, so we choose the empty string), so + // that when getting the available targets, we won't + // recursively try to call InitializeSession. + this.attachedTargetId = ""; + var targets = await this.Domains.Target.GetTargets().ConfigureAwait(false); + foreach (var target in targets) + { + if (target.Type == "page") { - if (target.Type == "page") - { - this.attachedTargetId = target.TargetId; - LogTrace("Found Target ID {0}.", this.attachedTargetId); - break; - } + this.attachedTargetId = target.TargetId; + LogTrace("Found Target ID {0}.", this.attachedTargetId); + break; } } + } - if (this.attachedTargetId == "") - { - this.attachedTargetId = null; - throw new WebDriverException("Unable to find target to attach to, no targets of type 'page' available"); - } - - string sessionId = await this.Domains.Target.AttachToTarget(this.attachedTargetId).ConfigureAwait(false); - LogTrace("Target ID {0} attached. Active session ID: {1}", this.attachedTargetId, sessionId); - this.ActiveSessionId = sessionId; + if (this.attachedTargetId == "") + { + this.attachedTargetId = null; + throw new WebDriverException("Unable to find target to attach to, no targets of type 'page' available"); + } - await this.Domains.Target.SetAutoAttach().ConfigureAwait(false); - LogTrace("AutoAttach is set.", this.attachedTargetId); + string sessionId = await this.Domains.Target.AttachToTarget(this.attachedTargetId).ConfigureAwait(false); + LogTrace("Target ID {0} attached. Active session ID: {1}", this.attachedTargetId, sessionId); + this.ActiveSessionId = sessionId; - // The Target domain needs to send Session-less commands! Else the waitForDebugger setting in setAutoAttach wont work! - if (options.WaitForDebuggerOnStart) - { - var setAutoAttachCommand = Domains.Target.CreateSetAutoAttachCommand(true); - var setDiscoverTargetsCommand = Domains.Target.CreateDiscoverTargetsCommand(); + await this.Domains.Target.SetAutoAttach().ConfigureAwait(false); + LogTrace("AutoAttach is set.", this.attachedTargetId); - await SendCommand(setAutoAttachCommand, string.Empty, default, null, true).ConfigureAwait(false); - await SendCommand(setDiscoverTargetsCommand, string.Empty, default, null, true).ConfigureAwait(false); - } + // The Target domain needs to send Session-less commands! Else the waitForDebugger setting in setAutoAttach wont work! + if (options.WaitForDebuggerOnStart) + { + var setAutoAttachCommand = Domains.Target.CreateSetAutoAttachCommand(true); + var setDiscoverTargetsCommand = Domains.Target.CreateDiscoverTargetsCommand(); - this.Domains.Target.TargetDetached += this.OnTargetDetached; + await SendCommand(setAutoAttachCommand, string.Empty, default, null, true).ConfigureAwait(false); + await SendCommand(setDiscoverTargetsCommand, string.Empty, default, null, true).ConfigureAwait(false); } - private void OnTargetDetached(object? sender, TargetDetachedEventArgs e) + this.Domains.Target.TargetDetached += this.OnTargetDetached; + } + + private void OnTargetDetached(object? sender, TargetDetachedEventArgs e) + { + if (e.SessionId == this.ActiveSessionId && e.TargetId == this.attachedTargetId) { - if (e.SessionId == this.ActiveSessionId && e.TargetId == this.attachedTargetId) - { - Task.Run(async () => await this.StopSession(false)).GetAwaiter().GetResult(); - } + Task.Run(async () => await this.StopSession(false)).GetAwaiter().GetResult(); } + } - private async Task InitializeSocketConnection() + private async Task InitializeSocketConnection() + { + LogTrace("Creating WebSocket"); + this.connection = new WebSocketConnection(this.openConnectionWaitTimeSpan, this.closeConnectionWaitTimeSpan); + connection.DataReceived += OnConnectionDataReceived; + await connection.Start(this.EndpointAddress!).ConfigureAwait(false); + LogTrace("WebSocket created"); + } + + private async Task TerminateSocketConnection() + { + LogTrace("Closing WebSocket"); + if (this.connection != null && this.connection.IsActive) { - LogTrace("Creating WebSocket"); - this.connection = new WebSocketConnection(this.openConnectionWaitTimeSpan, this.closeConnectionWaitTimeSpan); - connection.DataReceived += OnConnectionDataReceived; - await connection.Start(this.EndpointAddress!).ConfigureAwait(false); - LogTrace("WebSocket created"); + await this.connection.Stop().ConfigureAwait(false); + await this.ShutdownMessageQueue(this.connection).ConfigureAwait(false); } + LogTrace("WebSocket closed"); + } - private async Task TerminateSocketConnection() + private async Task ShutdownMessageQueue(WebSocketConnection connection) + { + // THe WebSocket connection is always closed before this method + // is called, so there will eventually be no more data written + // into the message queue, meaning this loop should be guaranteed + // to complete. + while (connection.IsActive) { - LogTrace("Closing WebSocket"); - if (this.connection != null && this.connection.IsActive) - { - await this.connection.Stop().ConfigureAwait(false); - await this.ShutdownMessageQueue(this.connection).ConfigureAwait(false); - } - LogTrace("WebSocket closed"); + await Task.Delay(TimeSpan.FromMilliseconds(10)); } - private async Task ShutdownMessageQueue(WebSocketConnection connection) + this.messageQueue.CompleteAdding(); + await this.messageQueueMonitorTask.ConfigureAwait(false); + } + + private void MonitorMessageQueue() + { + // GetConsumingEnumerable blocks until if BlockingCollection.IsCompleted + // is false (i.e., is still able to be written to), and there are no items + // in the collection. Once any items are added to the collection, the method + // unblocks and we can process any items in the collection at that moment. + // Once IsCompleted is true, the method unblocks with no items in returned + // in the IEnumerable, meaning the foreach loop will terminate gracefully. + foreach (string message in this.messageQueue.GetConsumingEnumerable()) { - // THe WebSocket connection is always closed before this method - // is called, so there will eventually be no more data written - // into the message queue, meaning this loop should be guaranteed - // to complete. - while (connection.IsActive) + // Don't break entire thread in case of unsuccessful message, + // and give a chance for the next message in queue to be processed + try { - await Task.Delay(TimeSpan.FromMilliseconds(10)); + this.ProcessMessage(message); } - - this.messageQueue.CompleteAdding(); - await this.messageQueueMonitorTask.ConfigureAwait(false); - } - - private void MonitorMessageQueue() - { - // GetConsumingEnumerable blocks until if BlockingCollection.IsCompleted - // is false (i.e., is still able to be written to), and there are no items - // in the collection. Once any items are added to the collection, the method - // unblocks and we can process any items in the collection at that moment. - // Once IsCompleted is true, the method unblocks with no items in returned - // in the IEnumerable, meaning the foreach loop will terminate gracefully. - foreach (string message in this.messageQueue.GetConsumingEnumerable()) + catch (Exception ex) { - // Don't break entire thread in case of unsuccessful message, - // and give a chance for the next message in queue to be processed - try + if (logger.IsEnabled(LogEventLevel.Error)) { - this.ProcessMessage(message); + logger.Error($"Unexpected error occurred while processing message: {ex}"); } - catch (Exception ex) - { - if (logger.IsEnabled(LogEventLevel.Error)) - { - logger.Error($"Unexpected error occurred while processing message: {ex}"); - } - LogError("Unexpected error occurred while processing message: {0}", ex); - } + LogError("Unexpected error occurred while processing message: {0}", ex); } } + } - private void ProcessMessage(string message) + private void ProcessMessage(string message) + { + if (logger.IsEnabled(LogEventLevel.Trace)) { - if (logger.IsEnabled(LogEventLevel.Trace)) - { - logger.Trace($"CDP RCV << {message}"); - } + logger.Trace($"CDP RCV << {message}"); + } - JsonElement messageObject; - using (var doc = JsonDocument.Parse(message)) - { - messageObject = doc.RootElement.Clone(); - } + JsonElement messageObject; + using (var doc = JsonDocument.Parse(message)) + { + messageObject = doc.RootElement.Clone(); + } - if (messageObject.TryGetProperty("id", out var idProperty)) - { - long commandId = idProperty.GetInt64(); + if (messageObject.TryGetProperty("id", out var idProperty)) + { + long commandId = idProperty.GetInt64(); - if (this.pendingCommands.TryGetValue(commandId, out DevToolsCommandData? commandInfo)) + if (this.pendingCommands.TryGetValue(commandId, out DevToolsCommandData? commandInfo)) + { + if (messageObject.TryGetProperty("error", out var errorProperty)) { - if (messageObject.TryGetProperty("error", out var errorProperty)) - { - commandInfo.IsError = true; - commandInfo.Result = errorProperty; - } - else - { - commandInfo.Result = messageObject.GetProperty("result"); - LogTrace("Received Response {0}: {1}", commandId, commandInfo.Result.ToString()); - } - - commandInfo.SyncEvent.Set(); + commandInfo.IsError = true; + commandInfo.Result = errorProperty; } else { - if (logger.IsEnabled(LogEventLevel.Error)) - { - logger.Error($"Received Unknown Response {commandId}: {message}"); - } - - LogError("Received Unknown Response {0}: {1}", commandId, message); + commandInfo.Result = messageObject.GetProperty("result"); + LogTrace("Received Response {0}: {1}", commandId, commandInfo.Result.ToString()); } - return; + commandInfo.SyncEvent.Set(); } - - if (messageObject.TryGetProperty("method", out var methodProperty)) + else { - var method = methodProperty.GetString() ?? throw new InvalidOperationException("CDP message contained \"method\" property with a value of \"null\"."); - var methodParts = method.Split(new char[] { '.' }, 2); - var eventData = messageObject.GetProperty("params"); - - LogTrace("Received Event {0}: {1}", method, eventData.ToString()); - - // Dispatch the event on a new thread so that any event handlers - // responding to the event will not block this thread from processing - // DevTools commands that may be sent in the body of the attached - // event handler. If thread pool starvation seems to become a problem, - // we can switch to a channel-based queue. - Task.Run(() => + if (logger.IsEnabled(LogEventLevel.Error)) { - try - { - OnDevToolsEventReceived(new DevToolsEventReceivedEventArgs(methodParts[0], methodParts[1], eventData)); - } - catch (Exception ex) - { - if (logger.IsEnabled(LogEventLevel.Warn)) - { - logger.Warn($"CDP VNT ^^ Unhandled error occurred in event handler of '{method}' method. {ex}"); - } - - throw; - } - }); + logger.Error($"Received Unknown Response {commandId}: {message}"); + } - return; + LogError("Received Unknown Response {0}: {1}", commandId, message); } - LogTrace("Received Other: {0}", message); + return; } - private void OnDevToolsEventReceived(DevToolsEventReceivedEventArgs e) + if (messageObject.TryGetProperty("method", out var methodProperty)) { - if (DevToolsEventReceived != null) + var method = methodProperty.GetString() ?? throw new InvalidOperationException("CDP message contained \"method\" property with a value of \"null\"."); + var methodParts = method.Split(new char[] { '.' }, 2); + var eventData = messageObject.GetProperty("params"); + + LogTrace("Received Event {0}: {1}", method, eventData.ToString()); + + // Dispatch the event on a new thread so that any event handlers + // responding to the event will not block this thread from processing + // DevTools commands that may be sent in the body of the attached + // event handler. If thread pool starvation seems to become a problem, + // we can switch to a channel-based queue. + Task.Run(() => { - DevToolsEventReceived(this, e); - } + try + { + OnDevToolsEventReceived(new DevToolsEventReceivedEventArgs(methodParts[0], methodParts[1], eventData)); + } + catch (Exception ex) + { + if (logger.IsEnabled(LogEventLevel.Warn)) + { + logger.Warn($"CDP VNT ^^ Unhandled error occurred in event handler of '{method}' method. {ex}"); + } + + throw; + } + }); + + return; } - private void OnConnectionDataReceived(object? sender, WebSocketConnectionDataReceivedEventArgs e) + LogTrace("Received Other: {0}", message); + } + + private void OnDevToolsEventReceived(DevToolsEventReceivedEventArgs e) + { + if (DevToolsEventReceived != null) { - this.messageQueue.Add(e.Data); + DevToolsEventReceived(this, e); } + } + + private void OnConnectionDataReceived(object? sender, WebSocketConnectionDataReceivedEventArgs e) + { + this.messageQueue.Add(e.Data); + } - private void LogTrace(string message, params object?[] args) + private void LogTrace(string message, params object?[] args) + { + if (LogMessage != null) { - if (LogMessage != null) - { - LogMessage(this, new DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel.Trace, message, args)); - } + LogMessage(this, new DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel.Trace, message, args)); } + } - private void LogError(string message, params object?[] args) + private void LogError(string message, params object?[] args) + { + if (LogMessage != null) { - if (LogMessage != null) - { - LogMessage(this, new DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel.Error, message, args)); - } + LogMessage(this, new DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel.Error, message, args)); } } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsSessionDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsSessionDomains.cs index c05a20488da16..cd483d3949b0d 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSessionDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSessionDomains.cs @@ -17,29 +17,28 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides an abstract base class for version-specific domain implementations. +/// +public abstract class DevToolsSessionDomains { /// - /// Provides an abstract base class for version-specific domain implementations. + /// Initializes a new instance of the class. /// - public abstract class DevToolsSessionDomains + protected DevToolsSessionDomains() { - /// - /// Initializes a new instance of the class. - /// - protected DevToolsSessionDomains() - { - this.PopulateCommandResponseTypeMap(); - } + this.PopulateCommandResponseTypeMap(); + } - /// - /// Gets the containing information about the types returned by DevTools Protocol commands., - /// - internal CommandResponseTypeMap ResponseTypeMap { get; } = new CommandResponseTypeMap(); + /// + /// Gets the containing information about the types returned by DevTools Protocol commands., + /// + internal CommandResponseTypeMap ResponseTypeMap { get; } = new CommandResponseTypeMap(); - /// - /// Populates the command response type map. - /// - protected abstract void PopulateCommandResponseTypeMap(); - } + /// + /// Populates the command response type map. + /// + protected abstract void PopulateCommandResponseTypeMap(); } diff --git a/dotnet/src/webdriver/DevTools/DevToolsSessionEventReceivedEventArgs.cs b/dotnet/src/webdriver/DevTools/DevToolsSessionEventReceivedEventArgs.cs index 849bf9643b5a5..24d5cdaf415a9 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSessionEventReceivedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSessionEventReceivedEventArgs.cs @@ -21,39 +21,38 @@ using System.Text.Json; using System.Text.Json.Nodes; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event data used when receiving events from the DevTools session. +/// +public class DevToolsEventReceivedEventArgs : EventArgs { /// - /// Event data used when receiving events from the DevTools session. + /// Initializes a new instance of the DevToolsEventReceivedEventArgs class. /// - public class DevToolsEventReceivedEventArgs : EventArgs + /// The domain on which the event is to be raised. + /// The name of the event to be raised. + /// The data for the event to be raised. + public DevToolsEventReceivedEventArgs(string domainName, string eventName, JsonElement eventData) { - /// - /// Initializes a new instance of the DevToolsEventReceivedEventArgs class. - /// - /// The domain on which the event is to be raised. - /// The name of the event to be raised. - /// The data for the event to be raised. - public DevToolsEventReceivedEventArgs(string domainName, string eventName, JsonElement eventData) - { - DomainName = domainName; - EventName = eventName; - EventData = eventData; - } + DomainName = domainName; + EventName = eventName; + EventData = eventData; + } - /// - /// Gets the domain on which the event is to be raised. - /// - public string DomainName { get; } + /// + /// Gets the domain on which the event is to be raised. + /// + public string DomainName { get; } - /// - /// Gets the name of the event to be raised. - /// - public string EventName { get; } + /// + /// Gets the name of the event to be raised. + /// + public string EventName { get; } - /// - /// Gets the data with which the event is to be raised. - /// - public JsonElement EventData { get; } - } + /// + /// Gets the data with which the event is to be raised. + /// + public JsonElement EventData { get; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsSessionLogMessageEventArgs.cs b/dotnet/src/webdriver/DevTools/DevToolsSessionLogMessageEventArgs.cs index a8d881a156624..c34abe64ccdce 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSessionLogMessageEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSessionLogMessageEventArgs.cs @@ -19,49 +19,48 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// The level of the log data emitted. +/// +public enum DevToolsSessionLogLevel { /// - /// The level of the log data emitted. + /// Log at the trace level. /// - public enum DevToolsSessionLogLevel - { - /// - /// Log at the trace level. - /// - Trace, + Trace, - /// - /// Log at the error level. - /// - Error - } + /// + /// Log at the error level. + /// + Error +} +/// +/// Represents the data used when the DevToolsSession object emits log data. +/// +public class DevToolsSessionLogMessageEventArgs : EventArgs +{ /// - /// Represents the data used when the DevToolsSession object emits log data. + /// Initializes a new instance of the DevToolsSessionLogMessageEventArgs class. /// - public class DevToolsSessionLogMessageEventArgs : EventArgs + /// The level of the log message. + /// The content of the log message. + /// Arguments to be substituted when the message is formatted. + public DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel level, string message, params object?[] args) { - /// - /// Initializes a new instance of the DevToolsSessionLogMessageEventArgs class. - /// - /// The level of the log message. - /// The content of the log message. - /// Arguments to be substituted when the message is formatted. - public DevToolsSessionLogMessageEventArgs(DevToolsSessionLogLevel level, string message, params object?[] args) - { - Level = level; - Message = string.Format(message, args); - } + Level = level; + Message = string.Format(message, args); + } - /// - /// Gets the message content. - /// - public string Message { get; } + /// + /// Gets the message content. + /// + public string Message { get; } - /// - /// Gets the message log level. - /// - public DevToolsSessionLogLevel Level { get; } - } + /// + /// Gets the message log level. + /// + public DevToolsSessionLogLevel Level { get; } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsVersionInfo.cs b/dotnet/src/webdriver/DevTools/DevToolsVersionInfo.cs index 55055725b9f6d..e129a99faa5af 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsVersionInfo.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsVersionInfo.cs @@ -21,125 +21,124 @@ using System.Text.Json.Serialization; using System.Text.RegularExpressions; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides information about the version of DevTools components being automated. +/// +public class DevToolsVersionInfo { /// - /// Provides information about the version of DevTools components being automated. + /// Gets or sets the browser name, usually expressed as "Browser/Version" (e.g., "Chrome/86.0.0.1234". /// - public class DevToolsVersionInfo + [JsonPropertyName("Browser")] + [JsonInclude] + public string? Browser { get; internal set; } + + /// + /// Gets the browser version without the preceding browser name. + /// + [JsonIgnore] + public string BrowserVersion { - /// - /// Gets or sets the browser name, usually expressed as "Browser/Version" (e.g., "Chrome/86.0.0.1234". - /// - [JsonPropertyName("Browser")] - [JsonInclude] - public string? Browser { get; internal set; } - - /// - /// Gets the browser version without the preceding browser name. - /// - [JsonIgnore] - public string BrowserVersion + get { - get + if (Browser is null) { - if (Browser is null) - { - throw new InvalidOperationException("Browser value is null"); - } - - return Regex.Match(Browser, ".*/(.*)").Groups[1].Value; + throw new InvalidOperationException("Browser value is null"); } + + return Regex.Match(Browser, ".*/(.*)").Groups[1].Value; } + } - /// - /// Gets the browser major version number without the preceding browser name. - /// - [JsonIgnore] - public string BrowserMajorVersion + /// + /// Gets the browser major version number without the preceding browser name. + /// + [JsonIgnore] + public string BrowserMajorVersion + { + get { - get + if (Browser is null) { - if (Browser is null) - { - throw new InvalidOperationException("Browser value is null"); - } - - return Regex.Match(Browser, ".*/(\\d+)\\..*").Groups[1].Value; + throw new InvalidOperationException("Browser value is null"); } + + return Regex.Match(Browser, ".*/(\\d+)\\..*").Groups[1].Value; } + } + + /// + /// Gets the version of the Developer Tools Protocol. + /// + [JsonPropertyName("Protocol-Version")] + [JsonInclude] + public string? ProtocolVersion { get; internal set; } + + /// + /// Gets the user agent string. + /// + [JsonPropertyName("User-Agent")] + [JsonInclude] + public string? UserAgent { get; internal set; } - /// - /// Gets the version of the Developer Tools Protocol. - /// - [JsonPropertyName("Protocol-Version")] - [JsonInclude] - public string? ProtocolVersion { get; internal set; } - - /// - /// Gets the user agent string. - /// - [JsonPropertyName("User-Agent")] - [JsonInclude] - public string? UserAgent { get; internal set; } - - /// - /// Gets the version string for the V8 script engine in use by this version of the browser. - /// - [JsonPropertyName("V8-Version")] - [JsonInclude] - public string? V8Version { get; internal set; } - - /// - /// Gets the URL for the WebSocket connection used for communicating via the DevTools Protocol. - /// - [JsonPropertyName("webSocketDebuggerUrl")] - [JsonInclude] - public string? WebSocketDebuggerUrl { get; internal set; } - - /// - /// Gets the version number of the V8 script engine, stripping values other than the version number. - /// - [JsonIgnore] - public string V8VersionNumber + /// + /// Gets the version string for the V8 script engine in use by this version of the browser. + /// + [JsonPropertyName("V8-Version")] + [JsonInclude] + public string? V8Version { get; internal set; } + + /// + /// Gets the URL for the WebSocket connection used for communicating via the DevTools Protocol. + /// + [JsonPropertyName("webSocketDebuggerUrl")] + [JsonInclude] + public string? WebSocketDebuggerUrl { get; internal set; } + + /// + /// Gets the version number of the V8 script engine, stripping values other than the version number. + /// + [JsonIgnore] + public string V8VersionNumber + { + get { - get + //Get the v8 version + Match? v8VersionMatch = V8Version is null ? null : Regex.Match(V8Version, @"^(\d+)\.(\d+)\.(\d+)(\.\d+.*)?"); + if (v8VersionMatch is null || v8VersionMatch.Success == false || v8VersionMatch.Groups.Count < 4) { - //Get the v8 version - Match? v8VersionMatch = V8Version is null ? null : Regex.Match(V8Version, @"^(\d+)\.(\d+)\.(\d+)(\.\d+.*)?"); - if (v8VersionMatch is null || v8VersionMatch.Success == false || v8VersionMatch.Groups.Count < 4) - { - throw new InvalidOperationException($"Unable to determine v8 version number from v8 version string ({V8Version})"); - } - - return $"{v8VersionMatch.Groups[1].Value}.{v8VersionMatch.Groups[2].Value}.{v8VersionMatch.Groups[3].Value}"; + throw new InvalidOperationException($"Unable to determine v8 version number from v8 version string ({V8Version})"); } + + return $"{v8VersionMatch.Groups[1].Value}.{v8VersionMatch.Groups[2].Value}.{v8VersionMatch.Groups[3].Value}"; } + } + + /// + /// Gets the version string for the version of WebKit used to build this version of the browser. + /// + [JsonPropertyName("WebKit-Version")] + [JsonInclude] + public string? WebKitVersion { get; internal set; } - /// - /// Gets the version string for the version of WebKit used to build this version of the browser. - /// - [JsonPropertyName("WebKit-Version")] - [JsonInclude] - public string? WebKitVersion { get; internal set; } - - /// - /// Gets the hash of the version of WebKit, stripping values other than the hash. - /// - [JsonIgnore] - public string WebKitVersionHash + /// + /// Gets the hash of the version of WebKit, stripping values other than the hash. + /// + [JsonIgnore] + public string WebKitVersionHash + { + get { - get + //Get the webkit version hash. + var webkitVersionMatch = WebKitVersion is null ? null : Regex.Match(WebKitVersion, @"\s\(@(\b[0-9a-f]{5,40}\b)"); + if (webkitVersionMatch is null || webkitVersionMatch.Success == false || webkitVersionMatch.Groups.Count != 2) { - //Get the webkit version hash. - var webkitVersionMatch = WebKitVersion is null ? null : Regex.Match(WebKitVersion, @"\s\(@(\b[0-9a-f]{5,40}\b)"); - if (webkitVersionMatch is null || webkitVersionMatch.Success == false || webkitVersionMatch.Groups.Count != 2) - { - throw new InvalidOperationException($"Unable to determine webkit version hash from webkit version string ({WebKitVersion})"); - } - - return webkitVersionMatch.Groups[1].Value; + throw new InvalidOperationException($"Unable to determine webkit version hash from webkit version string ({WebKitVersion})"); } + + return webkitVersionMatch.Groups[1].Value; } } } diff --git a/dotnet/src/webdriver/DevTools/EntryAddedEventArgs.cs b/dotnet/src/webdriver/DevTools/EntryAddedEventArgs.cs index 60df52864a714..473af9b2d164a 100644 --- a/dotnet/src/webdriver/DevTools/EntryAddedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/EntryAddedEventArgs.cs @@ -19,26 +19,25 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides data for events relating to entries being added to the browser's log. +/// +public class EntryAddedEventArgs : EventArgs { /// - /// Provides data for events relating to entries being added to the browser's log. + /// Initializes a new instance of the type. /// - public class EntryAddedEventArgs : EventArgs + /// The entry added to the browser's log. + /// If + public EntryAddedEventArgs(LogEntry entry) { - /// - /// Initializes a new instance of the type. - /// - /// The entry added to the browser's log. - /// If - public EntryAddedEventArgs(LogEntry entry) - { - Entry = entry ?? throw new ArgumentNullException(nameof(entry)); - } - - /// - /// The entry added to the browser's log. - /// - public LogEntry Entry { get; } + Entry = entry ?? throw new ArgumentNullException(nameof(entry)); } + + /// + /// The entry added to the browser's log. + /// + public LogEntry Entry { get; } } diff --git a/dotnet/src/webdriver/DevTools/ExceptionThrownEventArgs.cs b/dotnet/src/webdriver/DevTools/ExceptionThrownEventArgs.cs index 554625ea88d90..57a12bcb1a8e8 100644 --- a/dotnet/src/webdriver/DevTools/ExceptionThrownEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/ExceptionThrownEventArgs.cs @@ -19,32 +19,31 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Provides data for events relating to JavaScript exception handling. +/// +public class ExceptionThrownEventArgs : EventArgs { /// - /// Provides data for events relating to JavaScript exception handling. + /// Initializes new instance of the type. /// - public class ExceptionThrownEventArgs : EventArgs + /// The time stamp of the exception. + /// The text of the exception. + public ExceptionThrownEventArgs(DateTime timestamp, string message) { - /// - /// Initializes new instance of the type. - /// - /// The time stamp of the exception. - /// The text of the exception. - public ExceptionThrownEventArgs(DateTime timestamp, string message) - { - Timestamp = timestamp; - Message = message; - } + Timestamp = timestamp; + Message = message; + } - /// - /// Gets the time stamp of the exception. - /// - public DateTime Timestamp { get; } + /// + /// Gets the time stamp of the exception. + /// + public DateTime Timestamp { get; } - /// - /// Gets the text of the exception. - /// - public string Message { get; } - } + /// + /// Gets the text of the exception. + /// + public string Message { get; } } diff --git a/dotnet/src/webdriver/DevTools/ICommand.cs b/dotnet/src/webdriver/DevTools/ICommand.cs index 0c30598fceea6..a66b8ab99f9bb 100644 --- a/dotnet/src/webdriver/DevTools/ICommand.cs +++ b/dotnet/src/webdriver/DevTools/ICommand.cs @@ -17,27 +17,26 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents a command used by the DevTools Remote Interface +/// +public interface ICommand { /// - /// Represents a command used by the DevTools Remote Interface - /// - public interface ICommand - { - /// - /// Gets the name of the command. - /// - string CommandName { get; } - } + /// Gets the name of the command. + /// + string CommandName { get; } +} - /// - /// Represents a response to a command submitted by the DevTools Remote Interface - /// - public interface ICommandResponse; +/// +/// Represents a response to a command submitted by the DevTools Remote Interface +/// +public interface ICommandResponse; - /// - /// Represents a response to a command submitted by the DevTools Remote Interface - /// - public interface ICommandResponse : ICommandResponse - where T : ICommand; -} +/// +/// Represents a response to a command submitted by the DevTools Remote Interface +/// +public interface ICommandResponse : ICommandResponse + where T : ICommand; diff --git a/dotnet/src/webdriver/DevTools/IDevTools.cs b/dotnet/src/webdriver/DevTools/IDevTools.cs index 2a6fbed3a9a05..7652e2ca71b1b 100644 --- a/dotnet/src/webdriver/DevTools/IDevTools.cs +++ b/dotnet/src/webdriver/DevTools/IDevTools.cs @@ -20,51 +20,50 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Interface indicating the driver supports the Chrome DevTools Protocol. +/// +public interface IDevTools { /// - /// Interface indicating the driver supports the Chrome DevTools Protocol. + /// Gets a value indicating whether a DevTools session is active. /// - public interface IDevTools - { - /// - /// Gets a value indicating whether a DevTools session is active. - /// - bool HasActiveDevToolsSession { get; } + bool HasActiveDevToolsSession { get; } - /// - /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. - /// - /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - DevToolsSession GetDevToolsSession(); + /// + /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. + /// + /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + DevToolsSession GetDevToolsSession(); - /// - /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. - /// - /// The options for the DevToolsSession to use. - /// The active session to use to communicate with the Developer Tools debugging protocol. - /// If is . - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - DevToolsSession GetDevToolsSession(DevToolsOptions options); + /// + /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. + /// + /// The options for the DevToolsSession to use. + /// The active session to use to communicate with the Developer Tools debugging protocol. + /// If is . + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + DevToolsSession GetDevToolsSession(DevToolsOptions options); - /// - /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. - /// - /// The specific version of the Developer Tools debugging protocol to use. - /// The active session to use to communicate with the Developer Tools debugging protocol. - [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - DevToolsSession GetDevToolsSession(int protocolVersion); + /// + /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. + /// + /// The specific version of the Developer Tools debugging protocol to use. + /// The active session to use to communicate with the Developer Tools debugging protocol. + [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + DevToolsSession GetDevToolsSession(int protocolVersion); - /// - /// Closes a DevTools session - /// - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - void CloseDevToolsSession(); - } + /// + /// Closes a DevTools session + /// + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + void CloseDevToolsSession(); } diff --git a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs index 2682f1b929c8f..ffbea13b28422 100644 --- a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs @@ -24,78 +24,77 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents a WebSocket connection to a running DevTools instance that can be used to send +/// commands and receive events. +/// +public interface IDevToolsSession : IDisposable { /// - /// Represents a WebSocket connection to a running DevTools instance that can be used to send - /// commands and receive events. - /// - public interface IDevToolsSession : IDisposable - { - /// - /// Event raised when the DevToolsSession logs informational messages. - /// - event EventHandler? LogMessage; + /// Event raised when the DevToolsSession logs informational messages. + /// + event EventHandler? LogMessage; - /// - /// Event raised an event notification is received from the DevTools session. - /// - event EventHandler? DevToolsEventReceived; + /// + /// Event raised an event notification is received from the DevTools session. + /// + event EventHandler? DevToolsEventReceived; - /// - /// Gets the domains that are valid for the specified version of Developer Tools connection. - /// - /// - /// A type specific to the version of Developer Tools with which to communicate. - /// - /// The version-specific domains for this Developer Tools connection. - /// If the provided is not the right protocol version which is running. - T GetVersionSpecificDomains() where T : DevToolsSessionDomains; + /// + /// Gets the domains that are valid for the specified version of Developer Tools connection. + /// + /// + /// A type specific to the version of Developer Tools with which to communicate. + /// + /// The version-specific domains for this Developer Tools connection. + /// If the provided is not the right protocol version which is running. + T GetVersionSpecificDomains() where T : DevToolsSessionDomains; - /// - /// Sends the specified command and returns the associated command response. - /// - /// A command object implementing the interface. - /// The command to be sent. - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - Task?> SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) - where TCommand : ICommand; + /// + /// Sends the specified command and returns the associated command response. + /// + /// A command object implementing the interface. + /// The command to be sent. + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + Task?> SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) + where TCommand : ICommand; - /// - /// Sends the specified command and returns the associated command response. - /// - /// A command object implementing the interface. - /// A response object implementing the interface. - /// The command to send. - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - Task SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) - where TCommand : ICommand - where TCommandResponse : ICommandResponse; + /// + /// Sends the specified command and returns the associated command response. + /// + /// A command object implementing the interface. + /// A response object implementing the interface. + /// The command to send. + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + Task SendCommand(TCommand command, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived) + where TCommand : ICommand + where TCommandResponse : ICommandResponse; - /// - /// Returns a JsonNode based on a command created with the specified command name and params. - /// - /// The name of the command to send. - /// The parameters of the command as a JsonNode object - /// A CancellationToken object to allow for cancellation of the command. - /// The execution timeout of the command in milliseconds. - /// to throw an exception if a response is not received; otherwise, . - /// The command response object implementing the interface. - /// If is . - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - Task SendCommand(string commandName, JsonNode @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived); - } + /// + /// Returns a JsonNode based on a command created with the specified command name and params. + /// + /// The name of the command to send. + /// The parameters of the command as a JsonNode object + /// A CancellationToken object to allow for cancellation of the command. + /// The execution timeout of the command in milliseconds. + /// to throw an exception if a response is not received; otherwise, . + /// The command response object implementing the interface. + /// If is . + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + Task SendCommand(string commandName, JsonNode @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived); } diff --git a/dotnet/src/webdriver/DevTools/JavaScript.cs b/dotnet/src/webdriver/DevTools/JavaScript.cs index 801ed039544e5..5ef23219b5563 100644 --- a/dotnet/src/webdriver/DevTools/JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/JavaScript.cs @@ -20,125 +20,124 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class representing the browser's JavaScript execution as referenced by the DevTools Protocol. +/// +public abstract class JavaScript { /// - /// Class representing the browser's JavaScript execution as referenced by the DevTools Protocol. + /// Occurs when a JavaScript script binding is called. + /// + public event EventHandler? BindingCalled; + + /// + /// Occurs when the browser's JavaScript console API is called. + /// + public event EventHandler? ConsoleApiCalled; + + /// + /// Occurs when a JavaScript exception is thrown. + /// + public event EventHandler? ExceptionThrown; + + /// + /// Asynchronously enables the Runtime domain in the DevTools Protocol. /// - public abstract class JavaScript + /// A task that represents the asynchronous operation. + public abstract Task EnableRuntime(); + + /// + /// Asynchronously disables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public abstract Task DisableRuntime(); + + /// + /// Asynchronously enables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public abstract Task EnablePage(); + + /// + /// Asynchronously disables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public abstract Task DisablePage(); + + /// + /// Adds a binding to a specific JavaScript name. + /// + /// The name to which to bind to. + /// A task that represents the asynchronous operation. + public abstract Task AddBinding(string name); + + /// + /// Removes a binding from a specific JavaScript name. + /// + /// The name to which to remove the bind from. + /// A task that represents the asynchronous operation. + public abstract Task RemoveBinding(string name); + + /// + /// Adds a JavaScript snippet to evaluate when a new document is opened. + /// + /// The script to add to be evaluated when a new document is opened. + /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. + public abstract Task AddScriptToEvaluateOnNewDocument(string script); + + /// + /// Removes a JavaScript snippet from evaluate when a new document is opened. + /// + /// The ID of the script to be removed. + /// A task that represents the asynchronous operation. + public abstract Task RemoveScriptToEvaluateOnNewDocument(string scriptId); + + /// + /// Evaluates a JavaScript snippet. It does not return a value. + /// + /// The script to evaluate + /// A task that represents the asynchronous operation. + /// + /// This method is internal to the operation of pinned scripts in Selenium, and + /// is therefore internal by design. + /// + internal abstract Task Evaluate(string script); + + /// + /// Raises the BindingCalled event. + /// + /// An that contains the event data. + protected virtual void OnBindingCalled(BindingCalledEventArgs e) { - /// - /// Occurs when a JavaScript script binding is called. - /// - public event EventHandler? BindingCalled; - - /// - /// Occurs when the browser's JavaScript console API is called. - /// - public event EventHandler? ConsoleApiCalled; - - /// - /// Occurs when a JavaScript exception is thrown. - /// - public event EventHandler? ExceptionThrown; - - /// - /// Asynchronously enables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public abstract Task EnableRuntime(); - - /// - /// Asynchronously disables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public abstract Task DisableRuntime(); - - /// - /// Asynchronously enables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public abstract Task EnablePage(); - - /// - /// Asynchronously disables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public abstract Task DisablePage(); - - /// - /// Adds a binding to a specific JavaScript name. - /// - /// The name to which to bind to. - /// A task that represents the asynchronous operation. - public abstract Task AddBinding(string name); - - /// - /// Removes a binding from a specific JavaScript name. - /// - /// The name to which to remove the bind from. - /// A task that represents the asynchronous operation. - public abstract Task RemoveBinding(string name); - - /// - /// Adds a JavaScript snippet to evaluate when a new document is opened. - /// - /// The script to add to be evaluated when a new document is opened. - /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. - public abstract Task AddScriptToEvaluateOnNewDocument(string script); - - /// - /// Removes a JavaScript snippet from evaluate when a new document is opened. - /// - /// The ID of the script to be removed. - /// A task that represents the asynchronous operation. - public abstract Task RemoveScriptToEvaluateOnNewDocument(string scriptId); - - /// - /// Evaluates a JavaScript snippet. It does not return a value. - /// - /// The script to evaluate - /// A task that represents the asynchronous operation. - /// - /// This method is internal to the operation of pinned scripts in Selenium, and - /// is therefore internal by design. - /// - internal abstract Task Evaluate(string script); - - /// - /// Raises the BindingCalled event. - /// - /// An that contains the event data. - protected virtual void OnBindingCalled(BindingCalledEventArgs e) + if (this.BindingCalled != null) { - if (this.BindingCalled != null) - { - this.BindingCalled(this, e); - } + this.BindingCalled(this, e); } + } - /// - /// Raises the ConsoleApiCalled event. - /// - /// An that contains the event data. - protected virtual void OnConsoleApiCalled(ConsoleApiCalledEventArgs e) + /// + /// Raises the ConsoleApiCalled event. + /// + /// An that contains the event data. + protected virtual void OnConsoleApiCalled(ConsoleApiCalledEventArgs e) + { + if (this.ConsoleApiCalled != null) { - if (this.ConsoleApiCalled != null) - { - this.ConsoleApiCalled(this, e); - } + this.ConsoleApiCalled(this, e); } + } - /// - /// Raises the ExceptionThrown event. - /// - /// An that contains the event data. - protected virtual void OnExceptionThrown(ExceptionThrownEventArgs e) + /// + /// Raises the ExceptionThrown event. + /// + /// An that contains the event data. + protected virtual void OnExceptionThrown(ExceptionThrownEventArgs e) + { + if (this.ExceptionThrown != null) { - if (this.ExceptionThrown != null) - { - this.ExceptionThrown(this, e); - } + this.ExceptionThrown(this, e); } } } diff --git a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs index 25cb36aa082a8..000379dc2b87f 100644 --- a/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs +++ b/dotnet/src/webdriver/DevTools/Json/JsonEnumMemberConverter.cs @@ -25,58 +25,57 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace OpenQA.Selenium.DevTools.Json +namespace OpenQA.Selenium.DevTools.Json; + +internal sealed class JsonEnumMemberConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> : JsonConverter + where TEnum : struct, Enum { - internal sealed class JsonEnumMemberConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> : JsonConverter - where TEnum : struct, Enum - { - private readonly Dictionary _enumToString = new Dictionary(); - private readonly Dictionary _stringToEnum = new Dictionary(); + private readonly Dictionary _enumToString = new Dictionary(); + private readonly Dictionary _stringToEnum = new Dictionary(); - public JsonEnumMemberConverter() - { - var type = typeof(TEnum); + public JsonEnumMemberConverter() + { + var type = typeof(TEnum); #if NET8_0_OR_GREATER - TEnum[] values = Enum.GetValues(); + TEnum[] values = Enum.GetValues(); #else - Array values = Enum.GetValues(type); + Array values = Enum.GetValues(type); #endif - foreach (var value in values) - { - var enumMember = type.GetField(value.ToString())!; - var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false) - .Cast() - .FirstOrDefault(); + foreach (var value in values) + { + var enumMember = type.GetField(value.ToString())!; + var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false) + .Cast() + .FirstOrDefault(); - _stringToEnum.Add(value.ToString(), (TEnum)value); + _stringToEnum.Add(value.ToString(), (TEnum)value); - if (attr?.Value != null) - { - _enumToString[(TEnum)value] = attr.Value; - _stringToEnum[attr.Value] = (TEnum)value; - } - else - { - _enumToString.Add((TEnum)value, value.ToString()); - } + if (attr?.Value != null) + { + _enumToString[(TEnum)value] = attr.Value; + _stringToEnum[attr.Value] = (TEnum)value; } - } - - public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var stringValue = reader.GetString() ?? throw new JsonException("Could not read an enum string from \"null\""); - - if (_stringToEnum.TryGetValue(stringValue, out var enumValue)) + else { - return enumValue; + _enumToString.Add((TEnum)value, value.ToString()); } - - return default; } + } + + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var stringValue = reader.GetString() ?? throw new JsonException("Could not read an enum string from \"null\""); - public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + if (_stringToEnum.TryGetValue(stringValue, out var enumValue)) { - writer.WriteStringValue(_enumToString[value]); + return enumValue; } + + return default; + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(_enumToString[value]); } } diff --git a/dotnet/src/webdriver/DevTools/Log.cs b/dotnet/src/webdriver/DevTools/Log.cs index 579b3f4de110d..2b0c5b2f20f4f 100644 --- a/dotnet/src/webdriver/DevTools/Log.cs +++ b/dotnet/src/webdriver/DevTools/Log.cs @@ -20,46 +20,45 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class representing the browser's log as referenced by the DevTools Protocol. +/// +public abstract class Log { /// - /// Class representing the browser's log as referenced by the DevTools Protocol. + /// Occurs when an entry is added to the browser's log. /// - public abstract class Log - { - /// - /// Occurs when an entry is added to the browser's log. - /// - public event EventHandler? EntryAdded; + public event EventHandler? EntryAdded; - /// - /// Asynchronously enables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public abstract Task Enable(); + /// + /// Asynchronously enables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public abstract Task Enable(); - /// - /// Asynchronously disables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public abstract Task Disable(); + /// + /// Asynchronously disables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public abstract Task Disable(); - /// - /// Asynchronously clears the browser's log. - /// - /// A task that represents the asynchronous operation. - public abstract Task Clear(); + /// + /// Asynchronously clears the browser's log. + /// + /// A task that represents the asynchronous operation. + public abstract Task Clear(); - /// - /// Raises the EntryAdded event. - /// - /// An that contains the event data. - protected virtual void OnEntryAdded(EntryAddedEventArgs e) + /// + /// Raises the EntryAdded event. + /// + /// An that contains the event data. + protected virtual void OnEntryAdded(EntryAddedEventArgs e) + { + if (this.EntryAdded != null) { - if (this.EntryAdded != null) - { - this.EntryAdded(this, e); - } + this.EntryAdded(this, e); } } } diff --git a/dotnet/src/webdriver/DevTools/LogEntry.cs b/dotnet/src/webdriver/DevTools/LogEntry.cs index f5120ac3a3cb5..9487637a2fe39 100644 --- a/dotnet/src/webdriver/DevTools/LogEntry.cs +++ b/dotnet/src/webdriver/DevTools/LogEntry.cs @@ -17,32 +17,31 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents information about a log entry when the browser console is written to. +/// +public class LogEntry { /// - /// Represents information about a log entry when the browser console is written to. + /// Initializes a new instance of the type. /// - public class LogEntry + /// The kind of message written to the log. + /// The text of the message written to the log. + public LogEntry(string kind, string message) { - /// - /// Initializes a new instance of the type. - /// - /// The kind of message written to the log. - /// The text of the message written to the log. - public LogEntry(string kind, string message) - { - Kind = kind; - Message = message; - } + Kind = kind; + Message = message; + } - /// - /// Gets the kind of message written to the log. - /// - public string Kind { get; } + /// + /// Gets the kind of message written to the log. + /// + public string Kind { get; } - /// - /// Gets the text of the message written to the log. - /// - public string Message { get; } - } + /// + /// Gets the text of the message written to the log. + /// + public string Message { get; } } diff --git a/dotnet/src/webdriver/DevTools/Network.cs b/dotnet/src/webdriver/DevTools/Network.cs index e59eb509f06c9..0313a20ba403a 100644 --- a/dotnet/src/webdriver/DevTools/Network.cs +++ b/dotnet/src/webdriver/DevTools/Network.cs @@ -21,198 +21,197 @@ using System.Linq; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class providing functionality for manipulating network calls using DevTools Protocol commands +/// +public abstract class Network { /// - /// Class providing functionality for manipulating network calls using DevTools Protocol commands + /// Occurs when a network request requires authorization. + /// + public event AsyncEventHandler? AuthRequired; + + /// + /// Occurs when a network request is intercepted. + /// + public event AsyncEventHandler? RequestPaused; + + /// + /// Occurs when a network response is received. + /// + public event AsyncEventHandler? ResponsePaused; + + /// + /// Asynchronously disables network caching. + /// + /// A task that represents the asynchronous operation. + public abstract Task DisableNetworkCaching(); + + /// + /// Asynchronously enables network caching. + /// + /// A task that represents the asynchronous operation. + public abstract Task EnableNetworkCaching(); + + /// + /// Asynchronously enables the network domain. + /// + /// A task that represents the asynchronous operation. + public abstract Task EnableNetwork(); + + /// + /// Asynchronously disables the fetch domain. /// - public abstract class Network + /// A task that represents the asynchronous operation. + public abstract Task DisableNetwork(); + + /// + /// Asynchronously enables the fetch domain for all URL patterns. + /// + /// A task that represents the asynchronous operation. + public abstract Task EnableFetchForAllPatterns(); + + /// + /// Asynchronously sets the override of the user agent string. + /// + /// The user agent string to set. + /// A task that represents the asynchronous operation. + public async Task SetUserAgentOverride(string userAgent) { - /// - /// Occurs when a network request requires authorization. - /// - public event AsyncEventHandler? AuthRequired; - - /// - /// Occurs when a network request is intercepted. - /// - public event AsyncEventHandler? RequestPaused; - - /// - /// Occurs when a network response is received. - /// - public event AsyncEventHandler? ResponsePaused; - - /// - /// Asynchronously disables network caching. - /// - /// A task that represents the asynchronous operation. - public abstract Task DisableNetworkCaching(); - - /// - /// Asynchronously enables network caching. - /// - /// A task that represents the asynchronous operation. - public abstract Task EnableNetworkCaching(); - - /// - /// Asynchronously enables the network domain. - /// - /// A task that represents the asynchronous operation. - public abstract Task EnableNetwork(); - - /// - /// Asynchronously disables the fetch domain. - /// - /// A task that represents the asynchronous operation. - public abstract Task DisableNetwork(); - - /// - /// Asynchronously enables the fetch domain for all URL patterns. - /// - /// A task that represents the asynchronous operation. - public abstract Task EnableFetchForAllPatterns(); - - /// - /// Asynchronously sets the override of the user agent string. - /// - /// The user agent string to set. - /// A task that represents the asynchronous operation. - public async Task SetUserAgentOverride(string userAgent) - { - await SetUserAgentOverride(new UserAgent(userAgent)).ConfigureAwait(false); - } + await SetUserAgentOverride(new UserAgent(userAgent)).ConfigureAwait(false); + } - /// - /// Asynchronously sets the override of the user agent settings. - /// - /// A object containing the user agent values to override. - /// A task that represents the asynchronous operation. - /// If is null. - public abstract Task SetUserAgentOverride(UserAgent userAgent); - - /// - /// Asynchronously disables the fetch domain. - /// - /// A task that represents the asynchronous operation. - public abstract Task DisableFetch(); - - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// A task that represents the asynchronous operation. - /// If is . - public abstract Task ContinueRequest(HttpRequestData requestData); - - /// - /// Asynchronously continues an intercepted network request and returns the specified response. - /// - /// The of the request. - /// The with which to respond to the request - /// A task that represents the asynchronous operation. - /// If or are . - public abstract Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData); - - /// - /// Asynchronously continues an intercepted network request without modification. - /// - /// The of the network request. - /// A task that represents the asynchronous operation. - /// If is . - public abstract Task ContinueRequestWithoutModification(HttpRequestData requestData); - - /// - /// Asynchronously continues an intercepted network call using authentication. - /// - /// The ID of the network request for which to continue with authentication. - /// The user name with which to authenticate. - /// The password with which to authenticate. - /// A task that represents the asynchronous operation. - public abstract Task ContinueWithAuth(string requestId, string? userName, string? password); - - /// - /// Asynchronously cancels authorization of an intercepted network request. - /// - /// The ID of the network request for which to cancel authentication. - /// A task that represents the asynchronous operation. - public abstract Task CancelAuth(string requestId); - - /// - /// Asynchronously adds the response body to the provided object. - /// - /// The object to which to add the response body. - /// A task that represents the asynchronous operation. - /// If is . - public abstract Task AddResponseBody(HttpResponseData responseData); - - /// - /// Asynchronously continues an intercepted network response without modification. - /// - /// The of the network response. - /// A task that represents the asynchronous operation. - /// If is . - public abstract Task ContinueResponseWithoutModification(HttpResponseData responseData); - - /// - /// Raises the AuthRequired event. - /// - /// An that contains the event data. - protected virtual void OnAuthRequired(AuthRequiredEventArgs e) - { - var delegates = AuthRequired?.GetInvocationList(); + /// + /// Asynchronously sets the override of the user agent settings. + /// + /// A object containing the user agent values to override. + /// A task that represents the asynchronous operation. + /// If is null. + public abstract Task SetUserAgentOverride(UserAgent userAgent); + + /// + /// Asynchronously disables the fetch domain. + /// + /// A task that represents the asynchronous operation. + public abstract Task DisableFetch(); + + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// A task that represents the asynchronous operation. + /// If is . + public abstract Task ContinueRequest(HttpRequestData requestData); + + /// + /// Asynchronously continues an intercepted network request and returns the specified response. + /// + /// The of the request. + /// The with which to respond to the request + /// A task that represents the asynchronous operation. + /// If or are . + public abstract Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData); + + /// + /// Asynchronously continues an intercepted network request without modification. + /// + /// The of the network request. + /// A task that represents the asynchronous operation. + /// If is . + public abstract Task ContinueRequestWithoutModification(HttpRequestData requestData); + + /// + /// Asynchronously continues an intercepted network call using authentication. + /// + /// The ID of the network request for which to continue with authentication. + /// The user name with which to authenticate. + /// The password with which to authenticate. + /// A task that represents the asynchronous operation. + public abstract Task ContinueWithAuth(string requestId, string? userName, string? password); + + /// + /// Asynchronously cancels authorization of an intercepted network request. + /// + /// The ID of the network request for which to cancel authentication. + /// A task that represents the asynchronous operation. + public abstract Task CancelAuth(string requestId); + + /// + /// Asynchronously adds the response body to the provided object. + /// + /// The object to which to add the response body. + /// A task that represents the asynchronous operation. + /// If is . + public abstract Task AddResponseBody(HttpResponseData responseData); + + /// + /// Asynchronously continues an intercepted network response without modification. + /// + /// The of the network response. + /// A task that represents the asynchronous operation. + /// If is . + public abstract Task ContinueResponseWithoutModification(HttpResponseData responseData); + + /// + /// Raises the AuthRequired event. + /// + /// An that contains the event data. + protected virtual void OnAuthRequired(AuthRequiredEventArgs e) + { + var delegates = AuthRequired?.GetInvocationList(); - if (delegates != null) + if (delegates != null) + { + foreach (var d in delegates.Cast>()) { - foreach (var d in delegates.Cast>()) - { - Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); - } + Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); } } + } - /// - /// Raises the RequestPaused event. - /// - /// An that contains the event data. - protected virtual void OnRequestPaused(RequestPausedEventArgs e) - { - var delegates = RequestPaused?.GetInvocationList(); + /// + /// Raises the RequestPaused event. + /// + /// An that contains the event data. + protected virtual void OnRequestPaused(RequestPausedEventArgs e) + { + var delegates = RequestPaused?.GetInvocationList(); - if (delegates != null) + if (delegates != null) + { + foreach (var d in delegates.Cast>()) { - foreach (var d in delegates.Cast>()) - { - Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); - } + Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); } } + } - /// - /// Raises the ResponseReceived event. - /// - /// An that contains the event data. - protected virtual void OnResponsePaused(ResponsePausedEventArgs e) - { - var delegates = ResponsePaused?.GetInvocationList(); + /// + /// Raises the ResponseReceived event. + /// + /// An that contains the event data. + protected virtual void OnResponsePaused(ResponsePausedEventArgs e) + { + var delegates = ResponsePaused?.GetInvocationList(); - if (delegates != null) + if (delegates != null) + { + foreach (var d in delegates.Cast>()) { - foreach (var d in delegates.Cast>()) - { - Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); - } + Task.Run(async () => await d.Invoke(this, e)).GetAwaiter().GetResult(); } } - - /// A task that represents the asynchronous operation. - /// - /// Am asynchronous delegate for handling network events. - /// - /// The type of event args the event raises. - /// The sender of the event. - /// An object containing information about the event. - /// A task that represents the asynchronous operation. - public delegate Task AsyncEventHandler(object sender, TEventArgs e); } + + /// A task that represents the asynchronous operation. + /// + /// Am asynchronous delegate for handling network events. + /// + /// The type of event args the event raises. + /// The sender of the event. + /// An object containing information about the event. + /// A task that represents the asynchronous operation. + public delegate Task AsyncEventHandler(object sender, TEventArgs e); } diff --git a/dotnet/src/webdriver/DevTools/RequestPausedEventArgs.cs b/dotnet/src/webdriver/DevTools/RequestPausedEventArgs.cs index 1df3aaaf6e2c9..34015829e8212 100644 --- a/dotnet/src/webdriver/DevTools/RequestPausedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/RequestPausedEventArgs.cs @@ -19,32 +19,31 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the RequestPaused event is raised. +/// +public class RequestPausedEventArgs : EventArgs { /// - /// Event arguments present when the RequestPaused event is raised. + /// Initializes a new instance of the type. /// - public class RequestPausedEventArgs : EventArgs + /// The request ID. + /// The object for this request. + public RequestPausedEventArgs(string? requestId, HttpRequestData requestData) { - /// - /// Initializes a new instance of the type. - /// - /// The request ID. - /// The object for this request. - public RequestPausedEventArgs(string? requestId, HttpRequestData requestData) - { - RequestId = requestId; - RequestData = requestData; - } + RequestId = requestId; + RequestData = requestData; + } - /// - /// Gets the request ID. - /// - public string? RequestId { get; } + /// + /// Gets the request ID. + /// + public string? RequestId { get; } - /// - /// Gets the object for this request. - /// - public HttpRequestData RequestData { get; } - } + /// + /// Gets the object for this request. + /// + public HttpRequestData RequestData { get; } } diff --git a/dotnet/src/webdriver/DevTools/ResponsePausedEventArgs.cs b/dotnet/src/webdriver/DevTools/ResponsePausedEventArgs.cs index 417a1e4b43680..9603141f8b368 100644 --- a/dotnet/src/webdriver/DevTools/ResponsePausedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/ResponsePausedEventArgs.cs @@ -19,26 +19,25 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the ResponseReceived event is raised. +/// +public class ResponsePausedEventArgs : EventArgs { /// - /// Event arguments present when the ResponseReceived event is raised. + /// Initializes a new instance of the type. /// - public class ResponsePausedEventArgs : EventArgs + /// The object for this request. + /// If is . + public ResponsePausedEventArgs(HttpResponseData responseData) { - /// - /// Initializes a new instance of the type. - /// - /// The object for this request. - /// If is . - public ResponsePausedEventArgs(HttpResponseData responseData) - { - ResponseData = responseData ?? throw new ArgumentNullException(nameof(responseData)); - } - - /// - /// Gets the object for this request. - /// - public HttpResponseData ResponseData { get; } + ResponseData = responseData ?? throw new ArgumentNullException(nameof(responseData)); } + + /// + /// Gets the object for this request. + /// + public HttpResponseData ResponseData { get; } } diff --git a/dotnet/src/webdriver/DevTools/Target.cs b/dotnet/src/webdriver/DevTools/Target.cs index 179879c30af6e..46cc617e09079 100644 --- a/dotnet/src/webdriver/DevTools/Target.cs +++ b/dotnet/src/webdriver/DevTools/Target.cs @@ -21,85 +21,84 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Class representing a target for DevTools Protocol commands +/// +public abstract class Target { /// - /// Class representing a target for DevTools Protocol commands + /// Occurs when a target is detached. /// - public abstract class Target - { - /// - /// Occurs when a target is detached. - /// - public event EventHandler? TargetDetached; + public event EventHandler? TargetDetached; - /// - /// Occurs when a target is attached. - /// - public event EventHandler? TargetAttached; + /// + /// Occurs when a target is attached. + /// + public event EventHandler? TargetAttached; - /// - /// Asynchronously gets the targets available for this session. - /// - /// - /// A task that represents the asynchronous operation. The task result - /// contains the list of objects describing the - /// targets available for this session. - /// - public abstract Task> GetTargets(object? settings = null); + /// + /// Asynchronously gets the targets available for this session. + /// + /// + /// A task that represents the asynchronous operation. The task result + /// contains the list of objects describing the + /// targets available for this session. + /// + public abstract Task> GetTargets(object? settings = null); - /// - /// Asynchronously attaches to a target. - /// - /// The ID of the target to which to attach. - /// - /// A task representing the asynchronous attach operation. The task result contains the - /// session ID established for commands to the target attached to. - /// - public abstract Task AttachToTarget(string targetId); + /// + /// Asynchronously attaches to a target. + /// + /// The ID of the target to which to attach. + /// + /// A task representing the asynchronous attach operation. The task result contains the + /// session ID established for commands to the target attached to. + /// + public abstract Task AttachToTarget(string targetId); - /// - /// Asynchronously detaches from a target. - /// - /// The ID of the session of the target from which to detach. - /// The ID of the target from which to detach. - /// - /// A task representing the asynchronous detach operation. - /// - public abstract Task DetachFromTarget(string? sessionId = null, string? targetId = null); + /// + /// Asynchronously detaches from a target. + /// + /// The ID of the session of the target from which to detach. + /// The ID of the target from which to detach. + /// + /// A task representing the asynchronous detach operation. + /// + public abstract Task DetachFromTarget(string? sessionId = null, string? targetId = null); - /// - /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. - /// - /// A task that represents the asynchronous operation. - public abstract Task SetAutoAttach(); + /// + /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. + /// + /// A task that represents the asynchronous operation. + public abstract Task SetAutoAttach(); - internal abstract ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart); + internal abstract ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart); - internal abstract ICommand CreateDiscoverTargetsCommand(); + internal abstract ICommand CreateDiscoverTargetsCommand(); - /// - /// Raises the TargetDetached event. - /// - /// An that contains the event data. - protected virtual void OnTargetDetached(TargetDetachedEventArgs e) + /// + /// Raises the TargetDetached event. + /// + /// An that contains the event data. + protected virtual void OnTargetDetached(TargetDetachedEventArgs e) + { + if (this.TargetDetached != null) { - if (this.TargetDetached != null) - { - this.TargetDetached(this, e); - } + this.TargetDetached(this, e); } + } - /// - /// Raises the TargetAttached event. - /// - /// - protected virtual void OnTargetAttached(TargetAttachedEventArgs e) + /// + /// Raises the TargetAttached event. + /// + /// + protected virtual void OnTargetAttached(TargetAttachedEventArgs e) + { + if (this.TargetAttached != null) { - if (this.TargetAttached != null) - { - this.TargetAttached(this, e); - } + this.TargetAttached(this, e); } } } diff --git a/dotnet/src/webdriver/DevTools/TargetAttachedEventArgs.cs b/dotnet/src/webdriver/DevTools/TargetAttachedEventArgs.cs index 0f991da432c90..dae6b276d4f32 100644 --- a/dotnet/src/webdriver/DevTools/TargetAttachedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/TargetAttachedEventArgs.cs @@ -19,39 +19,38 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the TargetAttached event is raised. +/// +public class TargetAttachedEventArgs : EventArgs { /// - /// Event arguments present when the TargetAttached event is raised. + /// Initializes a new instance of the type. /// - public class TargetAttachedEventArgs : EventArgs + /// The ID of the session of the target attached. + /// The target which is attached. + /// If the target is waiting on the debugger. Target continues after invoking Runtime.runIfWaitingForDebugger. + public TargetAttachedEventArgs(string sessionId, TargetInfo? targetInfo, bool waitingForDebugger) { - /// - /// Initializes a new instance of the type. - /// - /// The ID of the session of the target attached. - /// The target which is attached. - /// If the target is waiting on the debugger. Target continues after invoking Runtime.runIfWaitingForDebugger. - public TargetAttachedEventArgs(string sessionId, TargetInfo? targetInfo, bool waitingForDebugger) - { - SessionId = sessionId; - TargetInfo = targetInfo; - WaitingForDebugger = waitingForDebugger; - } + SessionId = sessionId; + TargetInfo = targetInfo; + WaitingForDebugger = waitingForDebugger; + } - /// - /// Gets the ID of the session of the target attached. - /// - public string SessionId { get; } + /// + /// Gets the ID of the session of the target attached. + /// + public string SessionId { get; } - /// - /// Gets the target which is attached. - /// - public TargetInfo? TargetInfo { get; } + /// + /// Gets the target which is attached. + /// + public TargetInfo? TargetInfo { get; } - /// - /// Gets if the target is waiting on the debugger. Target continues after invoking Runtime.runIfWaitingForDebugger. - /// - public bool WaitingForDebugger { get; } - } + /// + /// Gets if the target is waiting on the debugger. Target continues after invoking Runtime.runIfWaitingForDebugger. + /// + public bool WaitingForDebugger { get; } } diff --git a/dotnet/src/webdriver/DevTools/TargetDetachedEventArgs.cs b/dotnet/src/webdriver/DevTools/TargetDetachedEventArgs.cs index b21bc5d343dec..2ad55b1a99bde 100644 --- a/dotnet/src/webdriver/DevTools/TargetDetachedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/TargetDetachedEventArgs.cs @@ -19,32 +19,31 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Event arguments present when the TargetDetached event is raised. +/// +public class TargetDetachedEventArgs : EventArgs { /// - /// Event arguments present when the TargetDetached event is raised. + /// Initializes a new instance of the type. /// - public class TargetDetachedEventArgs : EventArgs + /// The ID of the session of the target detached. + /// The ID of the target detached. + public TargetDetachedEventArgs(string sessionId, string? targetId) { - /// - /// Initializes a new instance of the type. - /// - /// The ID of the session of the target detached. - /// The ID of the target detached. - public TargetDetachedEventArgs(string sessionId, string? targetId) - { - SessionId = sessionId; - TargetId = targetId; - } + SessionId = sessionId; + TargetId = targetId; + } - /// - /// Gets the ID of the session of the target detached. - /// - public string SessionId { get; } + /// + /// Gets the ID of the session of the target detached. + /// + public string SessionId { get; } - /// - /// Gets the ID of the target detached. - /// - public string? TargetId { get; } - } + /// + /// Gets the ID of the target detached. + /// + public string? TargetId { get; } } diff --git a/dotnet/src/webdriver/DevTools/TargetInfo.cs b/dotnet/src/webdriver/DevTools/TargetInfo.cs index b66e7769e0f50..0242340adba73 100644 --- a/dotnet/src/webdriver/DevTools/TargetInfo.cs +++ b/dotnet/src/webdriver/DevTools/TargetInfo.cs @@ -17,67 +17,66 @@ // under the License. // -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents information about the target of a DevTools Protocol command +/// +public class TargetInfo { /// - /// Represents information about the target of a DevTools Protocol command + /// Initializes a new instance of the type. /// - public class TargetInfo + /// The ID of the target. + /// The type of target. + /// The title of the target. + /// The URL of the target. + /// Whether the protocol is attached to the target. + /// The ID of the opener of the target. + /// The browser context ID. + public TargetInfo(string targetId, string type, string title, string url, bool isAttached, string? openerId, string? browserContextId) { - /// - /// Initializes a new instance of the type. - /// - /// The ID of the target. - /// The type of target. - /// The title of the target. - /// The URL of the target. - /// Whether the protocol is attached to the target. - /// The ID of the opener of the target. - /// The browser context ID. - public TargetInfo(string targetId, string type, string title, string url, bool isAttached, string? openerId, string? browserContextId) - { - this.TargetId = targetId; - this.Type = type; - this.Title = title; - this.Url = url; - this.IsAttached = isAttached; - this.OpenerId = openerId; - this.BrowserContextId = browserContextId; - } + this.TargetId = targetId; + this.Type = type; + this.Title = title; + this.Url = url; + this.IsAttached = isAttached; + this.OpenerId = openerId; + this.BrowserContextId = browserContextId; + } - /// - /// Gets the ID of the target. - /// - public string TargetId { get; } + /// + /// Gets the ID of the target. + /// + public string TargetId { get; } - /// - /// Gets the type of target. - /// - public string Type { get; } + /// + /// Gets the type of target. + /// + public string Type { get; } - /// - /// Gets the title of the target. - /// - public string Title { get; } + /// + /// Gets the title of the target. + /// + public string Title { get; } - /// - /// Gets the URL of the target. - /// - public string Url { get; } + /// + /// Gets the URL of the target. + /// + public string Url { get; } - /// - /// Gets a value indicating if the protocol is attached to the target. - /// - public bool IsAttached { get; } + /// + /// Gets a value indicating if the protocol is attached to the target. + /// + public bool IsAttached { get; } - /// - /// Gets the ID of the opener of the target. - /// - public string? OpenerId { get; } + /// + /// Gets the ID of the opener of the target. + /// + public string? OpenerId { get; } - /// - /// Gets the browser context ID. - /// - public string? BrowserContextId { get; } - } + /// + /// Gets the browser context ID. + /// + public string? BrowserContextId { get; } } diff --git a/dotnet/src/webdriver/DevTools/WebSocketConnection.cs b/dotnet/src/webdriver/DevTools/WebSocketConnection.cs index e0b2447304264..811312fd5f346 100644 --- a/dotnet/src/webdriver/DevTools/WebSocketConnection.cs +++ b/dotnet/src/webdriver/DevTools/WebSocketConnection.cs @@ -23,285 +23,284 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents a connection to a WebDriver Bidi remote end. +/// +public class WebSocketConnection { + private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); + private readonly CancellationTokenSource clientTokenSource = new CancellationTokenSource(); + private readonly TimeSpan startupTimeout; + private readonly TimeSpan shutdownTimeout; + private Task? dataReceiveTask; + private ClientWebSocket client = new ClientWebSocket(); + private readonly SemaphoreSlim sendMethodSemaphore = new SemaphoreSlim(1, 1); + /// - /// Represents a connection to a WebDriver Bidi remote end. + /// Initializes a new instance of the class. /// - public class WebSocketConnection + public WebSocketConnection() + : this(DefaultTimeout) { - private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); - private readonly CancellationTokenSource clientTokenSource = new CancellationTokenSource(); - private readonly TimeSpan startupTimeout; - private readonly TimeSpan shutdownTimeout; - private Task? dataReceiveTask; - private ClientWebSocket client = new ClientWebSocket(); - private readonly SemaphoreSlim sendMethodSemaphore = new SemaphoreSlim(1, 1); - - /// - /// Initializes a new instance of the class. - /// - public WebSocketConnection() - : this(DefaultTimeout) - { - } + } - /// - /// Initializes a new instance of the class with a given startup timeout. - /// - /// The timeout before throwing an error when starting up the connection. - public WebSocketConnection(TimeSpan startupTimeout) - : this(startupTimeout, DefaultTimeout) - { - } + /// + /// Initializes a new instance of the class with a given startup timeout. + /// + /// The timeout before throwing an error when starting up the connection. + public WebSocketConnection(TimeSpan startupTimeout) + : this(startupTimeout, DefaultTimeout) + { + } - /// - /// Initializes a new instance of the class with a given startup and shutdown timeout. - /// - /// The timeout before throwing an error when starting up the connection. - /// The timeout before throwing an error when shutting down the connection. - public WebSocketConnection(TimeSpan startupTimeout, TimeSpan shutdownTimeout) - { - this.startupTimeout = startupTimeout; - this.shutdownTimeout = shutdownTimeout; - } + /// + /// Initializes a new instance of the class with a given startup and shutdown timeout. + /// + /// The timeout before throwing an error when starting up the connection. + /// The timeout before throwing an error when shutting down the connection. + public WebSocketConnection(TimeSpan startupTimeout, TimeSpan shutdownTimeout) + { + this.startupTimeout = startupTimeout; + this.shutdownTimeout = shutdownTimeout; + } - /// - /// Occurs when data is received from this connection. - /// - public event EventHandler? DataReceived; - - /// - /// Occurs when a log message is emitted from this connection. - /// - public event EventHandler? LogMessage; - - /// - /// Gets a value indicating whether this connection is active. - /// - public bool IsActive { get; private set; } = false; - - /// - /// Gets the buffer size for communication used by this connection. - /// - public int BufferSize { get; } = 4096; - - /// - /// Asynchronously starts communication with the remote end of this connection. - /// - /// The URL used to connect to the remote end. - /// The task object representing the asynchronous operation. - /// Thrown when the connection is not established within the startup timeout. - public virtual async Task Start(string url) - { - if (url is null) - { - throw new ArgumentNullException(nameof(url)); - } + /// + /// Occurs when data is received from this connection. + /// + public event EventHandler? DataReceived; - this.Log($"Opening connection to URL {url}", DevToolsSessionLogLevel.Trace); - bool connected = false; - DateTime timeout = DateTime.Now.Add(this.startupTimeout); - while (!connected && DateTime.Now <= timeout) - { - try - { - await this.client.ConnectAsync(new Uri(url), this.clientTokenSource.Token).ConfigureAwait(false); - connected = true; - } - catch (WebSocketException) - { - // If the server-side socket is not yet ready, it leaves the client socket in a closed state, - // which sees the object as disposed, so we must create a new one to try again - await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); - this.client = new ClientWebSocket(); - } - } + /// + /// Occurs when a log message is emitted from this connection. + /// + public event EventHandler? LogMessage; - if (!connected) - { - throw new TimeoutException($"Could not connect to browser within {this.startupTimeout.TotalSeconds} seconds"); - } + /// + /// Gets a value indicating whether this connection is active. + /// + public bool IsActive { get; private set; } = false; + + /// + /// Gets the buffer size for communication used by this connection. + /// + public int BufferSize { get; } = 4096; - this.dataReceiveTask = Task.Run(async () => await this.ReceiveData()); - this.IsActive = true; - this.Log($"Connection opened", DevToolsSessionLogLevel.Trace); + /// + /// Asynchronously starts communication with the remote end of this connection. + /// + /// The URL used to connect to the remote end. + /// The task object representing the asynchronous operation. + /// Thrown when the connection is not established within the startup timeout. + public virtual async Task Start(string url) + { + if (url is null) + { + throw new ArgumentNullException(nameof(url)); } - /// - /// Asynchronously stops communication with the remote end of this connection. - /// - /// The task object representing the asynchronous operation. - public virtual async Task Stop() + this.Log($"Opening connection to URL {url}", DevToolsSessionLogLevel.Trace); + bool connected = false; + DateTime timeout = DateTime.Now.Add(this.startupTimeout); + while (!connected && DateTime.Now <= timeout) { - this.Log($"Closing connection", DevToolsSessionLogLevel.Trace); - if (this.client.State != WebSocketState.Open) - { - this.Log($"Socket already closed (Socket state: {this.client.State})"); - } - else + try { - await this.CloseClientWebSocket().ConfigureAwait(false); + await this.client.ConnectAsync(new Uri(url), this.clientTokenSource.Token).ConfigureAwait(false); + connected = true; } - - // Whether we closed the socket or timed out, we cancel the token causing ReceiveAsync to abort the socket. - // The finally block at the end of the processing loop will dispose of the ClientWebSocket object. - this.clientTokenSource.Cancel(); - if (this.dataReceiveTask != null) + catch (WebSocketException) { - await this.dataReceiveTask.ConfigureAwait(false); + // If the server-side socket is not yet ready, it leaves the client socket in a closed state, + // which sees the object as disposed, so we must create a new one to try again + await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); + this.client = new ClientWebSocket(); } + } - this.client.Dispose(); + if (!connected) + { + throw new TimeoutException($"Could not connect to browser within {this.startupTimeout.TotalSeconds} seconds"); } - /// - /// Asynchronously sends data to the remote end of this connection. - /// - /// The data to be sent to the remote end of this connection. - /// The task object representing the asynchronous operation. - public virtual async Task SendData(string data) + this.dataReceiveTask = Task.Run(async () => await this.ReceiveData()); + this.IsActive = true; + this.Log($"Connection opened", DevToolsSessionLogLevel.Trace); + } + + /// + /// Asynchronously stops communication with the remote end of this connection. + /// + /// The task object representing the asynchronous operation. + public virtual async Task Stop() + { + this.Log($"Closing connection", DevToolsSessionLogLevel.Trace); + if (this.client.State != WebSocketState.Open) { - if (data is null) - { - throw new ArgumentNullException(nameof(data)); - } + this.Log($"Socket already closed (Socket state: {this.client.State})"); + } + else + { + await this.CloseClientWebSocket().ConfigureAwait(false); + } - ArraySegment messageBuffer = new ArraySegment(Encoding.UTF8.GetBytes(data)); - this.Log($"SEND >>> {data}"); + // Whether we closed the socket or timed out, we cancel the token causing ReceiveAsync to abort the socket. + // The finally block at the end of the processing loop will dispose of the ClientWebSocket object. + this.clientTokenSource.Cancel(); + if (this.dataReceiveTask != null) + { + await this.dataReceiveTask.ConfigureAwait(false); + } - await sendMethodSemaphore.WaitAsync().ConfigureAwait(false); + this.client.Dispose(); + } - try - { - await this.client.SendAsync(messageBuffer, WebSocketMessageType.Text, endOfMessage: true, CancellationToken.None).ConfigureAwait(false); - } - finally - { - sendMethodSemaphore.Release(); - } + /// + /// Asynchronously sends data to the remote end of this connection. + /// + /// The data to be sent to the remote end of this connection. + /// The task object representing the asynchronous operation. + public virtual async Task SendData(string data) + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); } - /// - /// Asynchronously closes the client WebSocket. - /// - /// The task object representing the asynchronous operation. - protected virtual async Task CloseClientWebSocket() + ArraySegment messageBuffer = new ArraySegment(Encoding.UTF8.GetBytes(data)); + this.Log($"SEND >>> {data}"); + + await sendMethodSemaphore.WaitAsync().ConfigureAwait(false); + + try { - // Close the socket first, because ReceiveAsync leaves an invalid socket (state = aborted) when the token is cancelled - CancellationTokenSource timeout = new CancellationTokenSource(this.shutdownTimeout); - try - { - // After this, the socket state which change to CloseSent - await this.client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", timeout.Token).ConfigureAwait(false); + await this.client.SendAsync(messageBuffer, WebSocketMessageType.Text, endOfMessage: true, CancellationToken.None).ConfigureAwait(false); + } + finally + { + sendMethodSemaphore.Release(); + } + } - // Now we wait for the server response, which will close the socket - while (this.client.State != WebSocketState.Closed && this.client.State != WebSocketState.Aborted && !timeout.Token.IsCancellationRequested) - { - // The loop may be too tight for the cancellation token to get triggered, so add a small delay - await Task.Delay(TimeSpan.FromMilliseconds(10)).ConfigureAwait(false); - } + /// + /// Asynchronously closes the client WebSocket. + /// + /// The task object representing the asynchronous operation. + protected virtual async Task CloseClientWebSocket() + { + // Close the socket first, because ReceiveAsync leaves an invalid socket (state = aborted) when the token is cancelled + CancellationTokenSource timeout = new CancellationTokenSource(this.shutdownTimeout); + try + { + // After this, the socket state which change to CloseSent + await this.client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", timeout.Token).ConfigureAwait(false); - this.Log($"Client state is {this.client.State}", DevToolsSessionLogLevel.Trace); - } - catch (OperationCanceledException) - { - // An OperationCanceledException is normal upon task/token cancellation, so disregard it - } - catch (WebSocketException e) + // Now we wait for the server response, which will close the socket + while (this.client.State != WebSocketState.Closed && this.client.State != WebSocketState.Aborted && !timeout.Token.IsCancellationRequested) { - this.Log($"Unexpected error during attempt at close: {e.Message}", DevToolsSessionLogLevel.Error); + // The loop may be too tight for the cancellation token to get triggered, so add a small delay + await Task.Delay(TimeSpan.FromMilliseconds(10)).ConfigureAwait(false); } + + this.Log($"Client state is {this.client.State}", DevToolsSessionLogLevel.Trace); + } + catch (OperationCanceledException) + { + // An OperationCanceledException is normal upon task/token cancellation, so disregard it + } + catch (WebSocketException e) + { + this.Log($"Unexpected error during attempt at close: {e.Message}", DevToolsSessionLogLevel.Error); } + } - /// - /// Raises the DataReceived event. - /// - /// The event args used when raising the event. - protected virtual void OnDataReceived(WebSocketConnectionDataReceivedEventArgs e) + /// + /// Raises the DataReceived event. + /// + /// The event args used when raising the event. + protected virtual void OnDataReceived(WebSocketConnectionDataReceivedEventArgs e) + { + if (this.DataReceived != null) { - if (this.DataReceived != null) - { - this.DataReceived(this, e); - } + this.DataReceived(this, e); } + } - /// - /// Raises the LogMessage event. - /// - /// The event args used when raising the event. - protected virtual void OnLogMessage(DevToolsSessionLogMessageEventArgs e) + /// + /// Raises the LogMessage event. + /// + /// The event args used when raising the event. + protected virtual void OnLogMessage(DevToolsSessionLogMessageEventArgs e) + { + if (this.LogMessage != null) { - if (this.LogMessage != null) - { - this.LogMessage(this, e); - } + this.LogMessage(this, e); } + } - private async Task ReceiveData() + private async Task ReceiveData() + { + CancellationToken cancellationToken = this.clientTokenSource.Token; + try { - CancellationToken cancellationToken = this.clientTokenSource.Token; - try + StringBuilder messageBuilder = new StringBuilder(); + ArraySegment buffer = WebSocket.CreateClientBuffer(this.BufferSize, this.BufferSize); + while (this.client.State != WebSocketState.Closed && !cancellationToken.IsCancellationRequested) { - StringBuilder messageBuilder = new StringBuilder(); - ArraySegment buffer = WebSocket.CreateClientBuffer(this.BufferSize, this.BufferSize); - while (this.client.State != WebSocketState.Closed && !cancellationToken.IsCancellationRequested) - { - WebSocketReceiveResult receiveResult = await this.client.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); + WebSocketReceiveResult receiveResult = await this.client.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); - // If the token is cancelled while ReceiveAsync is blocking, the socket state changes to aborted and it can't be used - if (!cancellationToken.IsCancellationRequested) + // If the token is cancelled while ReceiveAsync is blocking, the socket state changes to aborted and it can't be used + if (!cancellationToken.IsCancellationRequested) + { + // The server is notifying us that the connection will close, and we did + // not initiate the close; send acknowledgement + if (receiveResult.MessageType == WebSocketMessageType.Close && this.client.State != WebSocketState.Closed && this.client.State != WebSocketState.CloseSent) { - // The server is notifying us that the connection will close, and we did - // not initiate the close; send acknowledgement - if (receiveResult.MessageType == WebSocketMessageType.Close && this.client.State != WebSocketState.Closed && this.client.State != WebSocketState.CloseSent) - { - this.Log($"Acknowledging Close frame received from server (client state: {this.client.State})", DevToolsSessionLogLevel.Trace); - await this.client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None).ConfigureAwait(false); - } + this.Log($"Acknowledging Close frame received from server (client state: {this.client.State})", DevToolsSessionLogLevel.Trace); + await this.client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None).ConfigureAwait(false); + } - // Display text or binary data - if (this.client.State == WebSocketState.Open && receiveResult.MessageType != WebSocketMessageType.Close) + // Display text or binary data + if (this.client.State == WebSocketState.Open && receiveResult.MessageType != WebSocketMessageType.Close) + { + messageBuilder.Append(Encoding.UTF8.GetString(buffer.Array!, 0, receiveResult.Count)); + if (receiveResult.EndOfMessage) { - messageBuilder.Append(Encoding.UTF8.GetString(buffer.Array!, 0, receiveResult.Count)); - if (receiveResult.EndOfMessage) + string message = messageBuilder.ToString(); + messageBuilder = new StringBuilder(); + if (message.Length > 0) { - string message = messageBuilder.ToString(); - messageBuilder = new StringBuilder(); - if (message.Length > 0) - { - this.Log($"RECV <<< {message}"); - this.OnDataReceived(new WebSocketConnectionDataReceivedEventArgs(message)); - } + this.Log($"RECV <<< {message}"); + this.OnDataReceived(new WebSocketConnectionDataReceivedEventArgs(message)); } } } } - - this.Log($"Ending processing loop in state {this.client.State}", DevToolsSessionLogLevel.Trace); - } - catch (OperationCanceledException) - { - // An OperationCanceledException is normal upon task/token cancellation, so disregard it - } - catch (WebSocketException e) - { - this.Log($"Unexpected error during receive of data: {e.Message}", DevToolsSessionLogLevel.Error); - } - finally - { - this.IsActive = false; } - } - private void Log(string message) + this.Log($"Ending processing loop in state {this.client.State}", DevToolsSessionLogLevel.Trace); + } + catch (OperationCanceledException) { - this.Log(message, DevToolsSessionLogLevel.Trace); + // An OperationCanceledException is normal upon task/token cancellation, so disregard it } - - private void Log(string message, DevToolsSessionLogLevel level) + catch (WebSocketException e) { - this.OnLogMessage(new DevToolsSessionLogMessageEventArgs(level, "[{0}] {1}", "Connection", message)); + this.Log($"Unexpected error during receive of data: {e.Message}", DevToolsSessionLogLevel.Error); } + finally + { + this.IsActive = false; + } + } + + private void Log(string message) + { + this.Log(message, DevToolsSessionLogLevel.Trace); + } + + private void Log(string message, DevToolsSessionLogLevel level) + { + this.OnLogMessage(new DevToolsSessionLogMessageEventArgs(level, "[{0}] {1}", "Connection", message)); } } diff --git a/dotnet/src/webdriver/DevTools/WebSocketConnectionDataReceivedEventArgs.cs b/dotnet/src/webdriver/DevTools/WebSocketConnectionDataReceivedEventArgs.cs index d0aa266d1646a..16e9d60aea66f 100644 --- a/dotnet/src/webdriver/DevTools/WebSocketConnectionDataReceivedEventArgs.cs +++ b/dotnet/src/webdriver/DevTools/WebSocketConnectionDataReceivedEventArgs.cs @@ -19,26 +19,25 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Object containing event data for events raised when data is received from a WebDriver Bidi connection. +/// +public class WebSocketConnectionDataReceivedEventArgs : EventArgs { /// - /// Object containing event data for events raised when data is received from a WebDriver Bidi connection. + /// Initializes a new instance of the class. /// - public class WebSocketConnectionDataReceivedEventArgs : EventArgs + /// The data received from the connection. + /// If is . + public WebSocketConnectionDataReceivedEventArgs(string data) { - /// - /// Initializes a new instance of the class. - /// - /// The data received from the connection. - /// If is . - public WebSocketConnectionDataReceivedEventArgs(string data) - { - this.Data = data ?? throw new ArgumentNullException(nameof(data)); - } - - /// - /// Gets the data received from the connection. - /// - public string Data { get; } + this.Data = data ?? throw new ArgumentNullException(nameof(data)); } + + /// + /// Gets the data received from the connection. + /// + public string Data { get; } } diff --git a/dotnet/src/webdriver/DevTools/v133/V133Domains.cs b/dotnet/src/webdriver/DevTools/v133/V133Domains.cs index 89c279a98c66b..df2d7824d53dd 100644 --- a/dotnet/src/webdriver/DevTools/v133/V133Domains.cs +++ b/dotnet/src/webdriver/DevTools/v133/V133Domains.cs @@ -19,53 +19,52 @@ using System; -namespace OpenQA.Selenium.DevTools.V133 +namespace OpenQA.Selenium.DevTools.V133; + +/// +/// Class containing the domain implementation for version 133 of the DevTools Protocol. +/// +public class V133Domains : DevToolsDomains { + private readonly DevToolsSessionDomains domains; + /// - /// Class containing the domain implementation for version 133 of the DevTools Protocol. + /// Initializes a new instance of the V133Domains class. /// - public class V133Domains : DevToolsDomains + /// The DevToolsSession to use with this set of domains. + /// If is . + public V133Domains(DevToolsSession session) { - private readonly DevToolsSessionDomains domains; - - /// - /// Initializes a new instance of the V133Domains class. - /// - /// The DevToolsSession to use with this set of domains. - /// If is . - public V133Domains(DevToolsSession session) - { - this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); - } + this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); + } - /// - /// Gets the DevTools Protocol version for which this class is valid. - /// - public static int DevToolsVersion => 133; + /// + /// Gets the DevTools Protocol version for which this class is valid. + /// + public static int DevToolsVersion => 133; - /// - /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. - /// - public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; + /// + /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. + /// + public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; - /// - /// Gets the object used for manipulating network information in the browser. - /// - public override DevTools.Network Network => new V133Network(domains.Network, domains.Fetch); + /// + /// Gets the object used for manipulating network information in the browser. + /// + public override DevTools.Network Network => new V133Network(domains.Network, domains.Fetch); - /// - /// Gets the object used for manipulating the browser's JavaScript execution. - /// - public override JavaScript JavaScript => new V133JavaScript(domains.Runtime, domains.Page); + /// + /// Gets the object used for manipulating the browser's JavaScript execution. + /// + public override JavaScript JavaScript => new V133JavaScript(domains.Runtime, domains.Page); - /// - /// Gets the object used for manipulating DevTools Protocol targets. - /// - public override DevTools.Target Target => new V133Target(domains.Target); + /// + /// Gets the object used for manipulating DevTools Protocol targets. + /// + public override DevTools.Target Target => new V133Target(domains.Target); - /// - /// Gets the object used for manipulating the browser's logs. - /// - public override DevTools.Log Log => new V133Log(domains.Log); - } + /// + /// Gets the object used for manipulating the browser's logs. + /// + public override DevTools.Log Log => new V133Log(domains.Log); } diff --git a/dotnet/src/webdriver/DevTools/v133/V133JavaScript.cs b/dotnet/src/webdriver/DevTools/v133/V133JavaScript.cs index 578f8c9b36faa..aa4813678a3b9 100644 --- a/dotnet/src/webdriver/DevTools/v133/V133JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/v133/V133JavaScript.cs @@ -23,163 +23,162 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V133 +namespace OpenQA.Selenium.DevTools.V133; + +/// +/// Class containing the JavaScript implementation for version 133 of the DevTools Protocol. +/// +public class V133JavaScript : JavaScript { + private readonly RuntimeAdapter runtime; + private readonly PageAdapter page; + /// - /// Class containing the JavaScript implementation for version 133 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V133JavaScript : JavaScript + /// The DevTools Protocol adapter for the Runtime domain. + /// The DevTools Protocol adapter for the Page domain. + /// If or are . + public V133JavaScript(RuntimeAdapter runtime, PageAdapter page) { - private readonly RuntimeAdapter runtime; - private readonly PageAdapter page; - - /// - /// Initializes a new instance of the class. - /// - /// The DevTools Protocol adapter for the Runtime domain. - /// The DevTools Protocol adapter for the Page domain. - /// If or are . - public V133JavaScript(RuntimeAdapter runtime, PageAdapter page) - { - this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); - this.page = page ?? throw new ArgumentNullException(nameof(page)); - this.runtime.BindingCalled += OnRuntimeBindingCalled; - this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; - this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; - } + this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); + this.page = page ?? throw new ArgumentNullException(nameof(page)); + this.runtime.BindingCalled += OnRuntimeBindingCalled; + this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; + this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; + } - /// - /// Asynchronously enables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableRuntime() - { - await runtime.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableRuntime() + { + await runtime.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableRuntime() - { - await runtime.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableRuntime() + { + await runtime.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously enables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnablePage() - { - await page.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnablePage() + { + await page.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisablePage() - { - await page.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisablePage() + { + await page.Disable().ConfigureAwait(false); + } - /// - /// Adds a binding to a specific JavaScript name. - /// - /// The name to which to bind to. - /// A task that represents the asynchronous operation. - public override async Task AddBinding(string name) - { - await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Adds a binding to a specific JavaScript name. + /// + /// The name to which to bind to. + /// A task that represents the asynchronous operation. + public override async Task AddBinding(string name) + { + await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Removes a binding from a specific JavaScript name. - /// - /// The name to which to remove the bind from. - /// A task that represents the asynchronous operation. - public override async Task RemoveBinding(string name) - { - await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Removes a binding from a specific JavaScript name. + /// + /// The name to which to remove the bind from. + /// A task that represents the asynchronous operation. + public override async Task RemoveBinding(string name) + { + await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Adds a JavaScript snippet to evaluate when a new document is opened. - /// - /// The script to add to be evaluated when a new document is opened. - /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. - public override async Task AddScriptToEvaluateOnNewDocument(string script) - { - var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); - return result.Identifier; - } + /// + /// Adds a JavaScript snippet to evaluate when a new document is opened. + /// + /// The script to add to be evaluated when a new document is opened. + /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. + public override async Task AddScriptToEvaluateOnNewDocument(string script) + { + var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); + return result.Identifier; + } - /// - /// Removes a JavaScript snippet from evaluate when a new document is opened. - /// - /// The ID of the script to be removed. - /// A task that represents the asynchronous operation. - public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) - { - await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); - } + /// + /// Removes a JavaScript snippet from evaluate when a new document is opened. + /// + /// The ID of the script to be removed. + /// A task that represents the asynchronous operation. + public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) + { + await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); + } - /// - /// Evaluates a JavaScript snippet. It does not return a value. - /// - /// The script to evaluate - /// A task that represents the asynchronous operation. - /// - /// This method is internal to the operation of pinned scripts in Selenium, and - /// is therefore internal by design. - /// - internal override async Task Evaluate(string script) - { - await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); - } + /// + /// Evaluates a JavaScript snippet. It does not return a value. + /// + /// The script to evaluate + /// A task that represents the asynchronous operation. + /// + /// This method is internal to the operation of pinned scripts in Selenium, and + /// is therefore internal by design. + /// + internal override async Task Evaluate(string script) + { + await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); + } - private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) - { - BindingCalledEventArgs wrapped = new BindingCalledEventArgs - ( - executionContextId: e.ExecutionContextId, - name: e.Name, - payload: e.Payload - ); - - this.OnBindingCalled(wrapped); - } + private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) + { + BindingCalledEventArgs wrapped = new BindingCalledEventArgs + ( + executionContextId: e.ExecutionContextId, + name: e.Name, + payload: e.Payload + ); + + this.OnBindingCalled(wrapped); + } - private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) - { - // TODO: Collect stack trace elements - var wrapped = new ExceptionThrownEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - message: e.ExceptionDetails.Text - ); - - this.OnExceptionThrown(wrapped); - } + private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) + { + // TODO: Collect stack trace elements + var wrapped = new ExceptionThrownEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + message: e.ExceptionDetails.Text + ); + + this.OnExceptionThrown(wrapped); + } - private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + { + List args = new List(e.Args.Length); + foreach (var arg in e.Args) { - List args = new List(e.Args.Length); - foreach (var arg in e.Args) - { - string? argValue = arg.Value?.ToString(); - args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); - } - - var wrapped = new ConsoleApiCalledEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - type: e.Type, - arguments: args.AsReadOnly() - ); - - this.OnConsoleApiCalled(wrapped); + string? argValue = arg.Value?.ToString(); + args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); } + + var wrapped = new ConsoleApiCalledEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + type: e.Type, + arguments: args.AsReadOnly() + ); + + this.OnConsoleApiCalled(wrapped); } } diff --git a/dotnet/src/webdriver/DevTools/v133/V133Log.cs b/dotnet/src/webdriver/DevTools/v133/V133Log.cs index e6cf6043987b5..3c1eb3a8d32b2 100644 --- a/dotnet/src/webdriver/DevTools/v133/V133Log.cs +++ b/dotnet/src/webdriver/DevTools/v133/V133Log.cs @@ -21,61 +21,60 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V133 +namespace OpenQA.Selenium.DevTools.V133; + +/// +/// Class containing the browser's log as referenced by version 133 of the DevTools Protocol. +/// +public class V133Log : DevTools.Log { + private LogAdapter adapter; + /// - /// Class containing the browser's log as referenced by version 133 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V133Log : DevTools.Log + /// The adapter for the Log domain. + /// If is . + public V133Log(LogAdapter adapter) { - private LogAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Log domain. - /// If is . - public V133Log(LogAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - this.adapter.EntryAdded += OnAdapterEntryAdded; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + this.adapter.EntryAdded += OnAdapterEntryAdded; + } - /// - /// Asynchronously enables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Enable() - { - await adapter.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Enable() + { + await adapter.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Disable() - { - await adapter.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Disable() + { + await adapter.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously clears the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Clear() - { - await adapter.Clear().ConfigureAwait(false); - } + /// + /// Asynchronously clears the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Clear() + { + await adapter.Clear().ConfigureAwait(false); + } - private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) - { - var entry = new LogEntry( - kind: e.Entry.Source.ToString(), - message: e.Entry.Text - ); + private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) + { + var entry = new LogEntry( + kind: e.Entry.Source.ToString(), + message: e.Entry.Text + ); - this.OnEntryAdded(new EntryAddedEventArgs(entry)); - } + this.OnEntryAdded(new EntryAddedEventArgs(entry)); } } diff --git a/dotnet/src/webdriver/DevTools/v133/V133Network.cs b/dotnet/src/webdriver/DevTools/v133/V133Network.cs index e9634fb7eda2c..9f78a238ae419 100644 --- a/dotnet/src/webdriver/DevTools/v133/V133Network.cs +++ b/dotnet/src/webdriver/DevTools/v133/V133Network.cs @@ -24,364 +24,363 @@ using System.Text; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V133 +namespace OpenQA.Selenium.DevTools.V133; + +/// +/// Class providing functionality for manipulating network calls using version 133 of the DevTools Protocol +/// +public class V133Network : DevTools.Network { + private FetchAdapter fetch; + private NetworkAdapter network; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Network domain. + /// The adapter for the Fetch domain. + /// If or are . + public V133Network(NetworkAdapter network, FetchAdapter fetch) + { + this.network = network ?? throw new ArgumentNullException(nameof(network)); + this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); + fetch.AuthRequired += OnFetchAuthRequired; + fetch.RequestPaused += OnFetchRequestPaused; + } + /// - /// Class providing functionality for manipulating network calls using version 133 of the DevTools Protocol + /// Asynchronously disables network caching. /// - public class V133Network : DevTools.Network + /// A task that represents the asynchronous operation. + public override async Task DisableNetworkCaching() { - private FetchAdapter fetch; - private NetworkAdapter network; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Network domain. - /// The adapter for the Fetch domain. - /// If or are . - public V133Network(NetworkAdapter network, FetchAdapter fetch) + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables network caching. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetworkCaching() + { + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetwork() + { + await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + } + + /// + /// Asynchronously disables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableNetwork() + { + await network.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously enables the fetch domain for all URL patterns. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableFetchForAllPatterns() + { + await fetch.Enable(new Fetch.EnableCommandSettings() { - this.network = network ?? throw new ArgumentNullException(nameof(network)); - this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); - fetch.AuthRequired += OnFetchAuthRequired; - fetch.RequestPaused += OnFetchRequestPaused; - } + Patterns = new Fetch.RequestPattern[] + { + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } + }, + HandleAuthRequests = true + }).ConfigureAwait(false); + } - /// - /// Asynchronously disables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetworkCaching() + /// + /// Asynchronously disables the fetch domain. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableFetch() + { + await fetch.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously sets the override of the user agent settings. + /// + /// A object containing the user agent values to override. + /// A task that represents the asynchronous operation. + /// If is null. + public override async Task SetUserAgentOverride(UserAgent userAgent) + { + if (userAgent is null) { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(userAgent)); } - /// - /// Asynchronously enables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetworkCaching() + await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); - } + UserAgent = userAgent.UserAgentString, + AcceptLanguage = userAgent.AcceptLanguage, + Platform = userAgent.Platform + }).ConfigureAwait(false); + } - /// - /// Asynchronously enables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetwork() + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequest(HttpRequestData requestData) + { + if (requestData is null) { - await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously disables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetwork() + var commandSettings = new ContinueRequestCommandSettings() { - await network.Disable().ConfigureAwait(false); - } + RequestId = requestData.RequestId, + Method = requestData.Method, + Url = requestData.Url, + }; - /// - /// Asynchronously enables the fetch domain for all URL patterns. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableFetchForAllPatterns() + if (requestData.Headers?.Count > 0) { - await fetch.Enable(new Fetch.EnableCommandSettings() + List headers = new List(); + foreach (KeyValuePair headerPair in requestData.Headers) { - Patterns = new Fetch.RequestPattern[] - { - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } - }, - HandleAuthRequests = true - }).ConfigureAwait(false); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); + } + + commandSettings.Headers = headers.ToArray(); } - /// - /// Asynchronously disables the fetch domain. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableFetch() + if (!string.IsNullOrEmpty(requestData.PostData)) { - await fetch.Disable().ConfigureAwait(false); + commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); } - /// - /// Asynchronously sets the override of the user agent settings. - /// - /// A object containing the user agent values to override. - /// A task that represents the asynchronous operation. - /// If is null. - public override async Task SetUserAgentOverride(UserAgent userAgent) - { - if (userAgent is null) - { - throw new ArgumentNullException(nameof(userAgent)); - } + await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + } - await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() - { - UserAgent = userAgent.UserAgentString, - AcceptLanguage = userAgent.AcceptLanguage, - Platform = userAgent.Platform - }).ConfigureAwait(false); + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// The with which to respond to the request + /// A task that represents the asynchronous operation. + /// If or are . + public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequest(HttpRequestData requestData) + if (responseData is null) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } + throw new ArgumentNullException(nameof(responseData)); + } - var commandSettings = new ContinueRequestCommandSettings() - { - RequestId = requestData.RequestId, - Method = requestData.Method, - Url = requestData.Url, - }; + var commandSettings = new FulfillRequestCommandSettings() + { + RequestId = requestData.RequestId, + ResponseCode = responseData.StatusCode, + }; - if (requestData.Headers?.Count > 0) + if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) + { + List headers = new List(); + foreach (KeyValuePair headerPair in responseData.Headers) { - List headers = new List(); - foreach (KeyValuePair headerPair in requestData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } - - commandSettings.Headers = headers.ToArray(); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); } - if (!string.IsNullOrEmpty(requestData.PostData)) + foreach (string cookieHeader in responseData.CookieHeaders) { - commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); + headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); } - await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + commandSettings.ResponseHeaders = headers.ToArray(); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// The with which to respond to the request - /// A task that represents the asynchronous operation. - /// If or are . - public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + if (!string.IsNullOrEmpty(responseData.Body)) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } - - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - var commandSettings = new FulfillRequestCommandSettings() - { - RequestId = requestData.RequestId, - ResponseCode = responseData.StatusCode, - }; + commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + } - if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) - { - List headers = new List(); - foreach (KeyValuePair headerPair in responseData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } + await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); + } - foreach (string cookieHeader in responseData.CookieHeaders) - { - headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); - } + /// + /// Asynchronously continues an intercepted network call without modification. + /// + /// The of the network call. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); + } - commandSettings.ResponseHeaders = headers.ToArray(); - } + await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); + } - if (!string.IsNullOrEmpty(responseData.Body)) + /// + /// Asynchronously continues an intercepted network call using authentication. + /// + /// The ID of the network request for which to continue with authentication. + /// The user name with which to authenticate. + /// The password with which to authenticate. + /// A task that represents the asynchronous operation. + public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + { + RequestId = requestId, + AuthChallengeResponse = new V133.Fetch.AuthChallengeResponse() { - commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + Response = V133.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, + Username = userName, + Password = password } + }).ConfigureAwait(false); + } - await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call without modification. - /// - /// The of the network call. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + /// + /// Asynchronously cancels authorization of an intercepted network request. + /// + /// The ID of the network request for which to cancel authentication. + /// A task that represents the asynchronous operation. + public override async Task CancelAuth(string requestId) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() { - if (requestData is null) + RequestId = requestId, + AuthChallengeResponse = new OpenQA.Selenium.DevTools.V133.Fetch.AuthChallengeResponse() { - throw new ArgumentNullException(nameof(requestData)); + Response = V133.Fetch.AuthChallengeResponseResponseValues.CancelAuth } + }).ConfigureAwait(false); + } - await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call using authentication. - /// - /// The ID of the network request for which to continue with authentication. - /// The user name with which to authenticate. - /// The password with which to authenticate. - /// A task that represents the asynchronous operation. - public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + /// + /// Asynchronously adds the response body to the provided object. + /// + /// The object to which to add the response body. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task AddResponseBody(HttpResponseData responseData) + { + if (responseData is null) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() - { - RequestId = requestId, - AuthChallengeResponse = new V133.Fetch.AuthChallengeResponse() - { - Response = V133.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, - Username = userName, - Password = password - } - }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - /// - /// Asynchronously cancels authorization of an intercepted network request. - /// - /// The ID of the network request for which to cancel authentication. - /// A task that represents the asynchronous operation. - public override async Task CancelAuth(string requestId) + // If the response is a redirect, retrieving the body will throw an error in CDP. + if (responseData.StatusCode < 300 || responseData.StatusCode > 399) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + if (bodyResponse != null) { - RequestId = requestId, - AuthChallengeResponse = new OpenQA.Selenium.DevTools.V133.Fetch.AuthChallengeResponse() + if (bodyResponse.Base64Encoded) { - Response = V133.Fetch.AuthChallengeResponseResponseValues.CancelAuth + responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); } - }).ConfigureAwait(false); - } - - /// - /// Asynchronously adds the response body to the provided object. - /// - /// The object to which to add the response body. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task AddResponseBody(HttpResponseData responseData) - { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - // If the response is a redirect, retrieving the body will throw an error in CDP. - if (responseData.StatusCode < 300 || responseData.StatusCode > 399) - { - var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); - if (bodyResponse != null) + else { - if (bodyResponse.Base64Encoded) - { - responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); - } - else - { - responseData.Content = new HttpResponseContent(bodyResponse.Body); - } + responseData.Content = new HttpResponseContent(bodyResponse.Body); } } } + } - /// - /// Asynchronously continues an intercepted network response without modification. - /// - /// The of the network response. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + /// + /// Asynchronously continues an intercepted network response without modification. + /// + /// The of the network response. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + { + if (responseData is null) { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + } + + private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + { + AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs + ( + requestId: e.RequestId, + uri: e.Request.Url + ); + + this.OnAuthRequired(wrapped); + } + + private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + { + if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) { - AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs - ( - requestId: e.RequestId, - uri: e.Request.Url - ); + var requestData = new HttpRequestData + { + RequestId = e.RequestId, + Method = e.Request.Method, + Url = e.Request.Url, + PostData = e.Request.PostData, + Headers = new Dictionary(e.Request.Headers) + }; - this.OnAuthRequired(wrapped); + RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); + this.OnRequestPaused(wrapped); } - - private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + else { - if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) + var responseData = new HttpResponseData() { - var requestData = new HttpRequestData - { - RequestId = e.RequestId, - Method = e.Request.Method, - Url = e.Request.Url, - PostData = e.Request.PostData, - Headers = new Dictionary(e.Request.Headers) - }; - - RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); - this.OnRequestPaused(wrapped); - } - else + RequestId = e.RequestId, + Url = e.Request.Url, + ResourceType = e.ResourceType.ToString(), + StatusCode = e.ResponseStatusCode.GetValueOrDefault(), + ErrorReason = e.ResponseErrorReason?.ToString() + }; + + if (e.ResponseHeaders != null) { - var responseData = new HttpResponseData() + foreach (var header in e.ResponseHeaders) { - RequestId = e.RequestId, - Url = e.Request.Url, - ResourceType = e.ResourceType.ToString(), - StatusCode = e.ResponseStatusCode.GetValueOrDefault(), - ErrorReason = e.ResponseErrorReason?.ToString() - }; - - if (e.ResponseHeaders != null) - { - foreach (var header in e.ResponseHeaders) + if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) { - if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) + responseData.CookieHeaders.Add(header.Value); + } + else + { + if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) { - responseData.CookieHeaders.Add(header.Value); + responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; } else { - if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) - { - responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; - } - else - { - responseData.Headers.Add(header.Name, header.Value); - } + responseData.Headers.Add(header.Name, header.Value); } } } - - this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } + + this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } } } diff --git a/dotnet/src/webdriver/DevTools/v133/V133Target.cs b/dotnet/src/webdriver/DevTools/v133/V133Target.cs index ad134f0af28f2..6c0061ddedccf 100644 --- a/dotnet/src/webdriver/DevTools/v133/V133Target.cs +++ b/dotnet/src/webdriver/DevTools/v133/V133Target.cs @@ -23,141 +23,140 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V133 +namespace OpenQA.Selenium.DevTools.V133; + +/// +/// Class providing functionality for manipulating targets for version 133 of the DevTools Protocol +/// +public class V133Target : DevTools.Target { + private readonly TargetAdapter adapter; + /// - /// Class providing functionality for manipulating targets for version 133 of the DevTools Protocol + /// Initializes a new instance of the class. /// - public class V133Target : DevTools.Target + /// The adapter for the Target domain. + /// If is . + public V133Target(TargetAdapter adapter) { - private readonly TargetAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Target domain. - /// If is . - public V133Target(TargetAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - adapter.DetachedFromTarget += OnDetachedFromTarget; - adapter.AttachedToTarget += OnAttachedToTarget; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + adapter.DetachedFromTarget += OnDetachedFromTarget; + adapter.AttachedToTarget += OnAttachedToTarget; + } - /// - /// Asynchronously gets the targets available for this session. - /// - /// - /// A task that represents the asynchronous operation. The task result - /// contains the list of objects describing the - /// targets available for this session. - /// - public override async Task> GetTargets(object? settings = null) - { - settings ??= new GetTargetsCommandSettings(); - - var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - - List targets = new List(response.TargetInfos.Length); - for (int i = 0; i < response.TargetInfos.Length; i++) - { - var targetInfo = response.TargetInfos[i]; - var mapped = new TargetInfo - ( - targetId: targetInfo.TargetId, - title: targetInfo.Title, - type: targetInfo.Type, - url: targetInfo.Url, - openerId: targetInfo.OpenerId, - browserContextId: targetInfo.BrowserContextId, - isAttached: targetInfo.Attached - ); - targets.Add(mapped); - } - - return targets.AsReadOnly(); - } + /// + /// Asynchronously gets the targets available for this session. + /// + /// + /// A task that represents the asynchronous operation. The task result + /// contains the list of objects describing the + /// targets available for this session. + /// + public override async Task> GetTargets(object? settings = null) + { + settings ??= new GetTargetsCommandSettings(); - /// - /// Asynchronously attaches to a target. - /// - /// The ID of the target to which to attach. - /// - /// A task representing the asynchronous attach operation. The task result contains the - /// session ID established for commands to the target attached to. - /// - public override async Task AttachToTarget(string targetId) - { - var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); - return result.SessionId; - } + var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - /// - /// Asynchronously detaches from a target. - /// - /// The ID of the session of the target from which to detach. - /// The ID of the target from which to detach. - /// A task representing the asynchronous detach operation. - public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + List targets = new List(response.TargetInfos.Length); + for (int i = 0; i < response.TargetInfos.Length; i++) { - await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() - { - SessionId = sessionId, - TargetId = targetId - }).ConfigureAwait(false); + var targetInfo = response.TargetInfos[i]; + var mapped = new TargetInfo + ( + targetId: targetInfo.TargetId, + title: targetInfo.Title, + type: targetInfo.Type, + url: targetInfo.Url, + openerId: targetInfo.OpenerId, + browserContextId: targetInfo.BrowserContextId, + isAttached: targetInfo.Attached + ); + targets.Add(mapped); } - /// - /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. - /// - /// A task that represents the asynchronous operation. - public override async Task SetAutoAttach() - { - await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); - } + return targets.AsReadOnly(); + } - private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) - { - this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); - } + /// + /// Asynchronously attaches to a target. + /// + /// The ID of the target to which to attach. + /// + /// A task representing the asynchronous attach operation. The task result contains the + /// session ID established for commands to the target attached to. + /// + public override async Task AttachToTarget(string targetId) + { + var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); + return result.SessionId; + } - private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + /// + /// Asynchronously detaches from a target. + /// + /// The ID of the session of the target from which to detach. + /// The ID of the target from which to detach. + /// A task representing the asynchronous detach operation. + public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + { + await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() { - var targetInfo = e.TargetInfo == null ? null : new TargetInfo - ( - browserContextId: e.TargetInfo.BrowserContextId, - isAttached: e.TargetInfo.Attached, - openerId: e.TargetInfo.OpenerId, - targetId: e.TargetInfo.TargetId, - title: e.TargetInfo.Title, - type: e.TargetInfo.Type, - url: e.TargetInfo.Url - ); + SessionId = sessionId, + TargetId = targetId + }).ConfigureAwait(false); + } - this.OnTargetAttached(new TargetAttachedEventArgs - ( - sessionId: e.SessionId, - targetInfo: targetInfo, - waitingForDebugger: e.WaitingForDebugger - )); - } + /// + /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. + /// + /// A task that represents the asynchronous operation. + public override async Task SetAutoAttach() + { + await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); + } - internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) + { + this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); + } + + private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + { + var targetInfo = e.TargetInfo == null ? null : new TargetInfo + ( + browserContextId: e.TargetInfo.BrowserContextId, + isAttached: e.TargetInfo.Attached, + openerId: e.TargetInfo.OpenerId, + targetId: e.TargetInfo.TargetId, + title: e.TargetInfo.Title, + type: e.TargetInfo.Type, + url: e.TargetInfo.Url + ); + + this.OnTargetAttached(new TargetAttachedEventArgs + ( + sessionId: e.SessionId, + targetInfo: targetInfo, + waitingForDebugger: e.WaitingForDebugger + )); + } + + internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + { + return new SetAutoAttachCommandSettings { - return new SetAutoAttachCommandSettings - { - AutoAttach = true, - Flatten = true, - WaitForDebuggerOnStart = waitForDebuggerOnStart - }; - } + AutoAttach = true, + Flatten = true, + WaitForDebuggerOnStart = waitForDebuggerOnStart + }; + } - internal override ICommand CreateDiscoverTargetsCommand() + internal override ICommand CreateDiscoverTargetsCommand() + { + return new SetDiscoverTargetsCommandSettings { - return new SetDiscoverTargetsCommandSettings - { - Discover = true - }; - } + Discover = true + }; } } diff --git a/dotnet/src/webdriver/DevTools/v134/V134Domains.cs b/dotnet/src/webdriver/DevTools/v134/V134Domains.cs index d83b7dffacca8..d936aa8b2c3a7 100644 --- a/dotnet/src/webdriver/DevTools/v134/V134Domains.cs +++ b/dotnet/src/webdriver/DevTools/v134/V134Domains.cs @@ -19,53 +19,52 @@ using System; -namespace OpenQA.Selenium.DevTools.V134 +namespace OpenQA.Selenium.DevTools.V134; + +/// +/// Class containing the domain implementation for version 134 of the DevTools Protocol. +/// +public class V134Domains : DevToolsDomains { + private readonly DevToolsSessionDomains domains; + /// - /// Class containing the domain implementation for version 134 of the DevTools Protocol. + /// Initializes a new instance of the V134Domains class. /// - public class V134Domains : DevToolsDomains + /// The DevToolsSession to use with this set of domains. + /// If is . + public V134Domains(DevToolsSession session) { - private readonly DevToolsSessionDomains domains; - - /// - /// Initializes a new instance of the V134Domains class. - /// - /// The DevToolsSession to use with this set of domains. - /// If is . - public V134Domains(DevToolsSession session) - { - this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); - } + this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); + } - /// - /// Gets the DevTools Protocol version for which this class is valid. - /// - public static int DevToolsVersion => 134; + /// + /// Gets the DevTools Protocol version for which this class is valid. + /// + public static int DevToolsVersion => 134; - /// - /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. - /// - public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; + /// + /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. + /// + public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; - /// - /// Gets the object used for manipulating network information in the browser. - /// - public override DevTools.Network Network => new V134Network(domains.Network, domains.Fetch); + /// + /// Gets the object used for manipulating network information in the browser. + /// + public override DevTools.Network Network => new V134Network(domains.Network, domains.Fetch); - /// - /// Gets the object used for manipulating the browser's JavaScript execution. - /// - public override JavaScript JavaScript => new V134JavaScript(domains.Runtime, domains.Page); + /// + /// Gets the object used for manipulating the browser's JavaScript execution. + /// + public override JavaScript JavaScript => new V134JavaScript(domains.Runtime, domains.Page); - /// - /// Gets the object used for manipulating DevTools Protocol targets. - /// - public override DevTools.Target Target => new V134Target(domains.Target); + /// + /// Gets the object used for manipulating DevTools Protocol targets. + /// + public override DevTools.Target Target => new V134Target(domains.Target); - /// - /// Gets the object used for manipulating the browser's logs. - /// - public override DevTools.Log Log => new V134Log(domains.Log); - } + /// + /// Gets the object used for manipulating the browser's logs. + /// + public override DevTools.Log Log => new V134Log(domains.Log); } diff --git a/dotnet/src/webdriver/DevTools/v134/V134JavaScript.cs b/dotnet/src/webdriver/DevTools/v134/V134JavaScript.cs index e989eb9c67a89..13d6d3d5618d3 100644 --- a/dotnet/src/webdriver/DevTools/v134/V134JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/v134/V134JavaScript.cs @@ -23,163 +23,162 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V134 +namespace OpenQA.Selenium.DevTools.V134; + +/// +/// Class containing the JavaScript implementation for version 134 of the DevTools Protocol. +/// +public class V134JavaScript : JavaScript { + private readonly RuntimeAdapter runtime; + private readonly PageAdapter page; + /// - /// Class containing the JavaScript implementation for version 134 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V134JavaScript : JavaScript + /// The DevTools Protocol adapter for the Runtime domain. + /// The DevTools Protocol adapter for the Page domain. + /// If or are . + public V134JavaScript(RuntimeAdapter runtime, PageAdapter page) { - private readonly RuntimeAdapter runtime; - private readonly PageAdapter page; - - /// - /// Initializes a new instance of the class. - /// - /// The DevTools Protocol adapter for the Runtime domain. - /// The DevTools Protocol adapter for the Page domain. - /// If or are . - public V134JavaScript(RuntimeAdapter runtime, PageAdapter page) - { - this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); - this.page = page ?? throw new ArgumentNullException(nameof(page)); - this.runtime.BindingCalled += OnRuntimeBindingCalled; - this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; - this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; - } + this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); + this.page = page ?? throw new ArgumentNullException(nameof(page)); + this.runtime.BindingCalled += OnRuntimeBindingCalled; + this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; + this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; + } - /// - /// Asynchronously enables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableRuntime() - { - await runtime.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableRuntime() + { + await runtime.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableRuntime() - { - await runtime.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableRuntime() + { + await runtime.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously enables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnablePage() - { - await page.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnablePage() + { + await page.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisablePage() - { - await page.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisablePage() + { + await page.Disable().ConfigureAwait(false); + } - /// - /// Adds a binding to a specific JavaScript name. - /// - /// The name to which to bind to. - /// A task that represents the asynchronous operation. - public override async Task AddBinding(string name) - { - await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Adds a binding to a specific JavaScript name. + /// + /// The name to which to bind to. + /// A task that represents the asynchronous operation. + public override async Task AddBinding(string name) + { + await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Removes a binding from a specific JavaScript name. - /// - /// The name to which to remove the bind from. - /// A task that represents the asynchronous operation. - public override async Task RemoveBinding(string name) - { - await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Removes a binding from a specific JavaScript name. + /// + /// The name to which to remove the bind from. + /// A task that represents the asynchronous operation. + public override async Task RemoveBinding(string name) + { + await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Adds a JavaScript snippet to evaluate when a new document is opened. - /// - /// The script to add to be evaluated when a new document is opened. - /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. - public override async Task AddScriptToEvaluateOnNewDocument(string script) - { - var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); - return result.Identifier; - } + /// + /// Adds a JavaScript snippet to evaluate when a new document is opened. + /// + /// The script to add to be evaluated when a new document is opened. + /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. + public override async Task AddScriptToEvaluateOnNewDocument(string script) + { + var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); + return result.Identifier; + } - /// - /// Removes a JavaScript snippet from evaluate when a new document is opened. - /// - /// The ID of the script to be removed. - /// A task that represents the asynchronous operation. - public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) - { - await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); - } + /// + /// Removes a JavaScript snippet from evaluate when a new document is opened. + /// + /// The ID of the script to be removed. + /// A task that represents the asynchronous operation. + public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) + { + await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); + } - /// - /// Evaluates a JavaScript snippet. It does not return a value. - /// - /// The script to evaluate - /// A task that represents the asynchronous operation. - /// - /// This method is internal to the operation of pinned scripts in Selenium, and - /// is therefore internal by design. - /// - internal override async Task Evaluate(string script) - { - await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); - } + /// + /// Evaluates a JavaScript snippet. It does not return a value. + /// + /// The script to evaluate + /// A task that represents the asynchronous operation. + /// + /// This method is internal to the operation of pinned scripts in Selenium, and + /// is therefore internal by design. + /// + internal override async Task Evaluate(string script) + { + await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); + } - private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) - { - BindingCalledEventArgs wrapped = new BindingCalledEventArgs - ( - executionContextId: e.ExecutionContextId, - name: e.Name, - payload: e.Payload - ); - - this.OnBindingCalled(wrapped); - } + private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) + { + BindingCalledEventArgs wrapped = new BindingCalledEventArgs + ( + executionContextId: e.ExecutionContextId, + name: e.Name, + payload: e.Payload + ); + + this.OnBindingCalled(wrapped); + } - private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) - { - // TODO: Collect stack trace elements - var wrapped = new ExceptionThrownEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - message: e.ExceptionDetails.Text - ); - - this.OnExceptionThrown(wrapped); - } + private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) + { + // TODO: Collect stack trace elements + var wrapped = new ExceptionThrownEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + message: e.ExceptionDetails.Text + ); + + this.OnExceptionThrown(wrapped); + } - private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + { + List args = new List(e.Args.Length); + foreach (var arg in e.Args) { - List args = new List(e.Args.Length); - foreach (var arg in e.Args) - { - string? argValue = arg.Value?.ToString(); - args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); - } - - var wrapped = new ConsoleApiCalledEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - type: e.Type, - arguments: args.AsReadOnly() - ); - - this.OnConsoleApiCalled(wrapped); + string? argValue = arg.Value?.ToString(); + args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); } + + var wrapped = new ConsoleApiCalledEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + type: e.Type, + arguments: args.AsReadOnly() + ); + + this.OnConsoleApiCalled(wrapped); } } diff --git a/dotnet/src/webdriver/DevTools/v134/V134Log.cs b/dotnet/src/webdriver/DevTools/v134/V134Log.cs index 1bdb8f93ab050..9ced92ed8a5f4 100644 --- a/dotnet/src/webdriver/DevTools/v134/V134Log.cs +++ b/dotnet/src/webdriver/DevTools/v134/V134Log.cs @@ -21,61 +21,60 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V134 +namespace OpenQA.Selenium.DevTools.V134; + +/// +/// Class containing the browser's log as referenced by version 134 of the DevTools Protocol. +/// +public class V134Log : DevTools.Log { + private LogAdapter adapter; + /// - /// Class containing the browser's log as referenced by version 134 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V134Log : DevTools.Log + /// The adapter for the Log domain. + /// If is . + public V134Log(LogAdapter adapter) { - private LogAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Log domain. - /// If is . - public V134Log(LogAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - this.adapter.EntryAdded += OnAdapterEntryAdded; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + this.adapter.EntryAdded += OnAdapterEntryAdded; + } - /// - /// Asynchronously enables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Enable() - { - await adapter.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Enable() + { + await adapter.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Disable() - { - await adapter.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Disable() + { + await adapter.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously clears the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Clear() - { - await adapter.Clear().ConfigureAwait(false); - } + /// + /// Asynchronously clears the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Clear() + { + await adapter.Clear().ConfigureAwait(false); + } - private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) - { - var entry = new LogEntry( - kind: e.Entry.Source.ToString(), - message: e.Entry.Text - ); + private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) + { + var entry = new LogEntry( + kind: e.Entry.Source.ToString(), + message: e.Entry.Text + ); - this.OnEntryAdded(new EntryAddedEventArgs(entry)); - } + this.OnEntryAdded(new EntryAddedEventArgs(entry)); } } diff --git a/dotnet/src/webdriver/DevTools/v134/V134Network.cs b/dotnet/src/webdriver/DevTools/v134/V134Network.cs index f211dc01d5036..3fa9bfa15ba54 100644 --- a/dotnet/src/webdriver/DevTools/v134/V134Network.cs +++ b/dotnet/src/webdriver/DevTools/v134/V134Network.cs @@ -24,364 +24,363 @@ using System.Text; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V134 +namespace OpenQA.Selenium.DevTools.V134; + +/// +/// Class providing functionality for manipulating network calls using version 134 of the DevTools Protocol +/// +public class V134Network : DevTools.Network { + private FetchAdapter fetch; + private NetworkAdapter network; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Network domain. + /// The adapter for the Fetch domain. + /// If or are . + public V134Network(NetworkAdapter network, FetchAdapter fetch) + { + this.network = network ?? throw new ArgumentNullException(nameof(network)); + this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); + fetch.AuthRequired += OnFetchAuthRequired; + fetch.RequestPaused += OnFetchRequestPaused; + } + /// - /// Class providing functionality for manipulating network calls using version 134 of the DevTools Protocol + /// Asynchronously disables network caching. /// - public class V134Network : DevTools.Network + /// A task that represents the asynchronous operation. + public override async Task DisableNetworkCaching() { - private FetchAdapter fetch; - private NetworkAdapter network; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Network domain. - /// The adapter for the Fetch domain. - /// If or are . - public V134Network(NetworkAdapter network, FetchAdapter fetch) + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables network caching. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetworkCaching() + { + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetwork() + { + await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + } + + /// + /// Asynchronously disables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableNetwork() + { + await network.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously enables the fetch domain for all URL patterns. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableFetchForAllPatterns() + { + await fetch.Enable(new Fetch.EnableCommandSettings() { - this.network = network ?? throw new ArgumentNullException(nameof(network)); - this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); - fetch.AuthRequired += OnFetchAuthRequired; - fetch.RequestPaused += OnFetchRequestPaused; - } + Patterns = new Fetch.RequestPattern[] + { + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } + }, + HandleAuthRequests = true + }).ConfigureAwait(false); + } - /// - /// Asynchronously disables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetworkCaching() + /// + /// Asynchronously disables the fetch domain. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableFetch() + { + await fetch.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously sets the override of the user agent settings. + /// + /// A object containing the user agent values to override. + /// A task that represents the asynchronous operation. + /// If is null. + public override async Task SetUserAgentOverride(UserAgent userAgent) + { + if (userAgent is null) { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(userAgent)); } - /// - /// Asynchronously enables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetworkCaching() + await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); - } + UserAgent = userAgent.UserAgentString, + AcceptLanguage = userAgent.AcceptLanguage, + Platform = userAgent.Platform + }).ConfigureAwait(false); + } - /// - /// Asynchronously enables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetwork() + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequest(HttpRequestData requestData) + { + if (requestData is null) { - await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously disables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetwork() + var commandSettings = new ContinueRequestCommandSettings() { - await network.Disable().ConfigureAwait(false); - } + RequestId = requestData.RequestId, + Method = requestData.Method, + Url = requestData.Url, + }; - /// - /// Asynchronously enables the fetch domain for all URL patterns. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableFetchForAllPatterns() + if (requestData.Headers?.Count > 0) { - await fetch.Enable(new Fetch.EnableCommandSettings() + List headers = new List(); + foreach (KeyValuePair headerPair in requestData.Headers) { - Patterns = new Fetch.RequestPattern[] - { - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } - }, - HandleAuthRequests = true - }).ConfigureAwait(false); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); + } + + commandSettings.Headers = headers.ToArray(); } - /// - /// Asynchronously disables the fetch domain. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableFetch() + if (!string.IsNullOrEmpty(requestData.PostData)) { - await fetch.Disable().ConfigureAwait(false); + commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); } - /// - /// Asynchronously sets the override of the user agent settings. - /// - /// A object containing the user agent values to override. - /// A task that represents the asynchronous operation. - /// If is null. - public override async Task SetUserAgentOverride(UserAgent userAgent) - { - if (userAgent is null) - { - throw new ArgumentNullException(nameof(userAgent)); - } + await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + } - await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() - { - UserAgent = userAgent.UserAgentString, - AcceptLanguage = userAgent.AcceptLanguage, - Platform = userAgent.Platform - }).ConfigureAwait(false); + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// The with which to respond to the request + /// A task that represents the asynchronous operation. + /// If or are . + public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequest(HttpRequestData requestData) + if (responseData is null) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } + throw new ArgumentNullException(nameof(responseData)); + } - var commandSettings = new ContinueRequestCommandSettings() - { - RequestId = requestData.RequestId, - Method = requestData.Method, - Url = requestData.Url, - }; + var commandSettings = new FulfillRequestCommandSettings() + { + RequestId = requestData.RequestId, + ResponseCode = responseData.StatusCode, + }; - if (requestData.Headers?.Count > 0) + if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) + { + List headers = new List(); + foreach (KeyValuePair headerPair in responseData.Headers) { - List headers = new List(); - foreach (KeyValuePair headerPair in requestData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } - - commandSettings.Headers = headers.ToArray(); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); } - if (!string.IsNullOrEmpty(requestData.PostData)) + foreach (string cookieHeader in responseData.CookieHeaders) { - commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); + headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); } - await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + commandSettings.ResponseHeaders = headers.ToArray(); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// The with which to respond to the request - /// A task that represents the asynchronous operation. - /// If or are . - public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + if (!string.IsNullOrEmpty(responseData.Body)) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } - - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - var commandSettings = new FulfillRequestCommandSettings() - { - RequestId = requestData.RequestId, - ResponseCode = responseData.StatusCode, - }; + commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + } - if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) - { - List headers = new List(); - foreach (KeyValuePair headerPair in responseData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } + await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); + } - foreach (string cookieHeader in responseData.CookieHeaders) - { - headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); - } + /// + /// Asynchronously continues an intercepted network call without modification. + /// + /// The of the network call. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); + } - commandSettings.ResponseHeaders = headers.ToArray(); - } + await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); + } - if (!string.IsNullOrEmpty(responseData.Body)) + /// + /// Asynchronously continues an intercepted network call using authentication. + /// + /// The ID of the network request for which to continue with authentication. + /// The user name with which to authenticate. + /// The password with which to authenticate. + /// A task that represents the asynchronous operation. + public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + { + RequestId = requestId, + AuthChallengeResponse = new V134.Fetch.AuthChallengeResponse() { - commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + Response = V134.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, + Username = userName, + Password = password } + }).ConfigureAwait(false); + } - await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call without modification. - /// - /// The of the network call. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + /// + /// Asynchronously cancels authorization of an intercepted network request. + /// + /// The ID of the network request for which to cancel authentication. + /// A task that represents the asynchronous operation. + public override async Task CancelAuth(string requestId) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() { - if (requestData is null) + RequestId = requestId, + AuthChallengeResponse = new OpenQA.Selenium.DevTools.V134.Fetch.AuthChallengeResponse() { - throw new ArgumentNullException(nameof(requestData)); + Response = V134.Fetch.AuthChallengeResponseResponseValues.CancelAuth } + }).ConfigureAwait(false); + } - await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call using authentication. - /// - /// The ID of the network request for which to continue with authentication. - /// The user name with which to authenticate. - /// The password with which to authenticate. - /// A task that represents the asynchronous operation. - public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + /// + /// Asynchronously adds the response body to the provided object. + /// + /// The object to which to add the response body. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task AddResponseBody(HttpResponseData responseData) + { + if (responseData is null) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() - { - RequestId = requestId, - AuthChallengeResponse = new V134.Fetch.AuthChallengeResponse() - { - Response = V134.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, - Username = userName, - Password = password - } - }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - /// - /// Asynchronously cancels authorization of an intercepted network request. - /// - /// The ID of the network request for which to cancel authentication. - /// A task that represents the asynchronous operation. - public override async Task CancelAuth(string requestId) + // If the response is a redirect, retrieving the body will throw an error in CDP. + if (responseData.StatusCode < 300 || responseData.StatusCode > 399) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + if (bodyResponse != null) { - RequestId = requestId, - AuthChallengeResponse = new OpenQA.Selenium.DevTools.V134.Fetch.AuthChallengeResponse() + if (bodyResponse.Base64Encoded) { - Response = V134.Fetch.AuthChallengeResponseResponseValues.CancelAuth + responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); } - }).ConfigureAwait(false); - } - - /// - /// Asynchronously adds the response body to the provided object. - /// - /// The object to which to add the response body. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task AddResponseBody(HttpResponseData responseData) - { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - // If the response is a redirect, retrieving the body will throw an error in CDP. - if (responseData.StatusCode < 300 || responseData.StatusCode > 399) - { - var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); - if (bodyResponse != null) + else { - if (bodyResponse.Base64Encoded) - { - responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); - } - else - { - responseData.Content = new HttpResponseContent(bodyResponse.Body); - } + responseData.Content = new HttpResponseContent(bodyResponse.Body); } } } + } - /// - /// Asynchronously continues an intercepted network response without modification. - /// - /// The of the network response. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + /// + /// Asynchronously continues an intercepted network response without modification. + /// + /// The of the network response. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + { + if (responseData is null) { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + } + + private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + { + AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs + ( + requestId: e.RequestId, + uri: e.Request.Url + ); + + this.OnAuthRequired(wrapped); + } + + private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + { + if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) { - AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs - ( - requestId: e.RequestId, - uri: e.Request.Url - ); + var requestData = new HttpRequestData + { + RequestId = e.RequestId, + Method = e.Request.Method, + Url = e.Request.Url, + PostData = e.Request.PostData, + Headers = new Dictionary(e.Request.Headers) + }; - this.OnAuthRequired(wrapped); + RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); + this.OnRequestPaused(wrapped); } - - private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + else { - if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) + var responseData = new HttpResponseData() { - var requestData = new HttpRequestData - { - RequestId = e.RequestId, - Method = e.Request.Method, - Url = e.Request.Url, - PostData = e.Request.PostData, - Headers = new Dictionary(e.Request.Headers) - }; - - RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); - this.OnRequestPaused(wrapped); - } - else + RequestId = e.RequestId, + Url = e.Request.Url, + ResourceType = e.ResourceType.ToString(), + StatusCode = e.ResponseStatusCode.GetValueOrDefault(), + ErrorReason = e.ResponseErrorReason?.ToString() + }; + + if (e.ResponseHeaders != null) { - var responseData = new HttpResponseData() + foreach (var header in e.ResponseHeaders) { - RequestId = e.RequestId, - Url = e.Request.Url, - ResourceType = e.ResourceType.ToString(), - StatusCode = e.ResponseStatusCode.GetValueOrDefault(), - ErrorReason = e.ResponseErrorReason?.ToString() - }; - - if (e.ResponseHeaders != null) - { - foreach (var header in e.ResponseHeaders) + if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) { - if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) + responseData.CookieHeaders.Add(header.Value); + } + else + { + if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) { - responseData.CookieHeaders.Add(header.Value); + responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; } else { - if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) - { - responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; - } - else - { - responseData.Headers.Add(header.Name, header.Value); - } + responseData.Headers.Add(header.Name, header.Value); } } } - - this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } + + this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } } } diff --git a/dotnet/src/webdriver/DevTools/v134/V134Target.cs b/dotnet/src/webdriver/DevTools/v134/V134Target.cs index f0c7a76164592..5df16cdc157d5 100644 --- a/dotnet/src/webdriver/DevTools/v134/V134Target.cs +++ b/dotnet/src/webdriver/DevTools/v134/V134Target.cs @@ -23,141 +23,140 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V134 +namespace OpenQA.Selenium.DevTools.V134; + +/// +/// Class providing functionality for manipulating targets for version 134 of the DevTools Protocol +/// +public class V134Target : DevTools.Target { + private readonly TargetAdapter adapter; + /// - /// Class providing functionality for manipulating targets for version 134 of the DevTools Protocol + /// Initializes a new instance of the class. /// - public class V134Target : DevTools.Target + /// The adapter for the Target domain. + /// If is . + public V134Target(TargetAdapter adapter) { - private readonly TargetAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Target domain. - /// If is . - public V134Target(TargetAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - adapter.DetachedFromTarget += OnDetachedFromTarget; - adapter.AttachedToTarget += OnAttachedToTarget; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + adapter.DetachedFromTarget += OnDetachedFromTarget; + adapter.AttachedToTarget += OnAttachedToTarget; + } - /// - /// Asynchronously gets the targets available for this session. - /// - /// - /// A task that represents the asynchronous operation. The task result - /// contains the list of objects describing the - /// targets available for this session. - /// - public override async Task> GetTargets(object? settings = null) - { - settings ??= new GetTargetsCommandSettings(); - - var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - - List targets = new List(response.TargetInfos.Length); - for (int i = 0; i < response.TargetInfos.Length; i++) - { - var targetInfo = response.TargetInfos[i]; - var mapped = new TargetInfo - ( - targetId: targetInfo.TargetId, - title: targetInfo.Title, - type: targetInfo.Type, - url: targetInfo.Url, - openerId: targetInfo.OpenerId, - browserContextId: targetInfo.BrowserContextId, - isAttached: targetInfo.Attached - ); - targets.Add(mapped); - } - - return targets.AsReadOnly(); - } + /// + /// Asynchronously gets the targets available for this session. + /// + /// + /// A task that represents the asynchronous operation. The task result + /// contains the list of objects describing the + /// targets available for this session. + /// + public override async Task> GetTargets(object? settings = null) + { + settings ??= new GetTargetsCommandSettings(); - /// - /// Asynchronously attaches to a target. - /// - /// The ID of the target to which to attach. - /// - /// A task representing the asynchronous attach operation. The task result contains the - /// session ID established for commands to the target attached to. - /// - public override async Task AttachToTarget(string targetId) - { - var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); - return result.SessionId; - } + var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - /// - /// Asynchronously detaches from a target. - /// - /// The ID of the session of the target from which to detach. - /// The ID of the target from which to detach. - /// A task representing the asynchronous detach operation. - public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + List targets = new List(response.TargetInfos.Length); + for (int i = 0; i < response.TargetInfos.Length; i++) { - await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() - { - SessionId = sessionId, - TargetId = targetId - }).ConfigureAwait(false); + var targetInfo = response.TargetInfos[i]; + var mapped = new TargetInfo + ( + targetId: targetInfo.TargetId, + title: targetInfo.Title, + type: targetInfo.Type, + url: targetInfo.Url, + openerId: targetInfo.OpenerId, + browserContextId: targetInfo.BrowserContextId, + isAttached: targetInfo.Attached + ); + targets.Add(mapped); } - /// - /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. - /// - /// A task that represents the asynchronous operation. - public override async Task SetAutoAttach() - { - await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); - } + return targets.AsReadOnly(); + } - private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) - { - this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); - } + /// + /// Asynchronously attaches to a target. + /// + /// The ID of the target to which to attach. + /// + /// A task representing the asynchronous attach operation. The task result contains the + /// session ID established for commands to the target attached to. + /// + public override async Task AttachToTarget(string targetId) + { + var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); + return result.SessionId; + } - private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + /// + /// Asynchronously detaches from a target. + /// + /// The ID of the session of the target from which to detach. + /// The ID of the target from which to detach. + /// A task representing the asynchronous detach operation. + public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + { + await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() { - var targetInfo = e.TargetInfo == null ? null : new TargetInfo - ( - browserContextId: e.TargetInfo.BrowserContextId, - isAttached: e.TargetInfo.Attached, - openerId: e.TargetInfo.OpenerId, - targetId: e.TargetInfo.TargetId, - title: e.TargetInfo.Title, - type: e.TargetInfo.Type, - url: e.TargetInfo.Url - ); + SessionId = sessionId, + TargetId = targetId + }).ConfigureAwait(false); + } - this.OnTargetAttached(new TargetAttachedEventArgs - ( - sessionId: e.SessionId, - targetInfo: targetInfo, - waitingForDebugger: e.WaitingForDebugger - )); - } + /// + /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. + /// + /// A task that represents the asynchronous operation. + public override async Task SetAutoAttach() + { + await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); + } - internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) + { + this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); + } + + private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + { + var targetInfo = e.TargetInfo == null ? null : new TargetInfo + ( + browserContextId: e.TargetInfo.BrowserContextId, + isAttached: e.TargetInfo.Attached, + openerId: e.TargetInfo.OpenerId, + targetId: e.TargetInfo.TargetId, + title: e.TargetInfo.Title, + type: e.TargetInfo.Type, + url: e.TargetInfo.Url + ); + + this.OnTargetAttached(new TargetAttachedEventArgs + ( + sessionId: e.SessionId, + targetInfo: targetInfo, + waitingForDebugger: e.WaitingForDebugger + )); + } + + internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + { + return new SetAutoAttachCommandSettings { - return new SetAutoAttachCommandSettings - { - AutoAttach = true, - Flatten = true, - WaitForDebuggerOnStart = waitForDebuggerOnStart - }; - } + AutoAttach = true, + Flatten = true, + WaitForDebuggerOnStart = waitForDebuggerOnStart + }; + } - internal override ICommand CreateDiscoverTargetsCommand() + internal override ICommand CreateDiscoverTargetsCommand() + { + return new SetDiscoverTargetsCommandSettings { - return new SetDiscoverTargetsCommandSettings - { - Discover = true - }; - } + Discover = true + }; } } diff --git a/dotnet/src/webdriver/DevTools/v135/V135Domains.cs b/dotnet/src/webdriver/DevTools/v135/V135Domains.cs index 039d960d1e0ab..fe69a098b8074 100644 --- a/dotnet/src/webdriver/DevTools/v135/V135Domains.cs +++ b/dotnet/src/webdriver/DevTools/v135/V135Domains.cs @@ -19,53 +19,52 @@ using System; -namespace OpenQA.Selenium.DevTools.V135 +namespace OpenQA.Selenium.DevTools.V135; + +/// +/// Class containing the domain implementation for version 135 of the DevTools Protocol. +/// +public class V135Domains : DevToolsDomains { + private readonly DevToolsSessionDomains domains; + /// - /// Class containing the domain implementation for version 135 of the DevTools Protocol. + /// Initializes a new instance of the V135Domains class. /// - public class V135Domains : DevToolsDomains + /// The DevToolsSession to use with this set of domains. + /// If is . + public V135Domains(DevToolsSession session) { - private readonly DevToolsSessionDomains domains; - - /// - /// Initializes a new instance of the V135Domains class. - /// - /// The DevToolsSession to use with this set of domains. - /// If is . - public V135Domains(DevToolsSession session) - { - this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); - } + this.domains = new DevToolsSessionDomains(session ?? throw new ArgumentNullException(nameof(session))); + } - /// - /// Gets the DevTools Protocol version for which this class is valid. - /// - public static int DevToolsVersion => 135; + /// + /// Gets the DevTools Protocol version for which this class is valid. + /// + public static int DevToolsVersion => 135; - /// - /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. - /// - public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; + /// + /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. + /// + public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; - /// - /// Gets the object used for manipulating network information in the browser. - /// - public override DevTools.Network Network => new V135Network(domains.Network, domains.Fetch); + /// + /// Gets the object used for manipulating network information in the browser. + /// + public override DevTools.Network Network => new V135Network(domains.Network, domains.Fetch); - /// - /// Gets the object used for manipulating the browser's JavaScript execution. - /// - public override JavaScript JavaScript => new V135JavaScript(domains.Runtime, domains.Page); + /// + /// Gets the object used for manipulating the browser's JavaScript execution. + /// + public override JavaScript JavaScript => new V135JavaScript(domains.Runtime, domains.Page); - /// - /// Gets the object used for manipulating DevTools Protocol targets. - /// - public override DevTools.Target Target => new V135Target(domains.Target); + /// + /// Gets the object used for manipulating DevTools Protocol targets. + /// + public override DevTools.Target Target => new V135Target(domains.Target); - /// - /// Gets the object used for manipulating the browser's logs. - /// - public override DevTools.Log Log => new V135Log(domains.Log); - } + /// + /// Gets the object used for manipulating the browser's logs. + /// + public override DevTools.Log Log => new V135Log(domains.Log); } diff --git a/dotnet/src/webdriver/DevTools/v135/V135JavaScript.cs b/dotnet/src/webdriver/DevTools/v135/V135JavaScript.cs index c0e8684ba8022..8f1cc5776f70b 100644 --- a/dotnet/src/webdriver/DevTools/v135/V135JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/v135/V135JavaScript.cs @@ -23,163 +23,162 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V135 +namespace OpenQA.Selenium.DevTools.V135; + +/// +/// Class containing the JavaScript implementation for version 135 of the DevTools Protocol. +/// +public class V135JavaScript : JavaScript { + private readonly RuntimeAdapter runtime; + private readonly PageAdapter page; + /// - /// Class containing the JavaScript implementation for version 135 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V135JavaScript : JavaScript + /// The DevTools Protocol adapter for the Runtime domain. + /// The DevTools Protocol adapter for the Page domain. + /// If or are . + public V135JavaScript(RuntimeAdapter runtime, PageAdapter page) { - private readonly RuntimeAdapter runtime; - private readonly PageAdapter page; - - /// - /// Initializes a new instance of the class. - /// - /// The DevTools Protocol adapter for the Runtime domain. - /// The DevTools Protocol adapter for the Page domain. - /// If or are . - public V135JavaScript(RuntimeAdapter runtime, PageAdapter page) - { - this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); - this.page = page ?? throw new ArgumentNullException(nameof(page)); - this.runtime.BindingCalled += OnRuntimeBindingCalled; - this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; - this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; - } + this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); + this.page = page ?? throw new ArgumentNullException(nameof(page)); + this.runtime.BindingCalled += OnRuntimeBindingCalled; + this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; + this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; + } - /// - /// Asynchronously enables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableRuntime() - { - await runtime.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableRuntime() + { + await runtime.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables the Runtime domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableRuntime() - { - await runtime.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableRuntime() + { + await runtime.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously enables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task EnablePage() - { - await page.Enable(new Page.EnableCommandSettings()).ConfigureAwait(false); - } + /// + /// Asynchronously enables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnablePage() + { + await page.Enable(new Page.EnableCommandSettings()).ConfigureAwait(false); + } - /// - /// Asynchronously disables the Page domain in the DevTools Protocol. - /// - /// A task that represents the asynchronous operation. - public override async Task DisablePage() - { - await page.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisablePage() + { + await page.Disable().ConfigureAwait(false); + } - /// - /// Adds a binding to a specific JavaScript name. - /// - /// The name to which to bind to. - /// A task that represents the asynchronous operation. - public override async Task AddBinding(string name) - { - await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Adds a binding to a specific JavaScript name. + /// + /// The name to which to bind to. + /// A task that represents the asynchronous operation. + public override async Task AddBinding(string name) + { + await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Removes a binding from a specific JavaScript name. - /// - /// The name to which to remove the bind from. - /// A task that represents the asynchronous operation. - public override async Task RemoveBinding(string name) - { - await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); - } + /// + /// Removes a binding from a specific JavaScript name. + /// + /// The name to which to remove the bind from. + /// A task that represents the asynchronous operation. + public override async Task RemoveBinding(string name) + { + await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }).ConfigureAwait(false); + } - /// - /// Adds a JavaScript snippet to evaluate when a new document is opened. - /// - /// The script to add to be evaluated when a new document is opened. - /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. - public override async Task AddScriptToEvaluateOnNewDocument(string script) - { - var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); - return result.Identifier; - } + /// + /// Adds a JavaScript snippet to evaluate when a new document is opened. + /// + /// The script to add to be evaluated when a new document is opened. + /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. + public override async Task AddScriptToEvaluateOnNewDocument(string script) + { + var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }).ConfigureAwait(false); + return result.Identifier; + } - /// - /// Removes a JavaScript snippet from evaluate when a new document is opened. - /// - /// The ID of the script to be removed. - /// A task that represents the asynchronous operation. - public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) - { - await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); - } + /// + /// Removes a JavaScript snippet from evaluate when a new document is opened. + /// + /// The ID of the script to be removed. + /// A task that represents the asynchronous operation. + public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) + { + await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }).ConfigureAwait(false); + } - /// - /// Evaluates a JavaScript snippet. It does not return a value. - /// - /// The script to evaluate - /// A task that represents the asynchronous operation. - /// - /// This method is internal to the operation of pinned scripts in Selenium, and - /// is therefore internal by design. - /// - internal override async Task Evaluate(string script) - { - await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); - } + /// + /// Evaluates a JavaScript snippet. It does not return a value. + /// + /// The script to evaluate + /// A task that represents the asynchronous operation. + /// + /// This method is internal to the operation of pinned scripts in Selenium, and + /// is therefore internal by design. + /// + internal override async Task Evaluate(string script) + { + await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }).ConfigureAwait(false); + } - private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) - { - BindingCalledEventArgs wrapped = new BindingCalledEventArgs - ( - executionContextId: e.ExecutionContextId, - name: e.Name, - payload: e.Payload - ); - - this.OnBindingCalled(wrapped); - } + private void OnRuntimeBindingCalled(object? sender, Runtime.BindingCalledEventArgs e) + { + BindingCalledEventArgs wrapped = new BindingCalledEventArgs + ( + executionContextId: e.ExecutionContextId, + name: e.Name, + payload: e.Payload + ); + + this.OnBindingCalled(wrapped); + } - private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) - { - // TODO: Collect stack trace elements - var wrapped = new ExceptionThrownEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - message: e.ExceptionDetails.Text - ); - - this.OnExceptionThrown(wrapped); - } + private void OnRuntimeExceptionThrown(object? sender, Runtime.ExceptionThrownEventArgs e) + { + // TODO: Collect stack trace elements + var wrapped = new ExceptionThrownEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + message: e.ExceptionDetails.Text + ); + + this.OnExceptionThrown(wrapped); + } - private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + private void OnRuntimeConsoleApiCalled(object? sender, ConsoleAPICalledEventArgs e) + { + List args = new List(e.Args.Length); + foreach (var arg in e.Args) { - List args = new List(e.Args.Length); - foreach (var arg in e.Args) - { - string? argValue = arg.Value?.ToString(); - args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); - } - - var wrapped = new ConsoleApiCalledEventArgs - ( - timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), - type: e.Type, - arguments: args.AsReadOnly() - ); - - this.OnConsoleApiCalled(wrapped); + string? argValue = arg.Value?.ToString(); + args.Add(new ConsoleApiArgument(arg.Type.ToString(), argValue)); } + + var wrapped = new ConsoleApiCalledEventArgs + ( + timestamp: new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + type: e.Type, + arguments: args.AsReadOnly() + ); + + this.OnConsoleApiCalled(wrapped); } } diff --git a/dotnet/src/webdriver/DevTools/v135/V135Log.cs b/dotnet/src/webdriver/DevTools/v135/V135Log.cs index 52677d055fe6a..f58890627da81 100644 --- a/dotnet/src/webdriver/DevTools/v135/V135Log.cs +++ b/dotnet/src/webdriver/DevTools/v135/V135Log.cs @@ -21,61 +21,60 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V135 +namespace OpenQA.Selenium.DevTools.V135; + +/// +/// Class containing the browser's log as referenced by version 135 of the DevTools Protocol. +/// +public class V135Log : DevTools.Log { + private LogAdapter adapter; + /// - /// Class containing the browser's log as referenced by version 135 of the DevTools Protocol. + /// Initializes a new instance of the class. /// - public class V135Log : DevTools.Log + /// The adapter for the Log domain. + /// If is . + public V135Log(LogAdapter adapter) { - private LogAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Log domain. - /// If is . - public V135Log(LogAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - this.adapter.EntryAdded += OnAdapterEntryAdded; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + this.adapter.EntryAdded += OnAdapterEntryAdded; + } - /// - /// Asynchronously enables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Enable() - { - await adapter.Enable().ConfigureAwait(false); - } + /// + /// Asynchronously enables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Enable() + { + await adapter.Enable().ConfigureAwait(false); + } - /// - /// Asynchronously disables manipulation of the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Disable() - { - await adapter.Disable().ConfigureAwait(false); - } + /// + /// Asynchronously disables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Disable() + { + await adapter.Disable().ConfigureAwait(false); + } - /// - /// Asynchronously clears the browser's log. - /// - /// A task that represents the asynchronous operation. - public override async Task Clear() - { - await adapter.Clear().ConfigureAwait(false); - } + /// + /// Asynchronously clears the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Clear() + { + await adapter.Clear().ConfigureAwait(false); + } - private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) - { - var entry = new LogEntry( - kind: e.Entry.Source.ToString(), - message: e.Entry.Text - ); + private void OnAdapterEntryAdded(object? sender, Log.EntryAddedEventArgs e) + { + var entry = new LogEntry( + kind: e.Entry.Source.ToString(), + message: e.Entry.Text + ); - this.OnEntryAdded(new EntryAddedEventArgs(entry)); - } + this.OnEntryAdded(new EntryAddedEventArgs(entry)); } } diff --git a/dotnet/src/webdriver/DevTools/v135/V135Network.cs b/dotnet/src/webdriver/DevTools/v135/V135Network.cs index 8526a9336e86a..d01128d823d68 100644 --- a/dotnet/src/webdriver/DevTools/v135/V135Network.cs +++ b/dotnet/src/webdriver/DevTools/v135/V135Network.cs @@ -24,364 +24,363 @@ using System.Text; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V135 +namespace OpenQA.Selenium.DevTools.V135; + +/// +/// Class providing functionality for manipulating network calls using version 135 of the DevTools Protocol +/// +public class V135Network : DevTools.Network { + private FetchAdapter fetch; + private NetworkAdapter network; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Network domain. + /// The adapter for the Fetch domain. + /// If or are . + public V135Network(NetworkAdapter network, FetchAdapter fetch) + { + this.network = network ?? throw new ArgumentNullException(nameof(network)); + this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); + fetch.AuthRequired += OnFetchAuthRequired; + fetch.RequestPaused += OnFetchRequestPaused; + } + /// - /// Class providing functionality for manipulating network calls using version 135 of the DevTools Protocol + /// Asynchronously disables network caching. /// - public class V135Network : DevTools.Network + /// A task that represents the asynchronous operation. + public override async Task DisableNetworkCaching() { - private FetchAdapter fetch; - private NetworkAdapter network; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Network domain. - /// The adapter for the Fetch domain. - /// If or are . - public V135Network(NetworkAdapter network, FetchAdapter fetch) + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables network caching. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetworkCaching() + { + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); + } + + /// + /// Asynchronously enables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetwork() + { + await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + } + + /// + /// Asynchronously disables the Network domain.. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableNetwork() + { + await network.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously enables the fetch domain for all URL patterns. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableFetchForAllPatterns() + { + await fetch.Enable(new Fetch.EnableCommandSettings() { - this.network = network ?? throw new ArgumentNullException(nameof(network)); - this.fetch = fetch ?? throw new ArgumentNullException(nameof(fetch)); - fetch.AuthRequired += OnFetchAuthRequired; - fetch.RequestPaused += OnFetchRequestPaused; - } + Patterns = new Fetch.RequestPattern[] + { + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, + new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } + }, + HandleAuthRequests = true + }).ConfigureAwait(false); + } - /// - /// Asynchronously disables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetworkCaching() + /// + /// Asynchronously disables the fetch domain. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableFetch() + { + await fetch.Disable().ConfigureAwait(false); + } + + /// + /// Asynchronously sets the override of the user agent settings. + /// + /// A object containing the user agent values to override. + /// A task that represents the asynchronous operation. + /// If is null. + public override async Task SetUserAgentOverride(UserAgent userAgent) + { + if (userAgent is null) { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(userAgent)); } - /// - /// Asynchronously enables network caching. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetworkCaching() + await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() { - await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }).ConfigureAwait(false); - } + UserAgent = userAgent.UserAgentString, + AcceptLanguage = userAgent.AcceptLanguage, + Platform = userAgent.Platform + }).ConfigureAwait(false); + } - /// - /// Asynchronously enables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableNetwork() + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequest(HttpRequestData requestData) + { + if (requestData is null) { - await network.Enable(new Network.EnableCommandSettings()).ConfigureAwait(false); + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously disables the Network domain.. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableNetwork() + var commandSettings = new ContinueRequestCommandSettings() { - await network.Disable().ConfigureAwait(false); - } + RequestId = requestData.RequestId, + Method = requestData.Method, + Url = requestData.Url, + }; - /// - /// Asynchronously enables the fetch domain for all URL patterns. - /// - /// A task that represents the asynchronous operation. - public override async Task EnableFetchForAllPatterns() + if (requestData.Headers?.Count > 0) { - await fetch.Enable(new Fetch.EnableCommandSettings() + List headers = new List(); + foreach (KeyValuePair headerPair in requestData.Headers) { - Patterns = new Fetch.RequestPattern[] - { - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, - new Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } - }, - HandleAuthRequests = true - }).ConfigureAwait(false); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); + } + + commandSettings.Headers = headers.ToArray(); } - /// - /// Asynchronously disables the fetch domain. - /// - /// A task that represents the asynchronous operation. - public override async Task DisableFetch() + if (!string.IsNullOrEmpty(requestData.PostData)) { - await fetch.Disable().ConfigureAwait(false); + commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); } - /// - /// Asynchronously sets the override of the user agent settings. - /// - /// A object containing the user agent values to override. - /// A task that represents the asynchronous operation. - /// If is null. - public override async Task SetUserAgentOverride(UserAgent userAgent) - { - if (userAgent is null) - { - throw new ArgumentNullException(nameof(userAgent)); - } + await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + } - await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() - { - UserAgent = userAgent.UserAgentString, - AcceptLanguage = userAgent.AcceptLanguage, - Platform = userAgent.Platform - }).ConfigureAwait(false); + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// The with which to respond to the request + /// A task that represents the asynchronous operation. + /// If or are . + public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequest(HttpRequestData requestData) + if (responseData is null) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } + throw new ArgumentNullException(nameof(responseData)); + } - var commandSettings = new ContinueRequestCommandSettings() - { - RequestId = requestData.RequestId, - Method = requestData.Method, - Url = requestData.Url, - }; + var commandSettings = new FulfillRequestCommandSettings() + { + RequestId = requestData.RequestId, + ResponseCode = responseData.StatusCode, + }; - if (requestData.Headers?.Count > 0) + if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) + { + List headers = new List(); + foreach (KeyValuePair headerPair in responseData.Headers) { - List headers = new List(); - foreach (KeyValuePair headerPair in requestData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } - - commandSettings.Headers = headers.ToArray(); + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); } - if (!string.IsNullOrEmpty(requestData.PostData)) + foreach (string cookieHeader in responseData.CookieHeaders) { - commandSettings.PostData = Convert.ToBase64String(Encoding.UTF8.GetBytes(requestData.PostData)); + headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); } - await fetch.ContinueRequest(commandSettings).ConfigureAwait(false); + commandSettings.ResponseHeaders = headers.ToArray(); } - /// - /// Asynchronously continues an intercepted network request. - /// - /// The of the request. - /// The with which to respond to the request - /// A task that represents the asynchronous operation. - /// If or are . - public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + if (!string.IsNullOrEmpty(responseData.Body)) { - if (requestData is null) - { - throw new ArgumentNullException(nameof(requestData)); - } - - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - var commandSettings = new FulfillRequestCommandSettings() - { - RequestId = requestData.RequestId, - ResponseCode = responseData.StatusCode, - }; + commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + } - if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) - { - List headers = new List(); - foreach (KeyValuePair headerPair in responseData.Headers) - { - headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); - } + await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); + } - foreach (string cookieHeader in responseData.CookieHeaders) - { - headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); - } + /// + /// Asynchronously continues an intercepted network call without modification. + /// + /// The of the network call. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + { + if (requestData is null) + { + throw new ArgumentNullException(nameof(requestData)); + } - commandSettings.ResponseHeaders = headers.ToArray(); - } + await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); + } - if (!string.IsNullOrEmpty(responseData.Body)) + /// + /// Asynchronously continues an intercepted network call using authentication. + /// + /// The ID of the network request for which to continue with authentication. + /// The user name with which to authenticate. + /// The password with which to authenticate. + /// A task that represents the asynchronous operation. + public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + { + RequestId = requestId, + AuthChallengeResponse = new V135.Fetch.AuthChallengeResponse() { - commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + Response = V135.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, + Username = userName, + Password = password } + }).ConfigureAwait(false); + } - await fetch.FulfillRequest(commandSettings).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call without modification. - /// - /// The of the network call. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + /// + /// Asynchronously cancels authorization of an intercepted network request. + /// + /// The ID of the network request for which to cancel authentication. + /// A task that represents the asynchronous operation. + public override async Task CancelAuth(string requestId) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() { - if (requestData is null) + RequestId = requestId, + AuthChallengeResponse = new OpenQA.Selenium.DevTools.V135.Fetch.AuthChallengeResponse() { - throw new ArgumentNullException(nameof(requestData)); + Response = V135.Fetch.AuthChallengeResponseResponseValues.CancelAuth } + }).ConfigureAwait(false); + } - await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }).ConfigureAwait(false); - } - - /// - /// Asynchronously continues an intercepted network call using authentication. - /// - /// The ID of the network request for which to continue with authentication. - /// The user name with which to authenticate. - /// The password with which to authenticate. - /// A task that represents the asynchronous operation. - public override async Task ContinueWithAuth(string requestId, string? userName, string? password) + /// + /// Asynchronously adds the response body to the provided object. + /// + /// The object to which to add the response body. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task AddResponseBody(HttpResponseData responseData) + { + if (responseData is null) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() - { - RequestId = requestId, - AuthChallengeResponse = new V135.Fetch.AuthChallengeResponse() - { - Response = V135.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, - Username = userName, - Password = password - } - }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - /// - /// Asynchronously cancels authorization of an intercepted network request. - /// - /// The ID of the network request for which to cancel authentication. - /// A task that represents the asynchronous operation. - public override async Task CancelAuth(string requestId) + // If the response is a redirect, retrieving the body will throw an error in CDP. + if (responseData.StatusCode < 300 || responseData.StatusCode > 399) { - await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + if (bodyResponse != null) { - RequestId = requestId, - AuthChallengeResponse = new OpenQA.Selenium.DevTools.V135.Fetch.AuthChallengeResponse() + if (bodyResponse.Base64Encoded) { - Response = V135.Fetch.AuthChallengeResponseResponseValues.CancelAuth + responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); } - }).ConfigureAwait(false); - } - - /// - /// Asynchronously adds the response body to the provided object. - /// - /// The object to which to add the response body. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task AddResponseBody(HttpResponseData responseData) - { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - // If the response is a redirect, retrieving the body will throw an error in CDP. - if (responseData.StatusCode < 300 || responseData.StatusCode > 399) - { - var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); - if (bodyResponse != null) + else { - if (bodyResponse.Base64Encoded) - { - responseData.Content = new HttpResponseContent(Convert.FromBase64String(bodyResponse.Body)); - } - else - { - responseData.Content = new HttpResponseContent(bodyResponse.Body); - } + responseData.Content = new HttpResponseContent(bodyResponse.Body); } } } + } - /// - /// Asynchronously continues an intercepted network response without modification. - /// - /// The of the network response. - /// A task that represents the asynchronous operation. - /// If is . - public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + /// + /// Asynchronously continues an intercepted network response without modification. + /// + /// The of the network response. + /// A task that represents the asynchronous operation. + /// If is . + public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + { + if (responseData is null) { - if (responseData is null) - { - throw new ArgumentNullException(nameof(responseData)); - } - - await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + throw new ArgumentNullException(nameof(responseData)); } - private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + await fetch.ContinueResponse(new ContinueResponseCommandSettings() { RequestId = responseData.RequestId }).ConfigureAwait(false); + } + + private void OnFetchAuthRequired(object? sender, Fetch.AuthRequiredEventArgs e) + { + AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs + ( + requestId: e.RequestId, + uri: e.Request.Url + ); + + this.OnAuthRequired(wrapped); + } + + private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + { + if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) { - AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs - ( - requestId: e.RequestId, - uri: e.Request.Url - ); + var requestData = new HttpRequestData + { + RequestId = e.RequestId, + Method = e.Request.Method, + Url = e.Request.Url, + PostData = e.Request.PostData, + Headers = new Dictionary(e.Request.Headers) + }; - this.OnAuthRequired(wrapped); + RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); + this.OnRequestPaused(wrapped); } - - private void OnFetchRequestPaused(object? sender, Fetch.RequestPausedEventArgs e) + else { - if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) + var responseData = new HttpResponseData() { - var requestData = new HttpRequestData - { - RequestId = e.RequestId, - Method = e.Request.Method, - Url = e.Request.Url, - PostData = e.Request.PostData, - Headers = new Dictionary(e.Request.Headers) - }; - - RequestPausedEventArgs wrapped = new RequestPausedEventArgs(null, requestData); - this.OnRequestPaused(wrapped); - } - else + RequestId = e.RequestId, + Url = e.Request.Url, + ResourceType = e.ResourceType.ToString(), + StatusCode = e.ResponseStatusCode.GetValueOrDefault(), + ErrorReason = e.ResponseErrorReason?.ToString() + }; + + if (e.ResponseHeaders != null) { - var responseData = new HttpResponseData() + foreach (var header in e.ResponseHeaders) { - RequestId = e.RequestId, - Url = e.Request.Url, - ResourceType = e.ResourceType.ToString(), - StatusCode = e.ResponseStatusCode.GetValueOrDefault(), - ErrorReason = e.ResponseErrorReason?.ToString() - }; - - if (e.ResponseHeaders != null) - { - foreach (var header in e.ResponseHeaders) + if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) { - if (header.Name.Equals("set-cookie", StringComparison.InvariantCultureIgnoreCase)) + responseData.CookieHeaders.Add(header.Value); + } + else + { + if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) { - responseData.CookieHeaders.Add(header.Value); + responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; } else { - if (responseData.Headers.TryGetValue(header.Name, out string? currentHeaderValue)) - { - responseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; - } - else - { - responseData.Headers.Add(header.Name, header.Value); - } + responseData.Headers.Add(header.Name, header.Value); } } } - - this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } + + this.OnResponsePaused(new ResponsePausedEventArgs(responseData)); } } } diff --git a/dotnet/src/webdriver/DevTools/v135/V135Target.cs b/dotnet/src/webdriver/DevTools/v135/V135Target.cs index 984b511125093..bcfd9fde4bd2d 100644 --- a/dotnet/src/webdriver/DevTools/v135/V135Target.cs +++ b/dotnet/src/webdriver/DevTools/v135/V135Target.cs @@ -23,141 +23,140 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V135 +namespace OpenQA.Selenium.DevTools.V135; + +/// +/// Class providing functionality for manipulating targets for version 135 of the DevTools Protocol +/// +public class V135Target : DevTools.Target { + private readonly TargetAdapter adapter; + /// - /// Class providing functionality for manipulating targets for version 135 of the DevTools Protocol + /// Initializes a new instance of the class. /// - public class V135Target : DevTools.Target + /// The adapter for the Target domain. + /// If is . + public V135Target(TargetAdapter adapter) { - private readonly TargetAdapter adapter; - - /// - /// Initializes a new instance of the class. - /// - /// The adapter for the Target domain. - /// If is . - public V135Target(TargetAdapter adapter) - { - this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); - adapter.DetachedFromTarget += OnDetachedFromTarget; - adapter.AttachedToTarget += OnAttachedToTarget; - } + this.adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); + adapter.DetachedFromTarget += OnDetachedFromTarget; + adapter.AttachedToTarget += OnAttachedToTarget; + } - /// - /// Asynchronously gets the targets available for this session. - /// - /// - /// A task that represents the asynchronous operation. The task result - /// contains the list of objects describing the - /// targets available for this session. - /// - public override async Task> GetTargets(object? settings = null) - { - settings ??= new GetTargetsCommandSettings(); - - var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - - List targets = new List(response.TargetInfos.Length); - for (int i = 0; i < response.TargetInfos.Length; i++) - { - var targetInfo = response.TargetInfos[i]; - var mapped = new TargetInfo - ( - targetId: targetInfo.TargetId, - title: targetInfo.Title, - type: targetInfo.Type, - url: targetInfo.Url, - openerId: targetInfo.OpenerId, - browserContextId: targetInfo.BrowserContextId, - isAttached: targetInfo.Attached - ); - targets.Add(mapped); - } - - return targets.AsReadOnly(); - } + /// + /// Asynchronously gets the targets available for this session. + /// + /// + /// A task that represents the asynchronous operation. The task result + /// contains the list of objects describing the + /// targets available for this session. + /// + public override async Task> GetTargets(object? settings = null) + { + settings ??= new GetTargetsCommandSettings(); - /// - /// Asynchronously attaches to a target. - /// - /// The ID of the target to which to attach. - /// - /// A task representing the asynchronous attach operation. The task result contains the - /// session ID established for commands to the target attached to. - /// - public override async Task AttachToTarget(string targetId) - { - var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); - return result.SessionId; - } + var response = await adapter.GetTargets((GetTargetsCommandSettings)settings).ConfigureAwait(false); - /// - /// Asynchronously detaches from a target. - /// - /// The ID of the session of the target from which to detach. - /// The ID of the target from which to detach. - /// A task representing the asynchronous detach operation. - public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + List targets = new List(response.TargetInfos.Length); + for (int i = 0; i < response.TargetInfos.Length; i++) { - await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() - { - SessionId = sessionId, - TargetId = targetId - }).ConfigureAwait(false); + var targetInfo = response.TargetInfos[i]; + var mapped = new TargetInfo + ( + targetId: targetInfo.TargetId, + title: targetInfo.Title, + type: targetInfo.Type, + url: targetInfo.Url, + openerId: targetInfo.OpenerId, + browserContextId: targetInfo.BrowserContextId, + isAttached: targetInfo.Attached + ); + targets.Add(mapped); } - /// - /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. - /// - /// A task that represents the asynchronous operation. - public override async Task SetAutoAttach() - { - await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); - } + return targets.AsReadOnly(); + } - private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) - { - this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); - } + /// + /// Asynchronously attaches to a target. + /// + /// The ID of the target to which to attach. + /// + /// A task representing the asynchronous attach operation. The task result contains the + /// session ID established for commands to the target attached to. + /// + public override async Task AttachToTarget(string targetId) + { + var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }).ConfigureAwait(false); + return result.SessionId; + } - private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + /// + /// Asynchronously detaches from a target. + /// + /// The ID of the session of the target from which to detach. + /// The ID of the target from which to detach. + /// A task representing the asynchronous detach operation. + public override async Task DetachFromTarget(string? sessionId = null, string? targetId = null) + { + await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() { - var targetInfo = e.TargetInfo == null ? null : new TargetInfo - ( - browserContextId: e.TargetInfo.BrowserContextId, - isAttached: e.TargetInfo.Attached, - openerId: e.TargetInfo.OpenerId, - targetId: e.TargetInfo.TargetId, - title: e.TargetInfo.Title, - type: e.TargetInfo.Type, - url: e.TargetInfo.Url - ); + SessionId = sessionId, + TargetId = targetId + }).ConfigureAwait(false); + } - this.OnTargetAttached(new TargetAttachedEventArgs - ( - sessionId: e.SessionId, - targetInfo: targetInfo, - waitingForDebugger: e.WaitingForDebugger - )); - } + /// + /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. + /// + /// A task that represents the asynchronous operation. + public override async Task SetAutoAttach() + { + await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }).ConfigureAwait(false); + } - internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + private void OnDetachedFromTarget(object? sender, DetachedFromTargetEventArgs e) + { + this.OnTargetDetached(new TargetDetachedEventArgs(e.SessionId, e.TargetId)); + } + + private void OnAttachedToTarget(object? sender, AttachedToTargetEventArgs e) + { + var targetInfo = e.TargetInfo == null ? null : new TargetInfo + ( + browserContextId: e.TargetInfo.BrowserContextId, + isAttached: e.TargetInfo.Attached, + openerId: e.TargetInfo.OpenerId, + targetId: e.TargetInfo.TargetId, + title: e.TargetInfo.Title, + type: e.TargetInfo.Type, + url: e.TargetInfo.Url + ); + + this.OnTargetAttached(new TargetAttachedEventArgs + ( + sessionId: e.SessionId, + targetInfo: targetInfo, + waitingForDebugger: e.WaitingForDebugger + )); + } + + internal override ICommand CreateSetAutoAttachCommand(bool waitForDebuggerOnStart) + { + return new SetAutoAttachCommandSettings { - return new SetAutoAttachCommandSettings - { - AutoAttach = true, - Flatten = true, - WaitForDebuggerOnStart = waitForDebuggerOnStart - }; - } + AutoAttach = true, + Flatten = true, + WaitForDebuggerOnStart = waitForDebuggerOnStart + }; + } - internal override ICommand CreateDiscoverTargetsCommand() + internal override ICommand CreateDiscoverTargetsCommand() + { + return new SetDiscoverTargetsCommandSettings { - return new SetDiscoverTargetsCommandSettings - { - Discover = true - }; - } + Discover = true + }; } } diff --git a/dotnet/src/webdriver/DomMutatedEventArgs.cs b/dotnet/src/webdriver/DomMutatedEventArgs.cs index 3ee853a0c36a4..f5600e4c81bea 100644 --- a/dotnet/src/webdriver/DomMutatedEventArgs.cs +++ b/dotnet/src/webdriver/DomMutatedEventArgs.cs @@ -19,21 +19,20 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the AttributeValueChanged event +/// +public class DomMutatedEventArgs : EventArgs { - /// - /// Provides data for the AttributeValueChanged event - /// - public class DomMutatedEventArgs : EventArgs + internal DomMutatedEventArgs(DomMutationData attributeData) { - internal DomMutatedEventArgs(DomMutationData attributeData) - { - AttributeData = attributeData; - } - - /// - /// Gets the data about the attribute being changed. - /// - public DomMutationData AttributeData { get; } + AttributeData = attributeData; } + + /// + /// Gets the data about the attribute being changed. + /// + public DomMutationData AttributeData { get; } } diff --git a/dotnet/src/webdriver/DomMutationData.cs b/dotnet/src/webdriver/DomMutationData.cs index 9cd60bef69d24..25773d514074a 100644 --- a/dotnet/src/webdriver/DomMutationData.cs +++ b/dotnet/src/webdriver/DomMutationData.cs @@ -19,54 +19,53 @@ using System.Text.Json.Serialization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data about the changes in the value of an attribute on an element. +/// +public class DomMutationData { /// - /// Provides data about the changes in the value of an attribute on an element. + /// Gets the ID of the element whose value is changing. /// - public class DomMutationData - { - /// - /// Gets the ID of the element whose value is changing. - /// - [JsonPropertyName("target")] - [JsonInclude] - public string TargetId { get; internal set; } = null!; + [JsonPropertyName("target")] + [JsonInclude] + public string TargetId { get; internal set; } = null!; - /// - /// Gets the name of the attribute that is changing. - /// - [JsonPropertyName("name")] - [JsonInclude] - public string AttributeName { get; internal set; } = null!; + /// + /// Gets the name of the attribute that is changing. + /// + [JsonPropertyName("name")] + [JsonInclude] + public string AttributeName { get; internal set; } = null!; - /// - /// Gets the value to which the attribute is being changed. - /// - [JsonPropertyName("value")] - [JsonInclude] - public string AttributeValue { get; internal set; } = null!; + /// + /// Gets the value to which the attribute is being changed. + /// + [JsonPropertyName("value")] + [JsonInclude] + public string AttributeValue { get; internal set; } = null!; - /// - /// Gets the value from which the attribute has been changed. - /// - [JsonPropertyName("oldValue")] - [JsonInclude] - public string AttributeOriginalValue { get; internal set; } = null!; + /// + /// Gets the value from which the attribute has been changed. + /// + [JsonPropertyName("oldValue")] + [JsonInclude] + public string AttributeOriginalValue { get; internal set; } = null!; - /// - /// Stores the element associated with the target ID, if any. - /// - [JsonIgnore] - public IWebElement? Element { get; internal set; } + /// + /// Stores the element associated with the target ID, if any. + /// + [JsonIgnore] + public IWebElement? Element { get; internal set; } - /// - /// Returns a string that represents the current object. - /// - /// A string that represents the current object. - public override string ToString() - { - return string.Format("target: {0}, name: {1}, value: {2}, originalValue: {3}", this.TargetId, this.AttributeName, this.AttributeValue, this.AttributeOriginalValue); - } + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return string.Format("target: {0}, name: {1}, value: {2}, originalValue: {3}", this.TargetId, this.AttributeName, this.AttributeValue, this.AttributeOriginalValue); } } diff --git a/dotnet/src/webdriver/DriverCommand.cs b/dotnet/src/webdriver/DriverCommand.cs index 9d91b5b3bc71c..f5fc45f85e9a1 100644 --- a/dotnet/src/webdriver/DriverCommand.cs +++ b/dotnet/src/webdriver/DriverCommand.cs @@ -19,475 +19,474 @@ using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Values describing the list of commands understood by a remote server using the JSON wire protocol. +/// +public static class DriverCommand { /// - /// Values describing the list of commands understood by a remote server using the JSON wire protocol. + /// Represents the Status command. + /// + public static readonly string Status = "status"; + + /// + /// Represents a New Session command + /// + public static readonly string NewSession = "newSession"; + + /// + /// Represents a Browser close command + /// + public static readonly string Close = "close"; + + /// + /// Represents a browser quit command + /// + public static readonly string Quit = "quit"; + + /// + /// Represents a GET command + /// + public static readonly string Get = "get"; + + /// + /// Represents a Browser going back command + /// + public static readonly string GoBack = "goBack"; + + /// + /// Represents a Browser going forward command + /// + public static readonly string GoForward = "goForward"; + + /// + /// Represents a Browser refreshing command + /// + public static readonly string Refresh = "refresh"; + + /// + /// Represents adding a cookie command + /// + public static readonly string AddCookie = "addCookie"; + + /// + /// Represents getting all cookies command + /// + public static readonly string GetAllCookies = "getCookies"; + + /// + /// Represents getting cookie command + /// + public static readonly string GetCookie = "getCookie"; + + /// + /// Represents deleting a cookie command + /// + public static readonly string DeleteCookie = "deleteCookie"; + + /// + /// Represents Deleting all cookies command + /// + public static readonly string DeleteAllCookies = "deleteAllCookies"; + + /// + /// Represents FindElement command + /// + public static readonly string FindElement = "findElement"; + + /// + /// Represents FindElements command + /// + public static readonly string FindElements = "findElements"; + + /// + /// Represents FindChildElement command + /// + public static readonly string FindChildElement = "findChildElement"; + + /// + /// Represents FindChildElements command + /// + public static readonly string FindChildElements = "findChildElements"; + + /// + /// Represents FindShadowChildElement command + /// + public static readonly string FindShadowChildElement = "findShadowChildElement"; + + /// + /// Represents FindShadowChildElements command + /// + public static readonly string FindShadowChildElements = "findShadowChildElements"; + + /// + /// Represents ClearElement command + /// + public static readonly string ClearElement = "clearElement"; + + /// + /// Represents ClickElement command + /// + public static readonly string ClickElement = "clickElement"; + + /// + /// Represents SendKeysToElements command + /// + public static readonly string SendKeysToElement = "sendKeysToElement"; + + /// + /// Represents GetCurrentWindowHandle command + /// + public static readonly string GetCurrentWindowHandle = "getCurrentWindowHandle"; + + /// + /// Represents GetWindowHandles command + /// + public static readonly string GetWindowHandles = "getWindowHandles"; + + /// + /// Represents SwitchToWindow command + /// + public static readonly string SwitchToWindow = "switchToWindow"; + + /// + /// Represents NewWindow command + /// + public static readonly string NewWindow = "newWindow"; + + /// + /// Represents SwitchToFrame command + /// + public static readonly string SwitchToFrame = "switchToFrame"; + + /// + /// Represents SwitchToParentFrame command + /// + public static readonly string SwitchToParentFrame = "switchToParentFrame"; + + /// + /// Represents GetActiveElement command + /// + public static readonly string GetActiveElement = "getActiveElement"; + + /// + /// Represents GetCurrentUrl command + /// + public static readonly string GetCurrentUrl = "getCurrentUrl"; + + /// + /// Represents GetPageSource command + /// + public static readonly string GetPageSource = "getPageSource"; + + /// + /// Represents GetTitle command + /// + public static readonly string GetTitle = "getTitle"; + + /// + /// Represents ExecuteScript command + /// + public static readonly string ExecuteScript = "executeScript"; + + /// + /// Represents ExecuteAsyncScript command + /// + public static readonly string ExecuteAsyncScript = "executeAsyncScript"; + + /// + /// Represents GetElementText command + /// + public static readonly string GetElementText = "getElementText"; + + /// + /// Represents GetElementTagName command + /// + public static readonly string GetElementTagName = "getElementTagName"; + + /// + /// Represents IsElementSelected command + /// + public static readonly string IsElementSelected = "isElementSelected"; + + /// + /// Represents IsElementEnabled command + /// + public static readonly string IsElementEnabled = "isElementEnabled"; + + /// + /// Represents IsElementDisplayed command + /// + public static readonly string IsElementDisplayed = "isElementDisplayed"; + + /// + /// Represents GetElementRect command + /// + public static readonly string GetElementRect = "getElementRect"; + + /// + /// Represents GetElementAttribute command + /// + public static readonly string GetElementAttribute = "getElementAttribute"; + + /// + /// Represents GetElementProperty command + /// + public static readonly string GetElementProperty = "getElementProperty"; + + /// + /// Represents GetElementValueOfCSSProperty command + /// + public static readonly string GetElementValueOfCssProperty = "getElementValueOfCssProperty"; + + /// + /// Represents GetComputedAccessibleLabel command + /// + public static readonly string GetComputedAccessibleLabel = "getComputedAccessibleLabel"; + + /// + /// Represents GetComputedAccessibleRole command + /// + public static readonly string GetComputedAccessibleRole = "getComputedAccessibleRole"; + + /// + /// Represents the GetElementShadowRoot command. + /// + public static readonly string GetElementShadowRoot = "getElementShadowRoot"; + + /// + /// Represents ElementEquals command + /// + public static readonly string ElementEquals = "elementEquals"; + + /// + /// Represents Screenshot command + /// + public static readonly string Screenshot = "screenshot"; + + /// + /// Represents the ElementScreenshot command + /// + public static readonly string ElementScreenshot = "elementScreenshot"; + + /// + /// Represents the Print command + /// + public static readonly string Print = "print"; + + /// + /// Represents GetWindowRect command + /// + public static readonly string GetWindowRect = "getWindowRect"; + + /// + /// Represents SetWindowRect command + /// + public static readonly string SetWindowRect = "setWindowRect"; + + /// + /// Represents MaximizeWindow command + /// + public static readonly string MaximizeWindow = "maximizeWindow"; + + /// + /// Represents MinimizeWindow command + /// + public static readonly string MinimizeWindow = "minimizeWindow"; + + /// + /// Represents FullScreenWindow command + /// + public static readonly string FullScreenWindow = "fullScreenWindow"; + + /// + /// Represents the DismissAlert command + /// + public static readonly string DismissAlert = "dismissAlert"; + + /// + /// Represents the AcceptAlert command + /// + public static readonly string AcceptAlert = "acceptAlert"; + + /// + /// Represents the GetAlertText command + /// + public static readonly string GetAlertText = "getAlertText"; + + /// + /// Represents the SetAlertValue command + /// + public static readonly string SetAlertValue = "setAlertValue"; + + /// + /// Represents the SetTimeout command + /// + public static readonly string SetTimeouts = "setTimeouts"; + + /// + /// Represents the SetTimeout command + /// + public static readonly string GetTimeouts = "getTimeouts"; + + /// + /// Represents the Actions command. + /// + public static readonly string Actions = "actions"; + + /// + /// Represents the CancelActions command. + /// + public static readonly string CancelActions = "cancelActions"; + + /// + /// Represents the UploadFile command. + /// + public static readonly string UploadFile = "uploadFile"; + + /// + /// Represents the GetAvailableLogTypes command. + /// + public static readonly string GetAvailableLogTypes = "getAvailableLogTypes"; + + /// + /// Represents the GetLog command. + /// + public static readonly string GetLog = "getLog"; + + // Virtual Authenticator API + + // http://w3c.github.io/webauthn#sctn-automation + /// + /// Represents the AddVirtualAuthenticator command. + /// + public static readonly string AddVirtualAuthenticator = "addVirtualAuthenticator"; + + /// + /// Represents the RemoveVirtualAuthenticator command. + /// + public static readonly string RemoveVirtualAuthenticator = "removeVirtualAuthenticator"; + + /// + /// Represents the AddCredential command + /// + public static readonly string AddCredential = "addCredential"; + + /// + /// Represents the GetCredentials command. + /// + public static readonly string GetCredentials = "getCredentials"; + + /// + /// Represents the RemoveCredential command. + /// + public static readonly string RemoveCredential = "removeCredential"; + + /// + /// Represents the RemoveAllCredentials command. + /// + public static readonly string RemoveAllCredentials = "removeAllCredentials"; + + /// + /// Represents the SetUserVerified command. + /// + public static readonly string SetUserVerified = "setUserVerified"; + + /// + /// Represents the GetDownloadableFiles command. + /// + public static readonly string GetDownloadableFiles = "getDownloadableFiles"; + + /// + /// Represents the DownloadFile command. + /// + public static readonly string DownloadFile = "downloadFile"; + + /// + /// Represents the DeleteDownloadableFiles command. + /// + public static readonly string DeleteDownloadableFiles = "deleteDownloadableFiles"; + + /// + /// Lists the set of known commands valid for the Selenium library. /// - public static class DriverCommand + public static readonly IList KnownCommands = new List() { - /// - /// Represents the Status command. - /// - public static readonly string Status = "status"; - - /// - /// Represents a New Session command - /// - public static readonly string NewSession = "newSession"; - - /// - /// Represents a Browser close command - /// - public static readonly string Close = "close"; - - /// - /// Represents a browser quit command - /// - public static readonly string Quit = "quit"; - - /// - /// Represents a GET command - /// - public static readonly string Get = "get"; - - /// - /// Represents a Browser going back command - /// - public static readonly string GoBack = "goBack"; - - /// - /// Represents a Browser going forward command - /// - public static readonly string GoForward = "goForward"; - - /// - /// Represents a Browser refreshing command - /// - public static readonly string Refresh = "refresh"; - - /// - /// Represents adding a cookie command - /// - public static readonly string AddCookie = "addCookie"; - - /// - /// Represents getting all cookies command - /// - public static readonly string GetAllCookies = "getCookies"; - - /// - /// Represents getting cookie command - /// - public static readonly string GetCookie = "getCookie"; - - /// - /// Represents deleting a cookie command - /// - public static readonly string DeleteCookie = "deleteCookie"; - - /// - /// Represents Deleting all cookies command - /// - public static readonly string DeleteAllCookies = "deleteAllCookies"; - - /// - /// Represents FindElement command - /// - public static readonly string FindElement = "findElement"; - - /// - /// Represents FindElements command - /// - public static readonly string FindElements = "findElements"; - - /// - /// Represents FindChildElement command - /// - public static readonly string FindChildElement = "findChildElement"; - - /// - /// Represents FindChildElements command - /// - public static readonly string FindChildElements = "findChildElements"; - - /// - /// Represents FindShadowChildElement command - /// - public static readonly string FindShadowChildElement = "findShadowChildElement"; - - /// - /// Represents FindShadowChildElements command - /// - public static readonly string FindShadowChildElements = "findShadowChildElements"; - - /// - /// Represents ClearElement command - /// - public static readonly string ClearElement = "clearElement"; - - /// - /// Represents ClickElement command - /// - public static readonly string ClickElement = "clickElement"; - - /// - /// Represents SendKeysToElements command - /// - public static readonly string SendKeysToElement = "sendKeysToElement"; - - /// - /// Represents GetCurrentWindowHandle command - /// - public static readonly string GetCurrentWindowHandle = "getCurrentWindowHandle"; - - /// - /// Represents GetWindowHandles command - /// - public static readonly string GetWindowHandles = "getWindowHandles"; - - /// - /// Represents SwitchToWindow command - /// - public static readonly string SwitchToWindow = "switchToWindow"; - - /// - /// Represents NewWindow command - /// - public static readonly string NewWindow = "newWindow"; - - /// - /// Represents SwitchToFrame command - /// - public static readonly string SwitchToFrame = "switchToFrame"; - - /// - /// Represents SwitchToParentFrame command - /// - public static readonly string SwitchToParentFrame = "switchToParentFrame"; - - /// - /// Represents GetActiveElement command - /// - public static readonly string GetActiveElement = "getActiveElement"; - - /// - /// Represents GetCurrentUrl command - /// - public static readonly string GetCurrentUrl = "getCurrentUrl"; - - /// - /// Represents GetPageSource command - /// - public static readonly string GetPageSource = "getPageSource"; - - /// - /// Represents GetTitle command - /// - public static readonly string GetTitle = "getTitle"; - - /// - /// Represents ExecuteScript command - /// - public static readonly string ExecuteScript = "executeScript"; - - /// - /// Represents ExecuteAsyncScript command - /// - public static readonly string ExecuteAsyncScript = "executeAsyncScript"; - - /// - /// Represents GetElementText command - /// - public static readonly string GetElementText = "getElementText"; - - /// - /// Represents GetElementTagName command - /// - public static readonly string GetElementTagName = "getElementTagName"; - - /// - /// Represents IsElementSelected command - /// - public static readonly string IsElementSelected = "isElementSelected"; - - /// - /// Represents IsElementEnabled command - /// - public static readonly string IsElementEnabled = "isElementEnabled"; - - /// - /// Represents IsElementDisplayed command - /// - public static readonly string IsElementDisplayed = "isElementDisplayed"; - - /// - /// Represents GetElementRect command - /// - public static readonly string GetElementRect = "getElementRect"; - - /// - /// Represents GetElementAttribute command - /// - public static readonly string GetElementAttribute = "getElementAttribute"; - - /// - /// Represents GetElementProperty command - /// - public static readonly string GetElementProperty = "getElementProperty"; - - /// - /// Represents GetElementValueOfCSSProperty command - /// - public static readonly string GetElementValueOfCssProperty = "getElementValueOfCssProperty"; - - /// - /// Represents GetComputedAccessibleLabel command - /// - public static readonly string GetComputedAccessibleLabel = "getComputedAccessibleLabel"; - - /// - /// Represents GetComputedAccessibleRole command - /// - public static readonly string GetComputedAccessibleRole = "getComputedAccessibleRole"; - - /// - /// Represents the GetElementShadowRoot command. - /// - public static readonly string GetElementShadowRoot = "getElementShadowRoot"; - - /// - /// Represents ElementEquals command - /// - public static readonly string ElementEquals = "elementEquals"; - - /// - /// Represents Screenshot command - /// - public static readonly string Screenshot = "screenshot"; - - /// - /// Represents the ElementScreenshot command - /// - public static readonly string ElementScreenshot = "elementScreenshot"; - - /// - /// Represents the Print command - /// - public static readonly string Print = "print"; - - /// - /// Represents GetWindowRect command - /// - public static readonly string GetWindowRect = "getWindowRect"; - - /// - /// Represents SetWindowRect command - /// - public static readonly string SetWindowRect = "setWindowRect"; - - /// - /// Represents MaximizeWindow command - /// - public static readonly string MaximizeWindow = "maximizeWindow"; - - /// - /// Represents MinimizeWindow command - /// - public static readonly string MinimizeWindow = "minimizeWindow"; - - /// - /// Represents FullScreenWindow command - /// - public static readonly string FullScreenWindow = "fullScreenWindow"; - - /// - /// Represents the DismissAlert command - /// - public static readonly string DismissAlert = "dismissAlert"; - - /// - /// Represents the AcceptAlert command - /// - public static readonly string AcceptAlert = "acceptAlert"; - - /// - /// Represents the GetAlertText command - /// - public static readonly string GetAlertText = "getAlertText"; - - /// - /// Represents the SetAlertValue command - /// - public static readonly string SetAlertValue = "setAlertValue"; - - /// - /// Represents the SetTimeout command - /// - public static readonly string SetTimeouts = "setTimeouts"; - - /// - /// Represents the SetTimeout command - /// - public static readonly string GetTimeouts = "getTimeouts"; - - /// - /// Represents the Actions command. - /// - public static readonly string Actions = "actions"; - - /// - /// Represents the CancelActions command. - /// - public static readonly string CancelActions = "cancelActions"; - - /// - /// Represents the UploadFile command. - /// - public static readonly string UploadFile = "uploadFile"; - - /// - /// Represents the GetAvailableLogTypes command. - /// - public static readonly string GetAvailableLogTypes = "getAvailableLogTypes"; - - /// - /// Represents the GetLog command. - /// - public static readonly string GetLog = "getLog"; - - // Virtual Authenticator API - - // http://w3c.github.io/webauthn#sctn-automation - /// - /// Represents the AddVirtualAuthenticator command. - /// - public static readonly string AddVirtualAuthenticator = "addVirtualAuthenticator"; - - /// - /// Represents the RemoveVirtualAuthenticator command. - /// - public static readonly string RemoveVirtualAuthenticator = "removeVirtualAuthenticator"; - - /// - /// Represents the AddCredential command - /// - public static readonly string AddCredential = "addCredential"; - - /// - /// Represents the GetCredentials command. - /// - public static readonly string GetCredentials = "getCredentials"; - - /// - /// Represents the RemoveCredential command. - /// - public static readonly string RemoveCredential = "removeCredential"; - - /// - /// Represents the RemoveAllCredentials command. - /// - public static readonly string RemoveAllCredentials = "removeAllCredentials"; - - /// - /// Represents the SetUserVerified command. - /// - public static readonly string SetUserVerified = "setUserVerified"; - - /// - /// Represents the GetDownloadableFiles command. - /// - public static readonly string GetDownloadableFiles = "getDownloadableFiles"; - - /// - /// Represents the DownloadFile command. - /// - public static readonly string DownloadFile = "downloadFile"; - - /// - /// Represents the DeleteDownloadableFiles command. - /// - public static readonly string DeleteDownloadableFiles = "deleteDownloadableFiles"; - - /// - /// Lists the set of known commands valid for the Selenium library. - /// - public static readonly IList KnownCommands = new List() - { - Status, - NewSession, - Quit, - GetTimeouts, - SetTimeouts, - Get, - GetCurrentUrl, - GoBack, - GoForward, - Refresh, - GetTitle, - GetCurrentWindowHandle, - Close, - SwitchToWindow, - GetWindowHandles, - SwitchToFrame, - SwitchToParentFrame, - GetWindowRect, - SetWindowRect, - MaximizeWindow, - MinimizeWindow, - FullScreenWindow, - FindElement, - FindElements, - FindChildElement, - FindChildElements, - FindShadowChildElement, - FindShadowChildElements, - GetActiveElement, - GetElementShadowRoot, - IsElementSelected, - GetElementAttribute, - GetElementProperty, - GetElementValueOfCssProperty, - GetElementText, - GetElementTagName, - GetElementRect, - IsElementEnabled, - GetComputedAccessibleRole, - GetComputedAccessibleLabel, - ClickElement, - ClearElement, - SendKeysToElement, - GetPageSource, - ExecuteScript, - ExecuteAsyncScript, - GetAllCookies, - GetCookie, - AddCookie, - DeleteCookie, - DeleteAllCookies, - Actions, - CancelActions, - AcceptAlert, - DismissAlert, - GetAlertText, - SetAlertValue, - Screenshot, - ElementScreenshot, - Print, - IsElementDisplayed, - UploadFile, - GetLog, - GetAvailableLogTypes, - AddVirtualAuthenticator, - RemoveVirtualAuthenticator, - AddCredential, - GetCredentials, - RemoveCredential, - RemoveAllCredentials, - SetUserVerified, - GetDownloadableFiles, - DownloadFile, - DeleteDownloadableFiles - }.AsReadOnly(); - } + Status, + NewSession, + Quit, + GetTimeouts, + SetTimeouts, + Get, + GetCurrentUrl, + GoBack, + GoForward, + Refresh, + GetTitle, + GetCurrentWindowHandle, + Close, + SwitchToWindow, + GetWindowHandles, + SwitchToFrame, + SwitchToParentFrame, + GetWindowRect, + SetWindowRect, + MaximizeWindow, + MinimizeWindow, + FullScreenWindow, + FindElement, + FindElements, + FindChildElement, + FindChildElements, + FindShadowChildElement, + FindShadowChildElements, + GetActiveElement, + GetElementShadowRoot, + IsElementSelected, + GetElementAttribute, + GetElementProperty, + GetElementValueOfCssProperty, + GetElementText, + GetElementTagName, + GetElementRect, + IsElementEnabled, + GetComputedAccessibleRole, + GetComputedAccessibleLabel, + ClickElement, + ClearElement, + SendKeysToElement, + GetPageSource, + ExecuteScript, + ExecuteAsyncScript, + GetAllCookies, + GetCookie, + AddCookie, + DeleteCookie, + DeleteAllCookies, + Actions, + CancelActions, + AcceptAlert, + DismissAlert, + GetAlertText, + SetAlertValue, + Screenshot, + ElementScreenshot, + Print, + IsElementDisplayed, + UploadFile, + GetLog, + GetAvailableLogTypes, + AddVirtualAuthenticator, + RemoveVirtualAuthenticator, + AddCredential, + GetCredentials, + RemoveCredential, + RemoveAllCredentials, + SetUserVerified, + GetDownloadableFiles, + DownloadFile, + DeleteDownloadableFiles + }.AsReadOnly(); } diff --git a/dotnet/src/webdriver/DriverFinder.cs b/dotnet/src/webdriver/DriverFinder.cs index e4afdca09382a..89e00b86006db 100644 --- a/dotnet/src/webdriver/DriverFinder.cs +++ b/dotnet/src/webdriver/DriverFinder.cs @@ -24,151 +24,150 @@ using System.IO; using System.Text; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Finds a driver, checks if the provided path exists, if not, Selenium Manager is used. +/// This implementation is still in beta and may change. +/// +public class DriverFinder { + private readonly DriverOptions options; + private Dictionary paths = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// If is . + public DriverFinder(DriverOptions options) + { + this.options = options ?? throw new ArgumentNullException(nameof(options)); + } + + /// + /// Gets the browser path retrieved by Selenium Manager + /// + /// + /// The full browser path + /// + public string GetBrowserPath() + { + return BinaryPaths()[SeleniumManager.BrowserPathKey]; + } + + /// + /// Gets the driver path retrieved by Selenium Manager + /// + /// + /// The full driver path + /// + public string GetDriverPath() + { + return BinaryPaths()[SeleniumManager.DriverPathKey]; + } + + /// + /// Gets whether there is a browser path for the given browser on this platform. + /// + /// if a browser path exists; otherwise, . + public bool HasBrowserPath() + { + return !string.IsNullOrWhiteSpace(GetBrowserPath()); + } + /// - /// Finds a driver, checks if the provided path exists, if not, Selenium Manager is used. - /// This implementation is still in beta and may change. + /// Tries to get the browser path, as retrieved by Selenium Manager. /// - public class DriverFinder + /// If the method returns , the full browser path. + /// if a browser path exists; otherwise, . + public bool TryGetBrowserPath([NotNullWhen(true)] out string? browserPath) { - private readonly DriverOptions options; - private Dictionary paths = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// If is . - public DriverFinder(DriverOptions options) + string? path = GetBrowserPath(); + if (!string.IsNullOrWhiteSpace(path)) { - this.options = options ?? throw new ArgumentNullException(nameof(options)); + browserPath = path; + return true; } - /// - /// Gets the browser path retrieved by Selenium Manager - /// - /// - /// The full browser path - /// - public string GetBrowserPath() + browserPath = null; + return false; + } + + /// + /// Invokes Selenium Manager to get the binaries paths and validates if they exist. + /// + /// + /// A Dictionary with the validated browser and driver path. + /// + /// If one of the paths does not exist. + private Dictionary BinaryPaths() + { + if (paths.ContainsKey(SeleniumManager.DriverPathKey) && !string.IsNullOrWhiteSpace(paths[SeleniumManager.DriverPathKey])) { - return BinaryPaths()[SeleniumManager.BrowserPathKey]; + return paths; } - /// - /// Gets the driver path retrieved by Selenium Manager - /// - /// - /// The full driver path - /// - public string GetDriverPath() + Dictionary binaryPaths = SeleniumManager.BinaryPaths(CreateArguments()); + string driverPath = binaryPaths[SeleniumManager.DriverPathKey]; + string browserPath = binaryPaths[SeleniumManager.BrowserPathKey]; + + if (File.Exists(driverPath)) { - return BinaryPaths()[SeleniumManager.DriverPathKey]; + paths.Add(SeleniumManager.DriverPathKey, driverPath); } - - /// - /// Gets whether there is a browser path for the given browser on this platform. - /// - /// if a browser path exists; otherwise, . - public bool HasBrowserPath() + else { - return !string.IsNullOrWhiteSpace(GetBrowserPath()); + throw new NoSuchDriverException($"The driver path is not a valid file: {driverPath}"); } - /// - /// Tries to get the browser path, as retrieved by Selenium Manager. - /// - /// If the method returns , the full browser path. - /// if a browser path exists; otherwise, . - public bool TryGetBrowserPath([NotNullWhen(true)] out string? browserPath) + if (File.Exists(browserPath)) { - string? path = GetBrowserPath(); - if (!string.IsNullOrWhiteSpace(path)) - { - browserPath = path; - return true; - } - - browserPath = null; - return false; + paths.Add(SeleniumManager.BrowserPathKey, browserPath); } - - /// - /// Invokes Selenium Manager to get the binaries paths and validates if they exist. - /// - /// - /// A Dictionary with the validated browser and driver path. - /// - /// If one of the paths does not exist. - private Dictionary BinaryPaths() + else { - if (paths.ContainsKey(SeleniumManager.DriverPathKey) && !string.IsNullOrWhiteSpace(paths[SeleniumManager.DriverPathKey])) - { - return paths; - } - - Dictionary binaryPaths = SeleniumManager.BinaryPaths(CreateArguments()); - string driverPath = binaryPaths[SeleniumManager.DriverPathKey]; - string browserPath = binaryPaths[SeleniumManager.BrowserPathKey]; + throw new NoSuchDriverException($"The browser path is not a valid file: {browserPath}"); + } - if (File.Exists(driverPath)) - { - paths.Add(SeleniumManager.DriverPathKey, driverPath); - } - else - { - throw new NoSuchDriverException($"The driver path is not a valid file: {driverPath}"); - } + return paths; + } - if (File.Exists(browserPath)) - { - paths.Add(SeleniumManager.BrowserPathKey, browserPath); - } - else - { - throw new NoSuchDriverException($"The browser path is not a valid file: {browserPath}"); - } + /// + /// Create arguments to invoke Selenium Manager + /// + /// + /// A string with all arguments to invoke Selenium Manager + /// + /// + private string CreateArguments() + { + StringBuilder argsBuilder = new StringBuilder(); + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser \"{0}\"", options.BrowserName); - return paths; + if (!string.IsNullOrEmpty(options.BrowserVersion)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-version {0}", options.BrowserVersion); } - /// - /// Create arguments to invoke Selenium Manager - /// - /// - /// A string with all arguments to invoke Selenium Manager - /// - /// - private string CreateArguments() + string? browserBinary = options.BinaryLocation; + if (!string.IsNullOrEmpty(browserBinary)) { - StringBuilder argsBuilder = new StringBuilder(); - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser \"{0}\"", options.BrowserName); - - if (!string.IsNullOrEmpty(options.BrowserVersion)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-version {0}", options.BrowserVersion); - } + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-path \"{0}\"", browserBinary); + } - string? browserBinary = options.BinaryLocation; - if (!string.IsNullOrEmpty(browserBinary)) + if (options.Proxy != null) + { + if (options.Proxy.SslProxy != null) { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-path \"{0}\"", browserBinary); + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.SslProxy); } - - if (options.Proxy != null) + else if (options.Proxy.HttpProxy != null) { - if (options.Proxy.SslProxy != null) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.SslProxy); - } - else if (options.Proxy.HttpProxy != null) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.HttpProxy); - } + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.HttpProxy); } - - return argsBuilder.ToString(); } + return argsBuilder.ToString(); } + } diff --git a/dotnet/src/webdriver/DriverOptions.cs b/dotnet/src/webdriver/DriverOptions.cs index d47f62374f04a..483ceb03130a4 100644 --- a/dotnet/src/webdriver/DriverOptions.cs +++ b/dotnet/src/webdriver/DriverOptions.cs @@ -24,547 +24,546 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Specifies the behavior of handling unexpected alerts in the IE driver. +/// +public enum UnhandledPromptBehavior { /// - /// Specifies the behavior of handling unexpected alerts in the IE driver. + /// 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. +/// +public enum PageLoadStrategy +{ + /// + /// Indicates the behavior is not set. + /// + Default, + + /// + /// Waits for pages to load and ready state to be 'complete'. + /// + Normal, + + /// + /// Waits for pages to load and for ready state to be 'interactive' or 'complete'. /// - public enum UnhandledPromptBehavior + Eager, + + /// + /// Does not wait for pages to load, returning immediately. + /// + None +} + +internal class Timeout +{ + public TimeSpan? Script { get; set; } + public TimeSpan? PageLoad { get; set; } + public TimeSpan? ImplicitWait { get; set; } + + public Dictionary ToCapabilities() { - /// - /// 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 + var timeoutCapabilities = new Dictionary(); + + if (Script.HasValue) timeoutCapabilities.Add("script", Script.Value.TotalMilliseconds); + if (PageLoad.HasValue) timeoutCapabilities.Add("pageLoad", PageLoad.Value.TotalMilliseconds); + if (ImplicitWait.HasValue) timeoutCapabilities.Add("implicit", ImplicitWait.Value.TotalMilliseconds); + + return timeoutCapabilities; } +} + +/// +/// Base class for managing options specific to a browser driver. +/// +public abstract class DriverOptions +{ + private readonly Dictionary additionalCapabilities = new Dictionary(); + private readonly Dictionary loggingPreferences = new Dictionary(); + private readonly Dictionary knownCapabilityNames = new Dictionary(); /// - /// Specifies the behavior of waiting for page loads in the driver. + /// Initializes a new instance of the class. /// - public enum PageLoadStrategy + protected DriverOptions() { - /// - /// Indicates the behavior is not set. - /// - Default, - - /// - /// Waits for pages to load and ready state to be 'complete'. - /// - Normal, - - /// - /// Waits for pages to load and for ready state to be 'interactive' or 'complete'. - /// - Eager, - - /// - /// Does not wait for pages to load, returning immediately. - /// - None + this.AddKnownCapabilityName(CapabilityType.BrowserName, "BrowserName property"); + this.AddKnownCapabilityName(CapabilityType.BrowserVersion, "BrowserVersion property"); + this.AddKnownCapabilityName(CapabilityType.PlatformName, "PlatformName property"); + this.AddKnownCapabilityName(CapabilityType.Proxy, "Proxy property"); + this.AddKnownCapabilityName(CapabilityType.UnhandledPromptBehavior, "UnhandledPromptBehavior property"); + this.AddKnownCapabilityName(CapabilityType.PageLoadStrategy, "PageLoadStrategy property"); + this.AddKnownCapabilityName(CapabilityType.UseStrictFileInteractability, "UseStrictFileInteractability property"); + this.AddKnownCapabilityName(CapabilityType.WebSocketUrl, "UseWebSocketUrl property"); + this.AddKnownCapabilityName(CapabilityType.EnableDownloads, "EnableDownloads property"); } - internal class Timeout - { - public TimeSpan? Script { get; set; } - public TimeSpan? PageLoad { get; set; } - public TimeSpan? ImplicitWait { get; set; } + /// + /// Gets or sets the name of the browser. + /// + public string? BrowserName { get; protected set; } - public Dictionary ToCapabilities() - { - var timeoutCapabilities = new Dictionary(); + /// + /// Gets or sets the version of the browser. + /// + public string? BrowserVersion { get; set; } - if (Script.HasValue) timeoutCapabilities.Add("script", Script.Value.TotalMilliseconds); - if (PageLoad.HasValue) timeoutCapabilities.Add("pageLoad", PageLoad.Value.TotalMilliseconds); - if (ImplicitWait.HasValue) timeoutCapabilities.Add("implicit", ImplicitWait.Value.TotalMilliseconds); + /// + /// Gets or sets the name of the platform on which the browser is running. + /// + public string? PlatformName { get; set; } - return timeoutCapabilities; - } + /// + /// Gets or sets a value indicating whether the browser should accept self-signed + /// SSL certificates. + /// + public bool? AcceptInsecureCertificates { get; set; } + + /// + /// Gets or sets a value indicating whether the driver should request a URL to + /// a WebSocket to be used for bidirectional communication. + /// + public bool? UseWebSocketUrl { get; set; } + + /// + /// 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; + + /// + /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. + /// Defaults to . + /// + public PageLoadStrategy PageLoadStrategy { get; set; } = PageLoadStrategy.Default; + + /// + /// Gets or sets the to be used with this browser. + /// + public Proxy? Proxy { get; set; } + + /// + /// Gets or sets a value indicating whether <input type='file'/> elements + /// must be visible to allow uploading of files. + /// + public bool UseStrictFileInteractability { get; set; } + + /// + /// Gets or sets a value indicating whether files may be downloaded from remote node. + /// + public bool? EnableDownloads { get; set; } + + /// + /// Gets or sets the asynchronous script timeout, which is the amount + /// of time the driver should wait when executing JavaScript asynchronously. + /// This timeout only affects the + /// method. + /// + public TimeSpan? ScriptTimeout { get; set; } + + /// + /// Gets or sets the page load timeout, which is the amount of time the driver + /// should wait for a page to load when setting the + /// property. + /// + public TimeSpan? PageLoadTimeout { get; set; } + + /// + /// Gets or sets the implicit wait timeout, which is the amount of time the + /// driver should wait when searching for an element if it is not immediately + /// present. + /// + /// + /// When searching for a single element, the driver should poll the page + /// until the element has been found, or this timeout expires before throwing + /// a . When searching for multiple elements, + /// the driver should poll the page until at least one element has been found + /// or this timeout has expired. + /// + /// Increasing the implicit wait timeout should be used judiciously as it + /// will have an adverse effect on test run time, especially when used with + /// slower location strategies like XPath. + /// + /// + public TimeSpan? ImplicitWaitTimeout { get; set; } + + /// + /// Set or Get the location of the browser + /// Override in subclass + /// + public virtual string? BinaryLocation + { + get => null; + set => throw new NotImplementedException(); } /// - /// Base class for managing options specific to a browser driver. + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the specific browser driver. /// - public abstract class DriverOptions + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// + public virtual void AddAdditionalOption(string optionName, object optionValue) { - private readonly Dictionary additionalCapabilities = new Dictionary(); - private readonly Dictionary loggingPreferences = new Dictionary(); - private readonly Dictionary knownCapabilityNames = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - protected DriverOptions() + this.ValidateCapabilityName(optionName); + this.additionalCapabilities[optionName] = optionValue; + } + + /// + /// Returns the for the specific browser driver with these + /// options included as capabilities. This does not copy the options. Further + /// changes will be reflected in the returned capabilities. + /// + /// The for browser driver with these options. + public abstract ICapabilities ToCapabilities(); + + /// + /// Compares this object with another to see if there + /// are merge conflicts between them. + /// + /// The object to compare with. + /// A object containing the status of the attempted merge. + /// If is . + public virtual DriverOptionsMergeResult GetMergeResult(DriverOptions other) + { + if (other is null) { - this.AddKnownCapabilityName(CapabilityType.BrowserName, "BrowserName property"); - this.AddKnownCapabilityName(CapabilityType.BrowserVersion, "BrowserVersion property"); - this.AddKnownCapabilityName(CapabilityType.PlatformName, "PlatformName property"); - this.AddKnownCapabilityName(CapabilityType.Proxy, "Proxy property"); - this.AddKnownCapabilityName(CapabilityType.UnhandledPromptBehavior, "UnhandledPromptBehavior property"); - this.AddKnownCapabilityName(CapabilityType.PageLoadStrategy, "PageLoadStrategy property"); - this.AddKnownCapabilityName(CapabilityType.UseStrictFileInteractability, "UseStrictFileInteractability property"); - this.AddKnownCapabilityName(CapabilityType.WebSocketUrl, "UseWebSocketUrl property"); - this.AddKnownCapabilityName(CapabilityType.EnableDownloads, "EnableDownloads property"); + throw new ArgumentNullException(nameof(other)); } - /// - /// Gets or sets the name of the browser. - /// - public string? BrowserName { get; protected set; } - - /// - /// Gets or sets the version of the browser. - /// - public string? BrowserVersion { get; set; } - - /// - /// Gets or sets the name of the platform on which the browser is running. - /// - public string? PlatformName { get; set; } - - /// - /// Gets or sets a value indicating whether the browser should accept self-signed - /// SSL certificates. - /// - public bool? AcceptInsecureCertificates { get; set; } - - /// - /// Gets or sets a value indicating whether the driver should request a URL to - /// a WebSocket to be used for bidirectional communication. - /// - public bool? UseWebSocketUrl { get; set; } - - /// - /// 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; - - /// - /// Gets or sets the value for describing how the browser is to wait for pages to load in the browser. - /// Defaults to . - /// - public PageLoadStrategy PageLoadStrategy { get; set; } = PageLoadStrategy.Default; - - /// - /// Gets or sets the to be used with this browser. - /// - public Proxy? Proxy { get; set; } - - /// - /// Gets or sets a value indicating whether <input type='file'/> elements - /// must be visible to allow uploading of files. - /// - public bool UseStrictFileInteractability { get; set; } - - /// - /// Gets or sets a value indicating whether files may be downloaded from remote node. - /// - public bool? EnableDownloads { get; set; } - - /// - /// Gets or sets the asynchronous script timeout, which is the amount - /// of time the driver should wait when executing JavaScript asynchronously. - /// This timeout only affects the - /// method. - /// - public TimeSpan? ScriptTimeout { get; set; } - - /// - /// Gets or sets the page load timeout, which is the amount of time the driver - /// should wait for a page to load when setting the - /// property. - /// - public TimeSpan? PageLoadTimeout { get; set; } - - /// - /// Gets or sets the implicit wait timeout, which is the amount of time the - /// driver should wait when searching for an element if it is not immediately - /// present. - /// - /// - /// When searching for a single element, the driver should poll the page - /// until the element has been found, or this timeout expires before throwing - /// a . When searching for multiple elements, - /// the driver should poll the page until at least one element has been found - /// or this timeout has expired. - /// - /// Increasing the implicit wait timeout should be used judiciously as it - /// will have an adverse effect on test run time, especially when used with - /// slower location strategies like XPath. - /// - /// - public TimeSpan? ImplicitWaitTimeout { get; set; } - - /// - /// Set or Get the location of the browser - /// Override in subclass - /// - public virtual string? BinaryLocation + DriverOptionsMergeResult result = new DriverOptionsMergeResult(); + if (this.BrowserName != null && other.BrowserName != null) { - get => null; - set => throw new NotImplementedException(); + result.IsMergeConflict = true; + result.MergeConflictOptionName = "BrowserName"; + return result; } - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the specific browser driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// - public virtual void AddAdditionalOption(string optionName, object optionValue) + if (this.BrowserVersion != null && other.BrowserVersion != null) { - this.ValidateCapabilityName(optionName); - this.additionalCapabilities[optionName] = optionValue; + result.IsMergeConflict = true; + result.MergeConflictOptionName = "BrowserVersion"; + return result; } - /// - /// Returns the for the specific browser driver with these - /// options included as capabilities. This does not copy the options. Further - /// changes will be reflected in the returned capabilities. - /// - /// The for browser driver with these options. - public abstract ICapabilities ToCapabilities(); - - /// - /// Compares this object with another to see if there - /// are merge conflicts between them. - /// - /// The object to compare with. - /// A object containing the status of the attempted merge. - /// If is . - public virtual DriverOptionsMergeResult GetMergeResult(DriverOptions other) + if (this.PlatformName != null && other.PlatformName != null) { - if (other is null) - { - throw new ArgumentNullException(nameof(other)); - } - - DriverOptionsMergeResult result = new DriverOptionsMergeResult(); - if (this.BrowserName != null && other.BrowserName != null) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "BrowserName"; - return result; - } + result.IsMergeConflict = true; + result.MergeConflictOptionName = "PlatformName"; + return result; + } - if (this.BrowserVersion != null && other.BrowserVersion != null) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "BrowserVersion"; - return result; - } + if (this.Proxy != null && other.Proxy != null) + { + result.IsMergeConflict = true; + result.MergeConflictOptionName = "Proxy"; + return result; + } - if (this.PlatformName != null && other.PlatformName != null) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "PlatformName"; - return result; - } + if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default && other.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) + { + result.IsMergeConflict = true; + result.MergeConflictOptionName = "UnhandledPromptBehavior"; + return result; + } - if (this.Proxy != null && other.Proxy != null) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "Proxy"; - return result; - } + if (this.PageLoadStrategy != PageLoadStrategy.Default && other.PageLoadStrategy != PageLoadStrategy.Default) + { + result.IsMergeConflict = true; + result.MergeConflictOptionName = "PageLoadStrategy"; + return result; + } - if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default && other.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "UnhandledPromptBehavior"; - return result; - } + return result; + } - if (this.PageLoadStrategy != PageLoadStrategy.Default && other.PageLoadStrategy != PageLoadStrategy.Default) - { - result.IsMergeConflict = true; - result.MergeConflictOptionName = "PageLoadStrategy"; - return result; - } + /// + /// Sets the logging preferences for this driver. + /// + /// The type of log for which to set the preference. + /// Known log types can be found in the class. + /// The value to which to set the log level. + public void SetLoggingPreference(string logType, LogLevel logLevel) + { + this.loggingPreferences[logType] = logLevel; + } - return result; + /// + /// Returns the current options as a . + /// + /// The current options as a . + internal IDictionary? ToDictionary() + { + ICapabilities? capabilities = this.ToCapabilities(); + if (capabilities is not IHasCapabilitiesDictionary desired) + { + return null; } - /// - /// Sets the logging preferences for this driver. - /// - /// The type of log for which to set the preference. - /// Known log types can be found in the class. - /// The value to which to set the log level. - public void SetLoggingPreference(string logType, LogLevel logLevel) + return desired.CapabilitiesDictionary; + } + + /// + /// Validates the name of the capability to verify it is not a capability + /// for which a type-safe property or method already exists. + /// + /// The name of the capability to validate. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + protected void ValidateCapabilityName([NotNull] string? capabilityName) + { + if (capabilityName is null || string.IsNullOrEmpty(capabilityName)) { - this.loggingPreferences[logType] = logLevel; + throw new ArgumentException("Capability name may not be null an empty string.", nameof(capabilityName)); } - /// - /// Returns the current options as a . - /// - /// The current options as a . - internal IDictionary? ToDictionary() + if (this.TryGetKnownCapability(capabilityName!, out string? typeSafeOptionName)) { - ICapabilities? capabilities = this.ToCapabilities(); - if (capabilities is not IHasCapabilitiesDictionary desired) - { - return null; - } - - return desired.CapabilitiesDictionary; + string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use the {1} instead.", capabilityName, typeSafeOptionName); + throw new ArgumentException(message, nameof(capabilityName)); } + } - /// - /// Validates the name of the capability to verify it is not a capability - /// for which a type-safe property or method already exists. - /// - /// The name of the capability to validate. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - protected void ValidateCapabilityName([NotNull] string? capabilityName) + /// + /// Adds a known capability to the list of known capabilities and associates it + /// with the type-safe property name of the options class to be used instead. + /// + /// The name of the capability. + /// The name of the option property or method to be used instead. + protected void AddKnownCapabilityName(string capabilityName, string typeSafeOptionName) + { + this.knownCapabilityNames[capabilityName] = typeSafeOptionName; + } + + /// + /// Remove a capability from the list of known capabilities + /// + /// The name of the capability to be removed. + protected void RemoveKnownCapabilityName(string? capabilityName) + { + if (capabilityName is not null) { - if (capabilityName is null || string.IsNullOrEmpty(capabilityName)) - { - throw new ArgumentException("Capability name may not be null an empty string.", nameof(capabilityName)); - } + this.knownCapabilityNames.Remove(capabilityName); + } + } - if (this.TryGetKnownCapability(capabilityName!, out string? typeSafeOptionName)) - { - string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use the {1} instead.", capabilityName, typeSafeOptionName); - throw new ArgumentException(message, nameof(capabilityName)); - } + /// + /// Gets a value indicating whether the specified capability name is a known capability name which has a type-safe option. + /// + /// The name of the capability to check. + /// if the capability name is known; otherwise . + protected bool IsKnownCapabilityName(string capabilityName) + { + return this.knownCapabilityNames.ContainsKey(capabilityName); + } + + /// + /// Gets a value indicating whether the specified capability name is a known capability name which has a type-safe option. + /// + /// The name of the capability to check. + /// The name of the type-safe option for the given capability name, or if not found. + /// if the capability name is known; otherwise . + protected bool TryGetKnownCapability(string capabilityName, [NotNullWhen(true)] out string? typeSafeOptionName) + { + return this.knownCapabilityNames.TryGetValue(capabilityName, out typeSafeOptionName); + } + + /// + /// Gets the name of the type-safe option for a given capability name. + /// + /// The name of the capability to check. + /// The name of the type-safe option for the given capability name. + protected string GetTypeSafeOptionName(string capabilityName) + { + if (!this.IsKnownCapabilityName(capabilityName)) + { + return string.Empty; } - /// - /// Adds a known capability to the list of known capabilities and associates it - /// with the type-safe property name of the options class to be used instead. - /// - /// The name of the capability. - /// The name of the option property or method to be used instead. - protected void AddKnownCapabilityName(string capabilityName, string typeSafeOptionName) + return this.knownCapabilityNames[capabilityName]; + } + + /// + /// Generates the logging preferences dictionary for transmission as a desired capability. + /// + /// The dictionary containing the logging preferences. + protected Dictionary? GenerateLoggingPreferencesDictionary() + { + if (this.loggingPreferences.Count == 0) { - this.knownCapabilityNames[capabilityName] = typeSafeOptionName; + return null; } - /// - /// Remove a capability from the list of known capabilities - /// - /// The name of the capability to be removed. - protected void RemoveKnownCapabilityName(string? capabilityName) + Dictionary loggingPreferenceCapability = new Dictionary(); + foreach (string logType in this.loggingPreferences.Keys) { - if (capabilityName is not null) - { - this.knownCapabilityNames.Remove(capabilityName); - } + loggingPreferenceCapability[logType] = this.loggingPreferences[logType].ToString().ToUpperInvariant(); } - /// - /// Gets a value indicating whether the specified capability name is a known capability name which has a type-safe option. - /// - /// The name of the capability to check. - /// if the capability name is known; otherwise . - protected bool IsKnownCapabilityName(string capabilityName) + return loggingPreferenceCapability; + } + + /// + /// Generates the current options as a capabilities object for further processing. + /// + /// A value indicating whether to generate capabilities compliant with the W3C WebDriver Specification. + /// A object representing the current options for further processing. + protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecificationCompliant) + { + DesiredCapabilities capabilities = new DesiredCapabilities(); + if (!string.IsNullOrEmpty(this.BrowserName)) { - return this.knownCapabilityNames.ContainsKey(capabilityName); + capabilities.SetCapability(CapabilityType.BrowserName, this.BrowserName!); } - /// - /// Gets a value indicating whether the specified capability name is a known capability name which has a type-safe option. - /// - /// The name of the capability to check. - /// The name of the type-safe option for the given capability name, or if not found. - /// if the capability name is known; otherwise . - protected bool TryGetKnownCapability(string capabilityName, [NotNullWhen(true)] out string? typeSafeOptionName) + if (!string.IsNullOrEmpty(this.BrowserVersion)) { - return this.knownCapabilityNames.TryGetValue(capabilityName, out typeSafeOptionName); + capabilities.SetCapability(CapabilityType.BrowserVersion, this.BrowserVersion!); } - /// - /// Gets the name of the type-safe option for a given capability name. - /// - /// The name of the capability to check. - /// The name of the type-safe option for the given capability name. - protected string GetTypeSafeOptionName(string capabilityName) + if (!string.IsNullOrEmpty(this.PlatformName)) { - if (!this.IsKnownCapabilityName(capabilityName)) - { - return string.Empty; - } + capabilities.SetCapability(CapabilityType.PlatformName, this.PlatformName!); + } - return this.knownCapabilityNames[capabilityName]; + if (this.AcceptInsecureCertificates.HasValue) + { + capabilities.SetCapability(CapabilityType.AcceptInsecureCertificates, this.AcceptInsecureCertificates); } - /// - /// Generates the logging preferences dictionary for transmission as a desired capability. - /// - /// The dictionary containing the logging preferences. - protected Dictionary? GenerateLoggingPreferencesDictionary() + if (this.UseWebSocketUrl.HasValue) { - if (this.loggingPreferences.Count == 0) - { - return null; - } + capabilities.SetCapability(CapabilityType.WebSocketUrl, this.UseWebSocketUrl); + } - Dictionary loggingPreferenceCapability = new Dictionary(); - foreach (string logType in this.loggingPreferences.Keys) - { - loggingPreferenceCapability[logType] = this.loggingPreferences[logType].ToString().ToUpperInvariant(); - } + if (this.EnableDownloads.HasValue) + { + capabilities.SetCapability(CapabilityType.EnableDownloads, this.EnableDownloads); + } - return loggingPreferenceCapability; + if (this.UseStrictFileInteractability) + { + capabilities.SetCapability(CapabilityType.UseStrictFileInteractability, true); } - /// - /// Generates the current options as a capabilities object for further processing. - /// - /// A value indicating whether to generate capabilities compliant with the W3C WebDriver Specification. - /// A object representing the current options for further processing. - protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecificationCompliant) + if (this.PageLoadStrategy != PageLoadStrategy.Default) { - DesiredCapabilities capabilities = new DesiredCapabilities(); - if (!string.IsNullOrEmpty(this.BrowserName)) + string pageLoadStrategySetting = "normal"; + switch (this.PageLoadStrategy) { - capabilities.SetCapability(CapabilityType.BrowserName, this.BrowserName!); - } + case PageLoadStrategy.Eager: + pageLoadStrategySetting = "eager"; + break; - if (!string.IsNullOrEmpty(this.BrowserVersion)) - { - capabilities.SetCapability(CapabilityType.BrowserVersion, this.BrowserVersion!); + case PageLoadStrategy.None: + pageLoadStrategySetting = "none"; + break; } - if (!string.IsNullOrEmpty(this.PlatformName)) - { - capabilities.SetCapability(CapabilityType.PlatformName, this.PlatformName!); - } + capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); + } - if (this.AcceptInsecureCertificates.HasValue) + if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) + { + string unhandledPropmtBehaviorSetting = "ignore"; + switch (this.UnhandledPromptBehavior) { - capabilities.SetCapability(CapabilityType.AcceptInsecureCertificates, this.AcceptInsecureCertificates); - } + case UnhandledPromptBehavior.Accept: + unhandledPropmtBehaviorSetting = "accept"; + break; - if (this.UseWebSocketUrl.HasValue) - { - capabilities.SetCapability(CapabilityType.WebSocketUrl, this.UseWebSocketUrl); - } + case UnhandledPromptBehavior.Dismiss: + unhandledPropmtBehaviorSetting = "dismiss"; + break; - if (this.EnableDownloads.HasValue) - { - capabilities.SetCapability(CapabilityType.EnableDownloads, this.EnableDownloads); - } + case UnhandledPromptBehavior.AcceptAndNotify: + unhandledPropmtBehaviorSetting = "accept and notify"; + break; - if (this.UseStrictFileInteractability) - { - capabilities.SetCapability(CapabilityType.UseStrictFileInteractability, true); + case UnhandledPromptBehavior.DismissAndNotify: + unhandledPropmtBehaviorSetting = "dismiss and notify"; + break; } - if (this.PageLoadStrategy != PageLoadStrategy.Default) - { - string pageLoadStrategySetting = "normal"; - switch (this.PageLoadStrategy) - { - case PageLoadStrategy.Eager: - pageLoadStrategySetting = "eager"; - break; - - case PageLoadStrategy.None: - pageLoadStrategySetting = "none"; - break; - } - - capabilities.SetCapability(CapabilityType.PageLoadStrategy, pageLoadStrategySetting); - } + capabilities.SetCapability(CapabilityType.UnhandledPromptBehavior, unhandledPropmtBehaviorSetting); + } - if (this.UnhandledPromptBehavior != UnhandledPromptBehavior.Default) + if (this.Proxy != null) + { + Dictionary? proxyCapability = this.Proxy.ToCapability(); + if (!isSpecificationCompliant) { - string unhandledPropmtBehaviorSetting = "ignore"; - switch (this.UnhandledPromptBehavior) - { - case UnhandledPromptBehavior.Accept: - unhandledPropmtBehaviorSetting = "accept"; - break; - - 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); + proxyCapability = this.Proxy.ToLegacyCapability(); } - if (this.Proxy != null) + if (proxyCapability != null) { - Dictionary? proxyCapability = this.Proxy.ToCapability(); - if (!isSpecificationCompliant) - { - proxyCapability = this.Proxy.ToLegacyCapability(); - } - - if (proxyCapability != null) - { - capabilities.SetCapability(CapabilityType.Proxy, proxyCapability); - } + capabilities.SetCapability(CapabilityType.Proxy, proxyCapability); } + } - if (this.ScriptTimeout.HasValue || this.PageLoadTimeout.HasValue || this.ImplicitWaitTimeout.HasValue) + if (this.ScriptTimeout.HasValue || this.PageLoadTimeout.HasValue || this.ImplicitWaitTimeout.HasValue) + { + var timeouts = new Timeout { - var timeouts = new Timeout - { - Script = this.ScriptTimeout, - PageLoad = this.PageLoadTimeout, - ImplicitWait = this.ImplicitWaitTimeout - }; - - capabilities.SetCapability(CapabilityType.Timeouts, timeouts.ToCapabilities()); - } + Script = this.ScriptTimeout, + PageLoad = this.PageLoadTimeout, + ImplicitWait = this.ImplicitWaitTimeout + }; - foreach (KeyValuePair pair in this.additionalCapabilities) - { - capabilities.SetCapability(pair.Key, pair.Value); - } + capabilities.SetCapability(CapabilityType.Timeouts, timeouts.ToCapabilities()); + } - return capabilities; + foreach (KeyValuePair pair in this.additionalCapabilities) + { + capabilities.SetCapability(pair.Key, pair.Value); } + + return capabilities; } } diff --git a/dotnet/src/webdriver/DriverOptionsMergeResult.cs b/dotnet/src/webdriver/DriverOptionsMergeResult.cs index 921ba14e64bf8..c677fdcd39b38 100644 --- a/dotnet/src/webdriver/DriverOptionsMergeResult.cs +++ b/dotnet/src/webdriver/DriverOptionsMergeResult.cs @@ -17,21 +17,20 @@ // under the License. // -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Represents the result of merging multiple . +/// +public class DriverOptionsMergeResult { /// - /// Represents the result of merging multiple . + /// Gets or sets a value indicating whether the DriverOptions would conflict when merged with another option. /// - public class DriverOptionsMergeResult - { - /// - /// Gets or sets a value indicating whether the DriverOptions would conflict when merged with another option. - /// - public bool IsMergeConflict { get; set; } + public bool IsMergeConflict { get; set; } - /// - /// Gets or sets the name of the name of the option that is in conflict, if any. - /// - public string? MergeConflictOptionName { get; set; } - } + /// + /// Gets or sets the name of the name of the option that is in conflict, if any. + /// + public string? MergeConflictOptionName { get; set; } } diff --git a/dotnet/src/webdriver/DriverProcessStartedEventArgs.cs b/dotnet/src/webdriver/DriverProcessStartedEventArgs.cs index 52f734e03a67c..5126576ab1767 100644 --- a/dotnet/src/webdriver/DriverProcessStartedEventArgs.cs +++ b/dotnet/src/webdriver/DriverProcessStartedEventArgs.cs @@ -21,52 +21,51 @@ using System.Diagnostics; using System.IO; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the DriverProcessStarted event of a object. +/// +public class DriverProcessStartedEventArgs : EventArgs { /// - /// Provides data for the DriverProcessStarted event of a object. + /// Initializes a new instance of the class. /// - public class DriverProcessStartedEventArgs : EventArgs + /// The object started. + /// If is . + public DriverProcessStartedEventArgs(Process driverProcess) { - /// - /// Initializes a new instance of the class. - /// - /// The object started. - /// If is . - public DriverProcessStartedEventArgs(Process driverProcess) + if (driverProcess is null) { - if (driverProcess is null) - { - throw new ArgumentNullException(nameof(driverProcess)); - } + throw new ArgumentNullException(nameof(driverProcess)); + } - this.ProcessId = driverProcess.Id; - if (driverProcess.StartInfo.RedirectStandardOutput && !driverProcess.StartInfo.UseShellExecute) - { - this.StandardOutputStreamReader = driverProcess.StandardOutput; - } + this.ProcessId = driverProcess.Id; + if (driverProcess.StartInfo.RedirectStandardOutput && !driverProcess.StartInfo.UseShellExecute) + { + this.StandardOutputStreamReader = driverProcess.StandardOutput; + } - if (driverProcess.StartInfo.RedirectStandardError && !driverProcess.StartInfo.UseShellExecute) - { - this.StandardErrorStreamReader = driverProcess.StandardError; - } + if (driverProcess.StartInfo.RedirectStandardError && !driverProcess.StartInfo.UseShellExecute) + { + this.StandardErrorStreamReader = driverProcess.StandardError; } + } - /// - /// Gets the unique ID of the driver executable process. - /// - public int ProcessId { get; } + /// + /// Gets the unique ID of the driver executable process. + /// + public int ProcessId { get; } - /// - /// Gets a object that can be used to read the contents - /// printed to stdout by a driver service process. - /// - public StreamReader? StandardOutputStreamReader { get; } + /// + /// Gets a object that can be used to read the contents + /// printed to stdout by a driver service process. + /// + public StreamReader? StandardOutputStreamReader { get; } - /// - /// Gets a object that can be used to read the contents - /// printed to stderr by a driver service process. - /// - public StreamReader? StandardErrorStreamReader { get; } - } + /// + /// Gets a object that can be used to read the contents + /// printed to stderr by a driver service process. + /// + public StreamReader? StandardErrorStreamReader { get; } } diff --git a/dotnet/src/webdriver/DriverProcessStartingEventArgs.cs b/dotnet/src/webdriver/DriverProcessStartingEventArgs.cs index 087e3d5682ca1..25c06d27a58d8 100644 --- a/dotnet/src/webdriver/DriverProcessStartingEventArgs.cs +++ b/dotnet/src/webdriver/DriverProcessStartingEventArgs.cs @@ -20,28 +20,27 @@ using System; using System.Diagnostics; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the DriverProcessStarting event of a object. +/// +public class DriverProcessStartingEventArgs : EventArgs { /// - /// Provides data for the DriverProcessStarting event of a object. + /// Initializes a new instance of the class. /// - public class DriverProcessStartingEventArgs : EventArgs + /// The of the + /// driver process to be started. + /// If is . + public DriverProcessStartingEventArgs(ProcessStartInfo startInfo) { - /// - /// Initializes a new instance of the class. - /// - /// The of the - /// driver process to be started. - /// If is . - public DriverProcessStartingEventArgs(ProcessStartInfo startInfo) - { - this.DriverServiceProcessStartInfo = startInfo ?? throw new ArgumentNullException(nameof(startInfo)); - } - - /// - /// Gets the object with which the - /// driver service process will be started. - /// - public ProcessStartInfo DriverServiceProcessStartInfo { get; } + this.DriverServiceProcessStartInfo = startInfo ?? throw new ArgumentNullException(nameof(startInfo)); } + + /// + /// Gets the object with which the + /// driver service process will be started. + /// + public ProcessStartInfo DriverServiceProcessStartInfo { get; } } diff --git a/dotnet/src/webdriver/DriverService.cs b/dotnet/src/webdriver/DriverService.cs index a20d66b938713..e59bf5d2a10c3 100644 --- a/dotnet/src/webdriver/DriverService.cs +++ b/dotnet/src/webdriver/DriverService.cs @@ -27,364 +27,363 @@ using System.Net.Http; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Exposes the service provided by a native WebDriver server executable. +/// +public abstract class DriverService : ICommandServer { + private bool isDisposed; + private Process? driverServiceProcess; + /// - /// Exposes the service provided by a native WebDriver server executable. + /// Initializes a new instance of the class. /// - public abstract class DriverService : ICommandServer + /// The full path to the directory containing the executable providing the service to drive the browser. + /// The port on which the driver executable should listen. + /// The file name of the driver service executable. + /// + /// If the path specified is or an empty string. + /// + /// + /// If the specified driver service executable does not exist in the specified directory. + /// + protected DriverService(string? servicePath, int port, string? driverServiceExecutableName) { - private bool isDisposed; - private Process? driverServiceProcess; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the directory containing the executable providing the service to drive the browser. - /// The port on which the driver executable should listen. - /// The file name of the driver service executable. - /// - /// If the path specified is or an empty string. - /// - /// - /// If the specified driver service executable does not exist in the specified directory. - /// - protected DriverService(string? servicePath, int port, string? driverServiceExecutableName) - { - this.DriverServicePath = servicePath; - this.DriverServiceExecutableName = driverServiceExecutableName; - this.Port = port; - } + this.DriverServicePath = servicePath; + this.DriverServiceExecutableName = driverServiceExecutableName; + this.Port = port; + } - /// - /// Occurs when the driver process is starting. - /// - public event EventHandler? DriverProcessStarting; + /// + /// Occurs when the driver process is starting. + /// + public event EventHandler? DriverProcessStarting; - /// - /// Occurs when the driver process has completely started. - /// - public event EventHandler? DriverProcessStarted; + /// + /// Occurs when the driver process has completely started. + /// + public event EventHandler? DriverProcessStarted; - /// - /// Gets the Uri of the service. - /// - public Uri ServiceUrl + /// + /// Gets the Uri of the service. + /// + public Uri ServiceUrl + { + get { - get - { - string url = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", this.HostName, this.Port); - return new Uri(url); - } + string url = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", this.HostName, this.Port); + return new Uri(url); } + } - /// - /// Gets or sets the host name of the service. Defaults to "localhost." - /// - /// - /// Most driver service executables do not allow connections from remote - /// (non-local) machines. This property can be used as a workaround so - /// that an IP address (like "127.0.0.1" or "::1") can be used instead. - /// - public string HostName { get; set; } = "localhost"; - - /// - /// Gets or sets the port of the service. - /// - public int Port { get; set; } - - /// - /// Gets or sets a value indicating whether the initial diagnostic information is suppressed - /// when starting the driver server executable. Defaults to , meaning - /// diagnostic information should be shown by the driver server executable. - /// - public bool SuppressInitialDiagnosticInformation { get; set; } - - /// - /// Gets a value indicating whether the service is running. - /// - [MemberNotNullWhen(true, nameof(driverServiceProcess))] - public bool IsRunning => this.driverServiceProcess != null && !this.driverServiceProcess.HasExited; - - /// - /// Gets or sets a value indicating whether the command prompt window of the service should be hidden. - /// - public bool HideCommandPromptWindow { get; set; } - - /// - /// Gets the process ID of the running driver service executable. Returns 0 if the process is not running. - /// - public int ProcessId - { - get - { - if (this.IsRunning) - { - // There's a slight chance that the Process object is running, - // but does not have an ID set. This should be rare, but we - // definitely don't want to throw an exception. - try - { - return this.driverServiceProcess.Id; - } - catch (InvalidOperationException) - { - } - } + /// + /// Gets or sets the host name of the service. Defaults to "localhost." + /// + /// + /// Most driver service executables do not allow connections from remote + /// (non-local) machines. This property can be used as a workaround so + /// that an IP address (like "127.0.0.1" or "::1") can be used instead. + /// + public string HostName { get; set; } = "localhost"; - return 0; - } - } + /// + /// Gets or sets the port of the service. + /// + public int Port { get; set; } + + /// + /// Gets or sets a value indicating whether the initial diagnostic information is suppressed + /// when starting the driver server executable. Defaults to , meaning + /// diagnostic information should be shown by the driver server executable. + /// + public bool SuppressInitialDiagnosticInformation { get; set; } + + /// + /// Gets a value indicating whether the service is running. + /// + [MemberNotNullWhen(true, nameof(driverServiceProcess))] + public bool IsRunning => this.driverServiceProcess != null && !this.driverServiceProcess.HasExited; - /// - /// Gets or sets a value indicating the time to wait for an initial connection before timing out. - /// - public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(20); - - /// - /// Gets or sets the executable file name of the driver service. - /// - public string? DriverServiceExecutableName { get; set; } - - /// - /// Gets or sets the path of the driver service. - /// - public string? DriverServicePath { get; set; } - - /// - /// Gets the command-line arguments for the driver service. - /// - protected virtual string CommandLineArguments => string.Format(CultureInfo.InvariantCulture, "--port={0}", this.Port); - - /// - /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. - /// - protected virtual TimeSpan TerminationTimeout => TimeSpan.FromSeconds(10); - - /// - /// Gets a value indicating whether the service has a shutdown API that can be called to terminate - /// it gracefully before forcing a termination. - /// - protected virtual bool HasShutdown => true; - - /// - /// Gets a value indicating whether the service is responding to HTTP requests. - /// - protected virtual bool IsInitialized + /// + /// Gets or sets a value indicating whether the command prompt window of the service should be hidden. + /// + public bool HideCommandPromptWindow { get; set; } + + /// + /// Gets the process ID of the running driver service executable. Returns 0 if the process is not running. + /// + public int ProcessId + { + get { - get + if (this.IsRunning) { - bool isInitialized = false; - + // There's a slight chance that the Process object is running, + // but does not have an ID set. This should be rare, but we + // definitely don't want to throw an exception. try { - using (var httpClient = new HttpClient()) - { - httpClient.DefaultRequestHeaders.ConnectionClose = true; - httpClient.Timeout = TimeSpan.FromSeconds(5); - - Uri serviceHealthUri = new Uri(this.ServiceUrl, new Uri(DriverCommand.Status, UriKind.Relative)); - using (var response = Task.Run(async () => await httpClient.GetAsync(serviceHealthUri)).GetAwaiter().GetResult()) - { - // Checking the response from the 'status' end point. Note that we are simply checking - // that the HTTP status returned is a 200 status, and that the resposne has the correct - // Content-Type header. A more sophisticated check would parse the JSON response and - // validate its values. At the moment we do not do this more sophisticated check. - isInitialized = response.StatusCode == HttpStatusCode.OK && response.Content.Headers.ContentType is { MediaType: string mediaType } && mediaType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase); - } - } + return this.driverServiceProcess.Id; } - catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException) + catch (InvalidOperationException) { - // Do nothing. The exception is expected, meaning driver service is not initialized. } - - return isInitialized; } - } - /// - /// Releases all resources associated with this . - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); + return 0; } + } - /// - /// Starts the DriverService if it is not already running. - /// - [MemberNotNull(nameof(driverServiceProcess))] - public void Start() - { - if (this.driverServiceProcess != null) - { - return; - } + /// + /// Gets or sets a value indicating the time to wait for an initial connection before timing out. + /// + public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(20); - this.driverServiceProcess = new Process(); + /// + /// Gets or sets the executable file name of the driver service. + /// + public string? DriverServiceExecutableName { get; set; } - if (this.DriverServicePath != null) + /// + /// Gets or sets the path of the driver service. + /// + public string? DriverServicePath { get; set; } + + /// + /// Gets the command-line arguments for the driver service. + /// + protected virtual string CommandLineArguments => string.Format(CultureInfo.InvariantCulture, "--port={0}", this.Port); + + /// + /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. + /// + protected virtual TimeSpan TerminationTimeout => TimeSpan.FromSeconds(10); + + /// + /// Gets a value indicating whether the service has a shutdown API that can be called to terminate + /// it gracefully before forcing a termination. + /// + protected virtual bool HasShutdown => true; + + /// + /// Gets a value indicating whether the service is responding to HTTP requests. + /// + protected virtual bool IsInitialized + { + get + { + bool isInitialized = false; + + try { - if (this.DriverServiceExecutableName is null) + using (var httpClient = new HttpClient()) { - throw new InvalidOperationException("If the driver service path is specified, the driver service executable name must be as well"); - } + httpClient.DefaultRequestHeaders.ConnectionClose = true; + httpClient.Timeout = TimeSpan.FromSeconds(5); - this.driverServiceProcess.StartInfo.FileName = Path.Combine(this.DriverServicePath, this.DriverServiceExecutableName); + Uri serviceHealthUri = new Uri(this.ServiceUrl, new Uri(DriverCommand.Status, UriKind.Relative)); + using (var response = Task.Run(async () => await httpClient.GetAsync(serviceHealthUri)).GetAwaiter().GetResult()) + { + // Checking the response from the 'status' end point. Note that we are simply checking + // that the HTTP status returned is a 200 status, and that the resposne has the correct + // Content-Type header. A more sophisticated check would parse the JSON response and + // validate its values. At the moment we do not do this more sophisticated check. + isInitialized = response.StatusCode == HttpStatusCode.OK && response.Content.Headers.ContentType is { MediaType: string mediaType } && mediaType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase); + } + } } - else + catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException) { - this.driverServiceProcess.StartInfo.FileName = new DriverFinder(this.GetDefaultDriverOptions()).GetDriverPath(); + // Do nothing. The exception is expected, meaning driver service is not initialized. } - this.driverServiceProcess.StartInfo.Arguments = this.CommandLineArguments; - this.driverServiceProcess.StartInfo.UseShellExecute = false; - this.driverServiceProcess.StartInfo.CreateNoWindow = this.HideCommandPromptWindow; + return isInitialized; + } + } + + /// + /// Releases all resources associated with this . + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } - DriverProcessStartingEventArgs eventArgs = new DriverProcessStartingEventArgs(this.driverServiceProcess.StartInfo); - this.OnDriverProcessStarting(eventArgs); + /// + /// Starts the DriverService if it is not already running. + /// + [MemberNotNull(nameof(driverServiceProcess))] + public void Start() + { + if (this.driverServiceProcess != null) + { + return; + } - this.driverServiceProcess.Start(); - bool serviceAvailable = this.WaitForServiceInitialization(); - DriverProcessStartedEventArgs processStartedEventArgs = new DriverProcessStartedEventArgs(this.driverServiceProcess); - this.OnDriverProcessStarted(processStartedEventArgs); + this.driverServiceProcess = new Process(); - if (!serviceAvailable) + if (this.DriverServicePath != null) + { + if (this.DriverServiceExecutableName is null) { - throw new WebDriverException($"Cannot start the driver service on {this.ServiceUrl}"); + throw new InvalidOperationException("If the driver service path is specified, the driver service executable name must be as well"); } - } - /// - /// The browser options instance that corresponds to the driver service - /// - /// - protected abstract DriverOptions GetDefaultDriverOptions(); - - /// - /// Releases all resources associated with this . - /// - /// if the Dispose method was explicitly called; otherwise, . - protected virtual void Dispose(bool disposing) + this.driverServiceProcess.StartInfo.FileName = Path.Combine(this.DriverServicePath, this.DriverServiceExecutableName); + } + else { - if (!this.isDisposed) - { - if (disposing) - { - this.Stop(); - } + this.driverServiceProcess.StartInfo.FileName = new DriverFinder(this.GetDefaultDriverOptions()).GetDriverPath(); + } - this.isDisposed = true; - } + this.driverServiceProcess.StartInfo.Arguments = this.CommandLineArguments; + this.driverServiceProcess.StartInfo.UseShellExecute = false; + this.driverServiceProcess.StartInfo.CreateNoWindow = this.HideCommandPromptWindow; + + DriverProcessStartingEventArgs eventArgs = new DriverProcessStartingEventArgs(this.driverServiceProcess.StartInfo); + this.OnDriverProcessStarting(eventArgs); + + this.driverServiceProcess.Start(); + bool serviceAvailable = this.WaitForServiceInitialization(); + DriverProcessStartedEventArgs processStartedEventArgs = new DriverProcessStartedEventArgs(this.driverServiceProcess); + this.OnDriverProcessStarted(processStartedEventArgs); + + if (!serviceAvailable) + { + throw new WebDriverException($"Cannot start the driver service on {this.ServiceUrl}"); } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected void OnDriverProcessStarting(DriverProcessStartingEventArgs eventArgs) + /// + /// The browser options instance that corresponds to the driver service + /// + /// + protected abstract DriverOptions GetDefaultDriverOptions(); + + /// + /// Releases all resources associated with this . + /// + /// if the Dispose method was explicitly called; otherwise, . + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) { - if (eventArgs == null) + if (disposing) { - throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); + this.Stop(); } - this.DriverProcessStarting?.Invoke(this, eventArgs); + this.isDisposed = true; } + } - /// - /// Raises the event. - /// - /// A that contains the event data. - protected void OnDriverProcessStarted(DriverProcessStartedEventArgs eventArgs) + /// + /// Raises the event. + /// + /// A that contains the event data. + protected void OnDriverProcessStarting(DriverProcessStartingEventArgs eventArgs) + { + if (eventArgs == null) { - if (eventArgs == null) - { - throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); - } + throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); + } - this.DriverProcessStarted?.Invoke(this, eventArgs); + this.DriverProcessStarting?.Invoke(this, eventArgs); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected void OnDriverProcessStarted(DriverProcessStartedEventArgs eventArgs) + { + if (eventArgs == null) + { + throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); } - /// - /// Stops the DriverService. - /// - private void Stop() + this.DriverProcessStarted?.Invoke(this, eventArgs); + } + + /// + /// Stops the DriverService. + /// + private void Stop() + { + if (this.IsRunning) { - if (this.IsRunning) + if (this.HasShutdown) { - if (this.HasShutdown) + Uri shutdownUrl = new Uri(this.ServiceUrl, "/shutdown"); + DateTime timeout = DateTime.Now.Add(this.TerminationTimeout); + using (var httpClient = new HttpClient()) { - Uri shutdownUrl = new Uri(this.ServiceUrl, "/shutdown"); - DateTime timeout = DateTime.Now.Add(this.TerminationTimeout); - using (var httpClient = new HttpClient()) - { - httpClient.DefaultRequestHeaders.ConnectionClose = true; + httpClient.DefaultRequestHeaders.ConnectionClose = true; - while (this.IsRunning && DateTime.Now < timeout) + while (this.IsRunning && DateTime.Now < timeout) + { + try { - try + // Issue the shutdown HTTP request, then wait a short while for + // the process to have exited. If the process hasn't yet exited, + // we'll retry. We wait for exit here, since catching the exception + // for a failed HTTP request due to a closed socket is particularly + // expensive. + using (var response = Task.Run(async () => await httpClient.GetAsync(shutdownUrl)).GetAwaiter().GetResult()) { - // Issue the shutdown HTTP request, then wait a short while for - // the process to have exited. If the process hasn't yet exited, - // we'll retry. We wait for exit here, since catching the exception - // for a failed HTTP request due to a closed socket is particularly - // expensive. - using (var response = Task.Run(async () => await httpClient.GetAsync(shutdownUrl)).GetAwaiter().GetResult()) - { - - } - this.driverServiceProcess.WaitForExit(3000); - } - catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) - { } + + this.driverServiceProcess.WaitForExit(3000); + } + catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) + { } } } + } - // If at this point, the process still hasn't exited, wait for one - // last-ditch time, then, if it still hasn't exited, kill it. Note - // that falling into this branch of code should be exceedingly rare. - if (this.IsRunning) + // If at this point, the process still hasn't exited, wait for one + // last-ditch time, then, if it still hasn't exited, kill it. Note + // that falling into this branch of code should be exceedingly rare. + if (this.IsRunning) + { + this.driverServiceProcess.WaitForExit(Convert.ToInt32(this.TerminationTimeout.TotalMilliseconds)); + if (!this.driverServiceProcess.HasExited) { - this.driverServiceProcess.WaitForExit(Convert.ToInt32(this.TerminationTimeout.TotalMilliseconds)); - if (!this.driverServiceProcess.HasExited) - { - this.driverServiceProcess.Kill(); - } + this.driverServiceProcess.Kill(); } - - this.driverServiceProcess.Dispose(); - this.driverServiceProcess = null; } + + this.driverServiceProcess.Dispose(); + this.driverServiceProcess = null; } + } - /// - /// Waits until a the service is initialized, or the timeout set - /// by the property is reached. - /// - /// if the service is properly started and receiving HTTP requests; - /// otherwise; . - private bool WaitForServiceInitialization() + /// + /// Waits until a the service is initialized, or the timeout set + /// by the property is reached. + /// + /// if the service is properly started and receiving HTTP requests; + /// otherwise; . + private bool WaitForServiceInitialization() + { + bool isInitialized = false; + DateTime timeout = DateTime.Now.Add(this.InitializationTimeout); + while (!isInitialized && DateTime.Now < timeout) { - bool isInitialized = false; - DateTime timeout = DateTime.Now.Add(this.InitializationTimeout); - while (!isInitialized && DateTime.Now < timeout) + // If the driver service process has exited, we can exit early. + if (!this.IsRunning) { - // If the driver service process has exited, we can exit early. - if (!this.IsRunning) - { - break; - } - - isInitialized = this.IsInitialized; + break; } - return isInitialized; + isInitialized = this.IsInitialized; } + + return isInitialized; } } diff --git a/dotnet/src/webdriver/DriverServiceNotFoundException.cs b/dotnet/src/webdriver/DriverServiceNotFoundException.cs index 56bf8a0f6a01f..53a2ad59c2418 100644 --- a/dotnet/src/webdriver/DriverServiceNotFoundException.cs +++ b/dotnet/src/webdriver/DriverServiceNotFoundException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when the driver service is not available. +/// +[Serializable] +public class DriverServiceNotFoundException : WebDriverException { /// - /// The exception that is thrown when the driver service is not available. + /// Initializes a new instance of the class. /// - [Serializable] - public class DriverServiceNotFoundException : WebDriverException + public DriverServiceNotFoundException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public DriverServiceNotFoundException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public DriverServiceNotFoundException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public DriverServiceNotFoundException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public DriverServiceNotFoundException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public DriverServiceNotFoundException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/Edge/EdgeDriver.cs b/dotnet/src/webdriver/Edge/EdgeDriver.cs index 4ef8a3d05369e..240648306ec77 100644 --- a/dotnet/src/webdriver/Edge/EdgeDriver.cs +++ b/dotnet/src/webdriver/Edge/EdgeDriver.cs @@ -23,142 +23,141 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium.Edge +namespace OpenQA.Selenium.Edge; + +/// +/// Provides a mechanism to write tests against Edge +/// +public class EdgeDriver : ChromiumDriver { + private static readonly Dictionary edgeCustomCommands = new Dictionary() + { + { ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cdp/execute") }, + { GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/ms/cast/get_sinks") }, + { SelectCastSinkCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/set_sink_to_use") }, + { StartCastTabMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/start_tab_mirroring") }, + { StartCastDesktopMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/start_desktop_mirroring") }, + { GetCastIssueMessageCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/ms/cast/get_issue_message") }, + { StopCastingCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/stop_casting") } + }; + /// - /// Provides a mechanism to write tests against Edge + /// Initializes a new instance of the class. /// - public class EdgeDriver : ChromiumDriver + public EdgeDriver() + : this(new EdgeOptions()) { - private static readonly Dictionary edgeCustomCommands = new Dictionary() - { - { ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cdp/execute") }, - { GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/ms/cast/get_sinks") }, - { SelectCastSinkCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/set_sink_to_use") }, - { StartCastTabMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/start_tab_mirroring") }, - { StartCastDesktopMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/start_desktop_mirroring") }, - { GetCastIssueMessageCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/ms/cast/get_issue_message") }, - { StopCastingCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/ms/cast/stop_casting") } - }; - - /// - /// Initializes a new instance of the class. - /// - public EdgeDriver() - : this(new EdgeOptions()) - { - } + } - /// - /// Initializes a new instance of the class using the specified options. - /// - /// The to be used with the Edge driver. - /// If is . - public EdgeDriver(EdgeOptions options) - : this(EdgeDriverService.CreateDefaultService(), options) - { - } + /// + /// Initializes a new instance of the class using the specified options. + /// + /// The to be used with the Edge driver. + /// If is . + public EdgeDriver(EdgeOptions options) + : this(EdgeDriverService.CreateDefaultService(), options) + { + } - /// - /// Initializes a new instance of the class using the specified driver service. - /// - /// The used to initialize the driver. - /// If is . - public EdgeDriver(EdgeDriverService service) - : this(service, new EdgeOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified driver service. + /// + /// The used to initialize the driver. + /// If is . + public EdgeDriver(EdgeDriverService service) + : this(service, new EdgeOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing the WebDriver executable. - /// - /// The full path to the directory containing the WebDriver executable. - public EdgeDriver(string edgeDriverDirectory) - : this(edgeDriverDirectory, new EdgeOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing the WebDriver executable. + /// + /// The full path to the directory containing the WebDriver executable. + public EdgeDriver(string edgeDriverDirectory) + : this(edgeDriverDirectory, new EdgeOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing the WebDriver executable and options. - /// - /// The full path to the directory containing the WebDriver executable. - /// The to be used with the Edge driver. - /// If is . - public EdgeDriver(string edgeDriverDirectory, EdgeOptions options) - : this(edgeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing the WebDriver executable and options. + /// + /// The full path to the directory containing the WebDriver executable. + /// The to be used with the Edge driver. + /// If is . + public EdgeDriver(string edgeDriverDirectory, EdgeOptions options) + : this(edgeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing the WebDriver executable, options, and command timeout. - /// - /// The full path to the directory containing the WebDriver executable. - /// The to be used with the Edge driver. - /// The maximum amount of time to wait for each command. - /// If is . - public EdgeDriver(string edgeDriverDirectory, EdgeOptions options, TimeSpan commandTimeout) - : this(EdgeDriverService.CreateDefaultService(edgeDriverDirectory), options, commandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing the WebDriver executable, options, and command timeout. + /// + /// The full path to the directory containing the WebDriver executable. + /// The to be used with the Edge driver. + /// The maximum amount of time to wait for each command. + /// If is . + public EdgeDriver(string edgeDriverDirectory, EdgeOptions options, TimeSpan commandTimeout) + : this(EdgeDriverService.CreateDefaultService(edgeDriverDirectory), options, commandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified - /// and options. - /// - /// The to use. - /// The used to initialize the driver. - /// If or are . - public EdgeDriver(EdgeDriverService service, EdgeOptions options) - : this(service, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified + /// and options. + /// + /// The to use. + /// The used to initialize the driver. + /// If or are . + public EdgeDriver(EdgeDriverService service, EdgeOptions options) + : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified . - /// - /// The to use. - /// The to be used with the Edge driver. - /// The maximum amount of time to wait for each command. - /// If or are . - public EdgeDriver(EdgeDriverService service, EdgeOptions options, TimeSpan commandTimeout) - : base(service, options, commandTimeout) - { - this.AddCustomEdgeCommands(); - } + /// + /// Initializes a new instance of the class using the specified . + /// + /// The to use. + /// The to be used with the Edge driver. + /// The maximum amount of time to wait for each command. + /// If or are . + public EdgeDriver(EdgeDriverService service, EdgeOptions options, TimeSpan commandTimeout) + : base(service, options, commandTimeout) + { + this.AddCustomEdgeCommands(); + } - /// - /// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver. - /// The keys of the dictionary are the names assigned to the command; the values are the - /// objects describing the command behavior. - /// - public static IReadOnlyDictionary CustomCommandDefinitions + /// + /// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver. + /// The keys of the dictionary are the names assigned to the command; the values are the + /// objects describing the command behavior. + /// + public static IReadOnlyDictionary CustomCommandDefinitions + { + get { - get + Dictionary customCommands = new Dictionary(); + foreach (KeyValuePair entry in ChromiumCustomCommands) { - Dictionary customCommands = new Dictionary(); - foreach (KeyValuePair entry in ChromiumCustomCommands) - { - customCommands[entry.Key] = entry.Value; - } - - foreach (KeyValuePair entry in edgeCustomCommands) - { - customCommands[entry.Key] = entry.Value; - } + customCommands[entry.Key] = entry.Value; + } - return new ReadOnlyDictionary(customCommands); + foreach (KeyValuePair entry in edgeCustomCommands) + { + customCommands[entry.Key] = entry.Value; } + + return new ReadOnlyDictionary(customCommands); } + } - private void AddCustomEdgeCommands() + private void AddCustomEdgeCommands() + { + foreach (KeyValuePair entry in CustomCommandDefinitions) { - foreach (KeyValuePair entry in CustomCommandDefinitions) - { - this.RegisterInternalDriverCommand(entry.Key, entry.Value); - } + this.RegisterInternalDriverCommand(entry.Key, entry.Value); } } } diff --git a/dotnet/src/webdriver/Edge/EdgeDriverService.cs b/dotnet/src/webdriver/Edge/EdgeDriverService.cs index 6e800453db69c..6da2741069290 100644 --- a/dotnet/src/webdriver/Edge/EdgeDriverService.cs +++ b/dotnet/src/webdriver/Edge/EdgeDriverService.cs @@ -22,84 +22,83 @@ using System; using System.IO; -namespace OpenQA.Selenium.Edge +namespace OpenQA.Selenium.Edge; + +/// +/// Exposes the service provided by the native WebDriver executable. +/// +public sealed class EdgeDriverService : ChromiumDriverService { + private const string MSEdgeDriverServiceFileName = "msedgedriver"; + /// - /// Exposes the service provided by the native WebDriver executable. + /// Initializes a new instance of the class. /// - public sealed class EdgeDriverService : ChromiumDriverService + /// The full path to the EdgeDriver executable. + /// The file name of the EdgeDriver executable. + /// The port on which the EdgeDriver executable should listen. + private EdgeDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, executableFileName, port) { - private const string MSEdgeDriverServiceFileName = "msedgedriver"; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the EdgeDriver executable. - /// The file name of the EdgeDriver executable. - /// The port on which the EdgeDriver executable should listen. - private EdgeDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, executableFileName, port) - { - } + } - /// - protected override DriverOptions GetDefaultDriverOptions() - { - return new EdgeOptions(); - } + /// + protected override DriverOptions GetDefaultDriverOptions() + { + return new EdgeOptions(); + } - /// - /// Gets or sets a value indicating whether the service should use verbose logging. - /// - [Obsolete("Use EnableVerboseLogging")] - public bool UseVerboseLogging - { - get => this.EnableVerboseLogging; - set => this.EnableVerboseLogging = value; - } + /// + /// Gets or sets a value indicating whether the service should use verbose logging. + /// + [Obsolete("Use EnableVerboseLogging")] + public bool UseVerboseLogging + { + get => this.EnableVerboseLogging; + set => this.EnableVerboseLogging = value; + } - /// - /// Creates a default instance of the EdgeDriverService. - /// - /// A EdgeDriverService that implements default settings. - public static EdgeDriverService CreateDefaultService() - { - return new EdgeDriverService(null, null, PortUtilities.FindFreePort()); - } + /// + /// Creates a default instance of the EdgeDriverService. + /// + /// A EdgeDriverService that implements default settings. + public static EdgeDriverService CreateDefaultService() + { + return new EdgeDriverService(null, null, PortUtilities.FindFreePort()); + } - /// - /// Creates a default instance of the EdgeDriverService using a specified path to the EdgeDriver executable. - /// - /// The path to the executable or the directory containing the EdgeDriver executable. - /// An EdgeDriverService using a random port. - public static EdgeDriverService CreateDefaultService(string? driverPath) + /// + /// Creates a default instance of the EdgeDriverService using a specified path to the EdgeDriver executable. + /// + /// The path to the executable or the directory containing the EdgeDriver executable. + /// An EdgeDriverService using a random port. + public static EdgeDriverService CreateDefaultService(string? driverPath) + { + if (File.Exists(driverPath)) { - if (File.Exists(driverPath)) - { - string fileName = Path.GetFileName(driverPath); - string driverFolder = Path.GetDirectoryName(driverPath)!; + string fileName = Path.GetFileName(driverPath); + string driverFolder = Path.GetDirectoryName(driverPath)!; - return CreateDefaultService(driverFolder, fileName); - } - else - { - string fileName = ChromiumDriverServiceFileName(MSEdgeDriverServiceFileName); - string? driverFolder = driverPath; - - return CreateDefaultService(driverFolder, fileName); - } + return CreateDefaultService(driverFolder, fileName); } - - /// - /// Creates a default instance of the EdgeDriverService using a specified path to the EdgeDriver executable with the given name. - /// - /// The directory containing the EdgeDriver executable. - /// The name of the EdgeDriver executable file. - /// A EdgeDriverService using a random port. - public static EdgeDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + else { - return new EdgeDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); + string fileName = ChromiumDriverServiceFileName(MSEdgeDriverServiceFileName); + string? driverFolder = driverPath; + + return CreateDefaultService(driverFolder, fileName); } + } + /// + /// Creates a default instance of the EdgeDriverService using a specified path to the EdgeDriver executable with the given name. + /// + /// The directory containing the EdgeDriver executable. + /// The name of the EdgeDriver executable file. + /// A EdgeDriverService using a random port. + public static EdgeDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + { + return new EdgeDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); } + } diff --git a/dotnet/src/webdriver/Edge/EdgeOptions.cs b/dotnet/src/webdriver/Edge/EdgeOptions.cs index fc660ca04a136..571706b87ab84 100644 --- a/dotnet/src/webdriver/Edge/EdgeOptions.cs +++ b/dotnet/src/webdriver/Edge/EdgeOptions.cs @@ -21,80 +21,79 @@ using System; using System.Globalization; -namespace OpenQA.Selenium.Edge +namespace OpenQA.Selenium.Edge; + +/// +/// Class to manage options specific to +/// +/// +/// +/// EdgeOptions options = new EdgeOptions(); +/// +/// +/// For use with EdgeDriver: +/// +/// +/// EdgeDriver driver = new EdgeDriver(options); +/// +/// +/// For use with RemoteWebDriver: +/// +/// +/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); +/// +/// +public class EdgeOptions : ChromiumOptions { + private const string DefaultBrowserNameValue = "MicrosoftEdge"; + private const string WebViewBrowserNameValue = "webview2"; + private const string EdgeOptionsCapabilityName = "edgeOptions"; + /// - /// Class to manage options specific to + /// Initializes a new instance of the class. /// - /// - /// - /// EdgeOptions options = new EdgeOptions(); - /// - /// - /// For use with EdgeDriver: - /// - /// - /// EdgeDriver driver = new EdgeDriver(options); - /// - /// - /// For use with RemoteWebDriver: - /// - /// - /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); - /// - /// - public class EdgeOptions : ChromiumOptions + public EdgeOptions() : base() { - private const string DefaultBrowserNameValue = "MicrosoftEdge"; - private const string WebViewBrowserNameValue = "webview2"; - private const string EdgeOptionsCapabilityName = "edgeOptions"; - - /// - /// Initializes a new instance of the class. - /// - public EdgeOptions() : base() - { - this.BrowserName = DefaultBrowserNameValue; - } + this.BrowserName = DefaultBrowserNameValue; + } - /// - /// Gets the vendor prefix to apply to Chromium-specific capability names. - /// - protected override string VendorPrefix => "ms"; + /// + /// Gets the vendor prefix to apply to Chromium-specific capability names. + /// + protected override string VendorPrefix => "ms"; - /// - /// Gets the name of the capability used to store Chromium options in - /// an object. - /// - public override string CapabilityName => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.VendorPrefix, EdgeOptionsCapabilityName); + /// + /// Gets the name of the capability used to store Chromium options in + /// an object. + /// + public override string CapabilityName => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.VendorPrefix, EdgeOptionsCapabilityName); - /// - /// Gets or sets whether to create a WebView session used for launching an Edge (Chromium) WebView-based app on desktop. - /// - public bool UseWebView - { - get => this.BrowserName == WebViewBrowserNameValue; - set => this.BrowserName = value ? WebViewBrowserNameValue : DefaultBrowserNameValue; - } + /// + /// Gets or sets whether to create a WebView session used for launching an Edge (Chromium) WebView-based app on desktop. + /// + public bool UseWebView + { + get => this.BrowserName == WebViewBrowserNameValue; + set => this.BrowserName = value ? WebViewBrowserNameValue : DefaultBrowserNameValue; + } - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the Edge driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// Calling this method adds capabilities to the Edge-specific options object passed to - /// WebDriver executable (property name 'ms:edgeOptions'). - public void AddAdditionalEdgeOption(string optionName, object optionValue) - { - this.AddAdditionalChromiumOption(optionName, optionValue); - } + /// + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the Edge driver. + /// + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// Calling this method adds capabilities to the Edge-specific options object passed to + /// WebDriver executable (property name 'ms:edgeOptions'). + public void AddAdditionalEdgeOption(string optionName, object optionValue) + { + this.AddAdditionalChromiumOption(optionName, optionValue); } } diff --git a/dotnet/src/webdriver/ElementClickInterceptedException.cs b/dotnet/src/webdriver/ElementClickInterceptedException.cs index ed8417ccbb145..d60f9d1223c5c 100644 --- a/dotnet/src/webdriver/ElementClickInterceptedException.cs +++ b/dotnet/src/webdriver/ElementClickInterceptedException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an element is not visible. +/// +[Serializable] +public class ElementClickInterceptedException : ElementNotInteractableException { /// - /// The exception that is thrown when an element is not visible. + /// Initializes a new instance of the class. /// - [Serializable] - public class ElementClickInterceptedException : ElementNotInteractableException + public ElementClickInterceptedException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public ElementClickInterceptedException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public ElementClickInterceptedException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public ElementClickInterceptedException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public ElementClickInterceptedException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public ElementClickInterceptedException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/ElementCoordinates.cs b/dotnet/src/webdriver/ElementCoordinates.cs index d4bb6be24656a..a134728401330 100644 --- a/dotnet/src/webdriver/ElementCoordinates.cs +++ b/dotnet/src/webdriver/ElementCoordinates.cs @@ -21,52 +21,51 @@ using OpenQA.Selenium.Internal; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can discover where an element is on the screen. +/// +internal sealed class ElementCoordinates : ICoordinates { + private readonly WebElement element; + /// - /// Defines the interface through which the user can discover where an element is on the screen. + /// Initializes a new instance of the class. /// - internal sealed class ElementCoordinates : ICoordinates + /// The to be located. + public ElementCoordinates(WebElement element) { - private readonly WebElement element; - - /// - /// Initializes a new instance of the class. - /// - /// The to be located. - public ElementCoordinates(WebElement element) - { - this.element = element ?? throw new ArgumentNullException(nameof(element)); - } + this.element = element ?? throw new ArgumentNullException(nameof(element)); + } - /// - /// Gets the location of an element in absolute screen coordinates. - /// - public System.Drawing.Point LocationOnScreen => throw new NotImplementedException(); + /// + /// Gets the location of an element in absolute screen coordinates. + /// + public System.Drawing.Point LocationOnScreen => throw new NotImplementedException(); - /// - /// Gets the location of an element relative to the origin of the view port. - /// - public System.Drawing.Point LocationInViewport => this.element.LocationOnScreenOnceScrolledIntoView; + /// + /// Gets the location of an element relative to the origin of the view port. + /// + public System.Drawing.Point LocationInViewport => this.element.LocationOnScreenOnceScrolledIntoView; - /// - /// Gets the location of an element's position within the HTML DOM. - /// - public System.Drawing.Point LocationInDom => this.element.Location; + /// + /// Gets the location of an element's position within the HTML DOM. + /// + public System.Drawing.Point LocationInDom => this.element.Location; - /// - /// Gets a locator providing a user-defined location for this element. - /// - public object AuxiliaryLocator + /// + /// Gets a locator providing a user-defined location for this element. + /// + public object AuxiliaryLocator + { + get { - get - { - // Note that the OSS dialect of the wire protocol for the Actions API - // uses the raw ID of the element, not an element reference. To use this, - // extract the ID using the well-known key to the dictionary for element - // references. - return ((IWebDriverObjectReference)this.element).ObjectReferenceId; - } + // Note that the OSS dialect of the wire protocol for the Actions API + // uses the raw ID of the element, not an element reference. To use this, + // extract the ID using the well-known key to the dictionary for element + // references. + return ((IWebDriverObjectReference)this.element).ObjectReferenceId; } } } diff --git a/dotnet/src/webdriver/ElementNotInteractableException.cs b/dotnet/src/webdriver/ElementNotInteractableException.cs index 704f293fcb71e..120ab8bdaa911 100644 --- a/dotnet/src/webdriver/ElementNotInteractableException.cs +++ b/dotnet/src/webdriver/ElementNotInteractableException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an element is not interactable. +/// +[Serializable] +public class ElementNotInteractableException : InvalidElementStateException { /// - /// The exception that is thrown when an element is not interactable. + /// Initializes a new instance of the class. /// - [Serializable] - public class ElementNotInteractableException : InvalidElementStateException + public ElementNotInteractableException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public ElementNotInteractableException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public ElementNotInteractableException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public ElementNotInteractableException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public ElementNotInteractableException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public ElementNotInteractableException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/EncodedFile.cs b/dotnet/src/webdriver/EncodedFile.cs index c8b35beb436eb..9c529cf23286a 100644 --- a/dotnet/src/webdriver/EncodedFile.cs +++ b/dotnet/src/webdriver/EncodedFile.cs @@ -19,50 +19,49 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents a file transmitted over the wire as a base64-encoded string. +/// +public abstract class EncodedFile { /// - /// Represents a file transmitted over the wire as a base64-encoded string. + /// Initializes a new instance of the class. /// - public abstract class EncodedFile + /// The file as a Base64-encoded string. + /// If is . + /// + /// The length of , ignoring white-space characters, is not zero or a multiple of 4. + /// -or- + /// The format of is invalid. contains a non-base-64 character, + /// more than two padding characters, or a non-white space-character among the padding characters. + /// + protected EncodedFile(string base64EncodedFile) { - /// - /// Initializes a new instance of the class. - /// - /// The file as a Base64-encoded string. - /// If is . - /// - /// The length of , ignoring white-space characters, is not zero or a multiple of 4. - /// -or- - /// The format of is invalid. contains a non-base-64 character, - /// more than two padding characters, or a non-white space-character among the padding characters. - /// - protected EncodedFile(string base64EncodedFile) - { - this.AsBase64EncodedString = base64EncodedFile ?? throw new ArgumentNullException(nameof(base64EncodedFile)); - this.AsByteArray = Convert.FromBase64String(base64EncodedFile); - } + this.AsBase64EncodedString = base64EncodedFile ?? throw new ArgumentNullException(nameof(base64EncodedFile)); + this.AsByteArray = Convert.FromBase64String(base64EncodedFile); + } - /// - /// Gets the value of the encoded file as a Base64-encoded string. - /// - public string AsBase64EncodedString { get; } + /// + /// Gets the value of the encoded file as a Base64-encoded string. + /// + public string AsBase64EncodedString { get; } - /// - /// Gets the value of the encoded file as an array of bytes. - /// - public byte[] AsByteArray { get; } + /// + /// Gets the value of the encoded file as an array of bytes. + /// + public byte[] AsByteArray { get; } - /// - /// Saves the file, overwriting it if it already exists. - /// - /// The full path and file name to save the file to. - public abstract void SaveAsFile(string fileName); + /// + /// Saves the file, overwriting it if it already exists. + /// + /// The full path and file name to save the file to. + public abstract void SaveAsFile(string fileName); - /// - /// Returns a String that represents the current Object. - /// - /// A String that represents the current Object. - public override string ToString() => this.AsBase64EncodedString; - } + /// + /// Returns a String that represents the current Object. + /// + /// A String that represents the current Object. + public override string ToString() => this.AsBase64EncodedString; } diff --git a/dotnet/src/webdriver/ErrorResponse.cs b/dotnet/src/webdriver/ErrorResponse.cs index 2a9407603a55f..caab7bac527cf 100644 --- a/dotnet/src/webdriver/ErrorResponse.cs +++ b/dotnet/src/webdriver/ErrorResponse.cs @@ -19,88 +19,87 @@ using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a way to store errors from a response +/// +public class ErrorResponse { /// - /// Provides a way to store errors from a response + /// Initializes a new instance of the class. /// - public class ErrorResponse + public ErrorResponse() { - /// - /// Initializes a new instance of the class. - /// - public ErrorResponse() - { - } + } - /// - /// Initializes a new instance of the class using the specified values. - /// - /// A containing names and values of - /// the properties of this . - public ErrorResponse(Dictionary? responseValue) + /// + /// Initializes a new instance of the class using the specified values. + /// + /// A containing names and values of + /// the properties of this . + public ErrorResponse(Dictionary? responseValue) + { + if (responseValue != null) { - if (responseValue != null) + if (responseValue.TryGetValue("message", out object? messageObj) + && messageObj?.ToString() is string message) { - if (responseValue.TryGetValue("message", out object? messageObj) - && messageObj?.ToString() is string message) - { - this.Message = message; - } - else - { - this.Message = "The error did not contain a message."; - } + this.Message = message; + } + else + { + this.Message = "The error did not contain a message."; + } - if (responseValue.TryGetValue("screen", out object? screenObj)) - { - this.Screenshot = screenObj?.ToString(); - } + if (responseValue.TryGetValue("screen", out object? screenObj)) + { + this.Screenshot = screenObj?.ToString(); + } - if (responseValue.TryGetValue("class", out object? classObj)) - { - this.ClassName = classObj?.ToString(); - } + if (responseValue.TryGetValue("class", out object? classObj)) + { + this.ClassName = classObj?.ToString(); + } - if (responseValue.TryGetValue("stackTrace", out object? stackTraceObj) - || responseValue.TryGetValue("stacktrace", out stackTraceObj)) + if (responseValue.TryGetValue("stackTrace", out object? stackTraceObj) + || responseValue.TryGetValue("stacktrace", out stackTraceObj)) + { + if (stackTraceObj is object?[] stackTraceArray) { - if (stackTraceObj is object?[] stackTraceArray) + List stackTraceList = new List(); + foreach (object? rawStackTraceElement in stackTraceArray) { - List stackTraceList = new List(); - foreach (object? rawStackTraceElement in stackTraceArray) + if (rawStackTraceElement is Dictionary elementAsDictionary) { - if (rawStackTraceElement is Dictionary elementAsDictionary) - { - stackTraceList.Add(new StackTraceElement(elementAsDictionary)); - } + stackTraceList.Add(new StackTraceElement(elementAsDictionary)); } - - this.StackTrace = stackTraceList.ToArray(); } + + this.StackTrace = stackTraceList.ToArray(); } } } + } - /// - /// Gets or sets the message from the response - /// - public string Message { get; } = string.Empty; + /// + /// Gets or sets the message from the response + /// + public string Message { get; } = string.Empty; - /// - /// Gets or sets the class name that threw the error - /// - public string? ClassName { get; } + /// + /// Gets or sets the class name that threw the error + /// + public string? ClassName { get; } - // TODO: (JimEvans) Change this to return an Image. - /// - /// Gets or sets the screenshot of the error - /// - public string? Screenshot { get; } + // TODO: (JimEvans) Change this to return an Image. + /// + /// Gets or sets the screenshot of the error + /// + public string? Screenshot { get; } - /// - /// Gets or sets the stack trace of the error - /// - public StackTraceElement[]? StackTrace { get; } - } + /// + /// Gets or sets the stack trace of the error + /// + public StackTraceElement[]? StackTrace { get; } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxAndroidOptions.cs b/dotnet/src/webdriver/Firefox/FirefoxAndroidOptions.cs index 56bcec501c53d..9a7ddd1a7607d 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxAndroidOptions.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxAndroidOptions.cs @@ -22,45 +22,44 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Generates the capabilities for automating Firefox applications on Android +/// +public class FirefoxAndroidOptions : AndroidOptions { + private readonly List androidIntentArguments = new List(); + /// - /// Generates the capabilities for automating Firefox applications on Android + /// Initializes a new instance of the class. /// - public class FirefoxAndroidOptions : AndroidOptions + /// + public FirefoxAndroidOptions(string androidPackage) : base(androidPackage) { - private readonly List androidIntentArguments = new List(); - - /// - /// Initializes a new instance of the class. - /// - /// - public FirefoxAndroidOptions(string androidPackage) : base(androidPackage) - { - } + } - /// - /// Gets a read-only list of the intent arguments set for this set of options. - /// - public ReadOnlyCollection AndroidIntentArguments => this.androidIntentArguments.AsReadOnly(); + /// + /// Gets a read-only list of the intent arguments set for this set of options. + /// + public ReadOnlyCollection AndroidIntentArguments => this.androidIntentArguments.AsReadOnly(); - /// - /// Argument to launch the intent with. The given intent arguments are appended to the "am start" command. - /// - /// The argument to add. - public void AddIntentArgument(string argument) - { - this.AddIntentArguments(argument); - } + /// + /// Argument to launch the intent with. The given intent arguments are appended to the "am start" command. + /// + /// The argument to add. + public void AddIntentArgument(string argument) + { + this.AddIntentArguments(argument); + } - /// - /// Arguments to launch the intent with. The given intent arguments are appended to the "am start" command. - /// - /// The arguments to add. - /// If is . - public void AddIntentArguments(params string[] arguments) - { - this.androidIntentArguments.AddRange(arguments); - } + /// + /// Arguments to launch the intent with. The given intent arguments are appended to the "am start" command. + /// + /// The arguments to add. + /// If is . + public void AddIntentArguments(params string[] arguments) + { + this.androidIntentArguments.AddRange(arguments); } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxCommandContext.cs b/dotnet/src/webdriver/Firefox/FirefoxCommandContext.cs index 13cd9de0900dd..85e6273e34118 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxCommandContext.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxCommandContext.cs @@ -17,23 +17,22 @@ // under the License. // -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Represents the valid values for the command context used for executing Firefox driver commands. +/// +public enum FirefoxCommandContext { /// - /// Represents the valid values for the command context used for executing Firefox driver commands. + /// Commands will be sent to the content context, operating on the + /// page loaded in the browser. /// - public enum FirefoxCommandContext - { - /// - /// Commands will be sent to the content context, operating on the - /// page loaded in the browser. - /// - Content, + Content, - /// - /// Commands will be sent to the chrome context, operating on the - /// browser elements hosting the page loaded in the browser. - /// - Chrome - } + /// + /// Commands will be sent to the chrome context, operating on the + /// browser elements hosting the page loaded in the browser. + /// + Chrome } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs index beebddf390d6c..b8c717858c497 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriver.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriver.cs @@ -25,410 +25,409 @@ using System.IO; using System.IO.Compression; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Provides a way to access Firefox to run tests. +/// +/// +/// When the FirefoxDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and +/// start your test. +/// +/// In the case of the FirefoxDriver, you can specify a named profile to be used, or you can let the +/// driver create a temporary, anonymous profile. A custom extension allowing the driver to communicate +/// to the browser will be installed into the profile. +/// +/// +/// +/// +/// [TestFixture] +/// public class Testing +/// { +/// private IWebDriver driver; +/// +/// [SetUp] +/// public void SetUp() +/// { +/// driver = new FirefoxDriver(); +/// } +/// +/// [Test] +/// public void TestGoogle() +/// { +/// driver.Navigate().GoToUrl("http://www.google.co.uk/"); +/// /* +/// * Rest of the test +/// */ +/// } +/// +/// [TearDown] +/// public void TearDown() +/// { +/// driver.Quit(); +/// } +/// } +/// +/// +public class FirefoxDriver : WebDriver { /// - /// Provides a way to access Firefox to run tests. + /// Command for setting the command context of a Firefox driver. /// - /// - /// When the FirefoxDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and - /// start your test. - /// - /// In the case of the FirefoxDriver, you can specify a named profile to be used, or you can let the - /// driver create a temporary, anonymous profile. A custom extension allowing the driver to communicate - /// to the browser will be installed into the profile. - /// - /// - /// - /// - /// [TestFixture] - /// public class Testing - /// { - /// private IWebDriver driver; - /// - /// [SetUp] - /// public void SetUp() - /// { - /// driver = new FirefoxDriver(); - /// } - /// - /// [Test] - /// public void TestGoogle() - /// { - /// driver.Navigate().GoToUrl("http://www.google.co.uk/"); - /// /* - /// * Rest of the test - /// */ - /// } - /// - /// [TearDown] - /// public void TearDown() - /// { - /// driver.Quit(); - /// } - /// } - /// - /// - public class FirefoxDriver : WebDriver + public static readonly string SetContextCommand = "setContext"; + + /// + /// Command for getting the command context of a Firefox driver. + /// + public static readonly string GetContextCommand = "getContext"; + + /// + /// Command for installing an addon to a Firefox driver. + /// + public static readonly string InstallAddOnCommand = "installAddOn"; + + /// + /// Command for uninstalling an addon from a Firefox driver. + /// + public static readonly string UninstallAddOnCommand = "uninstallAddOn"; + + /// + /// Command for getting aa full page screenshot from a Firefox driver. + /// + public static readonly string GetFullPageScreenshotCommand = "fullPageScreenshot"; + + private static readonly Dictionary firefoxCustomCommands = new Dictionary() { - /// - /// Command for setting the command context of a Firefox driver. - /// - public static readonly string SetContextCommand = "setContext"; - - /// - /// Command for getting the command context of a Firefox driver. - /// - public static readonly string GetContextCommand = "getContext"; - - /// - /// Command for installing an addon to a Firefox driver. - /// - public static readonly string InstallAddOnCommand = "installAddOn"; - - /// - /// Command for uninstalling an addon from a Firefox driver. - /// - public static readonly string UninstallAddOnCommand = "uninstallAddOn"; - - /// - /// Command for getting aa full page screenshot from a Firefox driver. - /// - public static readonly string GetFullPageScreenshotCommand = "fullPageScreenshot"; - - private static readonly Dictionary firefoxCustomCommands = new Dictionary() - { - { SetContextCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/context") }, - { GetContextCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/context") }, - { InstallAddOnCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/addon/install") }, - { UninstallAddOnCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/addon/uninstall") }, - { GetFullPageScreenshotCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/screenshot/full") } - }; + { SetContextCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/context") }, + { GetContextCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/context") }, + { InstallAddOnCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/addon/install") }, + { UninstallAddOnCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/moz/addon/uninstall") }, + { GetFullPageScreenshotCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/moz/screenshot/full") } + }; - /// - /// Initializes a new instance of the class. - /// - public FirefoxDriver() - : this(new FirefoxOptions()) - { - } + /// + /// Initializes a new instance of the class. + /// + public FirefoxDriver() + : this(new FirefoxOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified options. Uses the Mozilla-provided Marionette driver implementation. - /// - /// The to be used with the Firefox driver. - /// If is . - public FirefoxDriver(FirefoxOptions options) - : this(FirefoxDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified options. Uses the Mozilla-provided Marionette driver implementation. + /// + /// The to be used with the Firefox driver. + /// If is . + public FirefoxDriver(FirefoxOptions options) + : this(FirefoxDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified driver service. Uses the Mozilla-provided Marionette driver implementation. - /// - /// The used to initialize the driver. - /// If is . - public FirefoxDriver(FirefoxDriverService service) - : this(service, new FirefoxOptions(), RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified driver service. Uses the Mozilla-provided Marionette driver implementation. + /// + /// The used to initialize the driver. + /// If is . + public FirefoxDriver(FirefoxDriverService service) + : this(service, new FirefoxOptions(), RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing geckodriver.exe. - /// - /// The full path to the directory containing geckodriver.exe. - public FirefoxDriver(string geckoDriverDirectory) - : this(geckoDriverDirectory, new FirefoxOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing geckodriver.exe. + /// + /// The full path to the directory containing geckodriver.exe. + public FirefoxDriver(string geckoDriverDirectory) + : this(geckoDriverDirectory, new FirefoxOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing geckodriver.exe and options. - /// - /// The full path to the directory containing geckodriver.exe. - /// The to be used with the Firefox driver. - /// If is . - public FirefoxDriver(string geckoDriverDirectory, FirefoxOptions options) - : this(geckoDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing geckodriver.exe and options. + /// + /// The full path to the directory containing geckodriver.exe. + /// The to be used with the Firefox driver. + /// If is . + public FirefoxDriver(string geckoDriverDirectory, FirefoxOptions options) + : this(geckoDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing geckodriver.exe, options, and command timeout. - /// - /// The full path to the directory containing geckodriver.exe. - /// The to be used with the Firefox driver. - /// The maximum amount of time to wait for each command. - /// If is . - public FirefoxDriver(string geckoDriverDirectory, FirefoxOptions options, TimeSpan commandTimeout) - : this(FirefoxDriverService.CreateDefaultService(geckoDriverDirectory), options, commandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing geckodriver.exe, options, and command timeout. + /// + /// The full path to the directory containing geckodriver.exe. + /// The to be used with the Firefox driver. + /// The maximum amount of time to wait for each command. + /// If is . + public FirefoxDriver(string geckoDriverDirectory, FirefoxOptions options, TimeSpan commandTimeout) + : this(FirefoxDriverService.CreateDefaultService(geckoDriverDirectory), options, commandTimeout) + { + } + + /// + /// Initializes a new instance of the class using the specified options, driver service, and timeout. Uses the Mozilla-provided Marionette driver implementation. + /// + /// The to use. + /// The to be used with the Firefox driver. + /// If or are . + public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options) + : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified options, driver service, and timeout. Uses the Mozilla-provided Marionette driver implementation. - /// - /// The to use. - /// The to be used with the Firefox driver. - /// If or are . - public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options) - : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + /// + /// Initializes a new instance of the class using the specified options, driver service, and timeout. Uses the Mozilla-provided Marionette driver implementation. + /// + /// The to use. + /// The to be used with the Firefox driver. + /// The maximum amount of time to wait for each command. + /// If or are . + public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options, TimeSpan commandTimeout) + : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + { + // Add the custom commands unique to Firefox + this.AddCustomFirefoxCommands(); + } + + /// + /// Uses DriverFinder to set Service attributes if necessary when creating the command executor + /// + /// + /// + /// + /// + /// If is . + private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + { + if (options is null) { + throw new ArgumentNullException(nameof(options)); } - /// - /// Initializes a new instance of the class using the specified options, driver service, and timeout. Uses the Mozilla-provided Marionette driver implementation. - /// - /// The to use. - /// The to be used with the Firefox driver. - /// The maximum amount of time to wait for each command. - /// If or are . - public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options, TimeSpan commandTimeout) - : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + if (service is null) { - // Add the custom commands unique to Firefox - this.AddCustomFirefoxCommands(); + throw new ArgumentNullException(nameof(service)); } - /// - /// Uses DriverFinder to set Service attributes if necessary when creating the command executor - /// - /// - /// - /// - /// - /// If is . - private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + if (service.DriverServicePath == null) { - if (options is null) + DriverFinder finder = new DriverFinder(options); + string fullServicePath = finder.GetDriverPath(); + service.DriverServicePath = Path.GetDirectoryName(fullServicePath); + service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); + if (finder.TryGetBrowserPath(out string? browserPath)) { - throw new ArgumentNullException(nameof(options)); + options.BinaryLocation = browserPath; + options.BrowserVersion = null; } + } + return new DriverServiceCommandExecutor(service, commandTimeout); + } - if (service is null) - { - throw new ArgumentNullException(nameof(service)); - } + /// + /// Gets a read-only dictionary of the custom WebDriver commands defined for FirefoxDriver. + /// The keys of the dictionary are the names assigned to the command; the values are the + /// objects describing the command behavior. + /// + public static IReadOnlyDictionary CustomCommandDefinitions => new ReadOnlyDictionary(firefoxCustomCommands); - if (service.DriverServicePath == null) - { - DriverFinder finder = new DriverFinder(options); - string fullServicePath = finder.GetDriverPath(); - service.DriverServicePath = Path.GetDirectoryName(fullServicePath); - service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); - if (finder.TryGetBrowserPath(out string? browserPath)) - { - options.BinaryLocation = browserPath; - options.BrowserVersion = null; - } - } - return new DriverServiceCommandExecutor(service, commandTimeout); - } + /// + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. + /// + /// The Firefox driver does not allow a file detector to be set, + /// as the server component of the Firefox driver only allows uploads from + /// the local computer environment. Attempting to set this property has no + /// effect, but does not throw an exception. If you are attempting to run + /// the Firefox driver remotely, use in + /// conjunction with a standalone WebDriver server. + public override IFileDetector FileDetector + { + get => base.FileDetector; + set { } + } - /// - /// Gets a read-only dictionary of the custom WebDriver commands defined for FirefoxDriver. - /// The keys of the dictionary are the names assigned to the command; the values are the - /// objects describing the command behavior. - /// - public static IReadOnlyDictionary CustomCommandDefinitions => new ReadOnlyDictionary(firefoxCustomCommands); - - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// The Firefox driver does not allow a file detector to be set, - /// as the server component of the Firefox driver only allows uploads from - /// the local computer environment. Attempting to set this property has no - /// effect, but does not throw an exception. If you are attempting to run - /// the Firefox driver remotely, use in - /// conjunction with a standalone WebDriver server. - public override IFileDetector FileDetector - { - get => base.FileDetector; - set { } - } + /// + /// Sets the command context used when issuing commands to geckodriver. + /// + /// If response is not recognized + /// The context of commands. + public FirefoxCommandContext GetContext() + { + Response commandResponse = this.Execute(GetContextCommand, null); - /// - /// Sets the command context used when issuing commands to geckodriver. - /// - /// If response is not recognized - /// The context of commands. - public FirefoxCommandContext GetContext() + if (commandResponse.Value is not string response + || !Enum.TryParse(response, ignoreCase: true, out FirefoxCommandContext output)) { - Response commandResponse = this.Execute(GetContextCommand, null); + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Do not recognize response: {0}; expected Context or Chrome", commandResponse.Value)); + } - if (commandResponse.Value is not string response - || !Enum.TryParse(response, ignoreCase: true, out FirefoxCommandContext output)) - { - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Do not recognize response: {0}; expected Context or Chrome", commandResponse.Value)); - } + return output; + } - return output; - } + /// + /// Sets the command context used when issuing commands to geckodriver. + /// + /// The value to which to set the context. + public void SetContext(FirefoxCommandContext context) + { + string contextValue = context.ToString().ToLowerInvariant(); + Dictionary parameters = new Dictionary(); + parameters["context"] = contextValue; + this.Execute(SetContextCommand, parameters); + } - /// - /// Sets the command context used when issuing commands to geckodriver. - /// - /// The value to which to set the context. - public void SetContext(FirefoxCommandContext context) + /// + /// Installs a Firefox add-on from a directory. + /// + /// Full path of the directory of the add-on to install. + /// Whether the add-on is temporary; required for unsigned add-ons. + /// The unique identifier of the installed add-on. + /// If is null or empty. + /// If the directory at does not exist. + public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false) + { + if (string.IsNullOrEmpty(addOnDirectoryToInstall)) { - string contextValue = context.ToString().ToLowerInvariant(); - Dictionary parameters = new Dictionary(); - parameters["context"] = contextValue; - this.Execute(SetContextCommand, parameters); + throw new ArgumentNullException(nameof(addOnDirectoryToInstall), "Add-on file name must not be null or the empty string"); } - /// - /// Installs a Firefox add-on from a directory. - /// - /// Full path of the directory of the add-on to install. - /// Whether the add-on is temporary; required for unsigned add-ons. - /// The unique identifier of the installed add-on. - /// If is null or empty. - /// If the directory at does not exist. - public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false) + if (!Directory.Exists(addOnDirectoryToInstall)) { - if (string.IsNullOrEmpty(addOnDirectoryToInstall)) - { - throw new ArgumentNullException(nameof(addOnDirectoryToInstall), "Add-on file name must not be null or the empty string"); - } + throw new ArgumentException("Directory " + addOnDirectoryToInstall + " does not exist", nameof(addOnDirectoryToInstall)); + } - if (!Directory.Exists(addOnDirectoryToInstall)) - { - throw new ArgumentException("Directory " + addOnDirectoryToInstall + " does not exist", nameof(addOnDirectoryToInstall)); - } + string addOnFileToInstall = Path.Combine(Path.GetTempPath(), "addon" + new Random().Next() + ".zip"); + ZipFile.CreateFromDirectory(addOnDirectoryToInstall, addOnFileToInstall); - string addOnFileToInstall = Path.Combine(Path.GetTempPath(), "addon" + new Random().Next() + ".zip"); - ZipFile.CreateFromDirectory(addOnDirectoryToInstall, addOnFileToInstall); + return this.InstallAddOnFromFile(addOnFileToInstall, temporary); + } - return this.InstallAddOnFromFile(addOnFileToInstall, temporary); + /// + /// Installs a Firefox add-on from a file, typically a .xpi file. + /// + /// Full path and file name of the add-on to install. + /// Whether the add-on is temporary; required for unsigned add-ons. + /// The unique identifier of the installed add-on. + /// + /// If is null or empty. + /// or + /// If the file at does not exist. + /// + public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = false) + { + if (string.IsNullOrEmpty(addOnFileToInstall)) + { + throw new ArgumentNullException(nameof(addOnFileToInstall), "Add-on file name must not be null or the empty string"); } - /// - /// Installs a Firefox add-on from a file, typically a .xpi file. - /// - /// Full path and file name of the add-on to install. - /// Whether the add-on is temporary; required for unsigned add-ons. - /// The unique identifier of the installed add-on. - /// - /// If is null or empty. - /// or - /// If the file at does not exist. - /// - public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = false) + byte[] addOnBytes; + try { - if (string.IsNullOrEmpty(addOnFileToInstall)) - { - throw new ArgumentNullException(nameof(addOnFileToInstall), "Add-on file name must not be null or the empty string"); - } + addOnBytes = File.ReadAllBytes(addOnFileToInstall); + } + catch (Exception ex) + { + throw new ArgumentException($"Failed to read from file {addOnFileToInstall}", nameof(addOnFileToInstall), ex); + } - byte[] addOnBytes; - try - { - addOnBytes = File.ReadAllBytes(addOnFileToInstall); - } - catch (Exception ex) - { - throw new ArgumentException($"Failed to read from file {addOnFileToInstall}", nameof(addOnFileToInstall), ex); - } + string base64EncodedAddOn = Convert.ToBase64String(addOnBytes); - string base64EncodedAddOn = Convert.ToBase64String(addOnBytes); + return this.InstallAddOn(base64EncodedAddOn, temporary); + } - return this.InstallAddOn(base64EncodedAddOn, temporary); + /// + /// Installs a Firefox add-on. + /// + /// The base64-encoded string representation of the add-on binary. + /// Whether the add-on is temporary; required for unsigned add-ons. + /// The unique identifier of the installed add-on. + /// If is null or empty. + public string InstallAddOn(string base64EncodedAddOn, bool temporary = false) + { + if (string.IsNullOrEmpty(base64EncodedAddOn)) + { + throw new ArgumentNullException(nameof(base64EncodedAddOn), "Base64 encoded add-on must not be null or the empty string"); } - /// - /// Installs a Firefox add-on. - /// - /// The base64-encoded string representation of the add-on binary. - /// Whether the add-on is temporary; required for unsigned add-ons. - /// The unique identifier of the installed add-on. - /// If is null or empty. - public string InstallAddOn(string base64EncodedAddOn, bool temporary = false) + Dictionary parameters = new Dictionary { - if (string.IsNullOrEmpty(base64EncodedAddOn)) - { - throw new ArgumentNullException(nameof(base64EncodedAddOn), "Base64 encoded add-on must not be null or the empty string"); - } + ["addon"] = base64EncodedAddOn, + ["temporary"] = temporary + }; + Response response = this.Execute(InstallAddOnCommand, parameters); - Dictionary parameters = new Dictionary - { - ["addon"] = base64EncodedAddOn, - ["temporary"] = temporary - }; - Response response = this.Execute(InstallAddOnCommand, parameters); + return (string)response.Value!; + } - return (string)response.Value!; + /// + /// Uninstalls a Firefox add-on. + /// + /// The ID of the add-on to uninstall. + /// If is null or empty. + public void UninstallAddOn(string addOnId) + { + if (string.IsNullOrEmpty(addOnId)) + { + throw new ArgumentNullException(nameof(addOnId), "Base64 encoded add-on must not be null or the empty string"); } - /// - /// Uninstalls a Firefox add-on. - /// - /// The ID of the add-on to uninstall. - /// If is null or empty. - public void UninstallAddOn(string addOnId) - { - if (string.IsNullOrEmpty(addOnId)) - { - throw new ArgumentNullException(nameof(addOnId), "Base64 encoded add-on must not be null or the empty string"); - } + Dictionary parameters = new Dictionary(); + parameters["id"] = addOnId; + this.Execute(UninstallAddOnCommand, parameters); + } - Dictionary parameters = new Dictionary(); - parameters["id"] = addOnId; - this.Execute(UninstallAddOnCommand, parameters); - } + /// + /// Gets a object representing the image of the full page on the screen. + /// + /// A object containing the image. + public Screenshot GetFullPageScreenshot() + { + Response screenshotResponse = this.Execute(GetFullPageScreenshotCommand, null); - /// - /// Gets a object representing the image of the full page on the screen. - /// - /// A object containing the image. - public Screenshot GetFullPageScreenshot() - { - Response screenshotResponse = this.Execute(GetFullPageScreenshotCommand, null); + screenshotResponse.EnsureValueIsNotNull(); + string base64 = screenshotResponse.Value.ToString()!; + return new Screenshot(base64); + } - screenshotResponse.EnsureValueIsNotNull(); - string base64 = screenshotResponse.Value.ToString()!; - return new Screenshot(base64); - } + /// + /// In derived classes, the method prepares the environment for test execution. + /// + protected virtual void PrepareEnvironment() + { + // Does nothing, but provides a hook for subclasses to do "stuff" + } - /// - /// In derived classes, the method prepares the environment for test execution. - /// - protected virtual void PrepareEnvironment() - { - // Does nothing, but provides a hook for subclasses to do "stuff" - } + /// + /// Disposes of the FirefoxDriver and frees all resources. + /// + /// A value indicating whether the user initiated the + /// disposal of the object. Pass if the user is actively + /// disposing the object; otherwise . + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } - /// - /// Disposes of the FirefoxDriver and frees all resources. - /// - /// A value indicating whether the user initiated the - /// disposal of the object. Pass if the user is actively - /// disposing the object; otherwise . - protected override void Dispose(bool disposing) + private static ICapabilities ConvertOptionsToCapabilities(FirefoxOptions options) + { + if (options == null) { - base.Dispose(disposing); + throw new ArgumentNullException(nameof(options), "options must not be null"); } - private static ICapabilities ConvertOptionsToCapabilities(FirefoxOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options), "options must not be null"); - } - - return options.ToCapabilities(); - } + return options.ToCapabilities(); + } - private void AddCustomFirefoxCommands() + private void AddCustomFirefoxCommands() + { + foreach (KeyValuePair entry in CustomCommandDefinitions) { - foreach (KeyValuePair entry in CustomCommandDefinitions) - { - this.RegisterInternalDriverCommand(entry.Key, entry.Value); - } + this.RegisterInternalDriverCommand(entry.Key, entry.Value); } } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriverLogLevel.cs b/dotnet/src/webdriver/Firefox/FirefoxDriverLogLevel.cs index 580958ba0605d..dd742a92cb50c 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriverLogLevel.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriverLogLevel.cs @@ -17,51 +17,50 @@ // under the License. // -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Represents the valid values of logging levels available with the Firefox driver (geckodriver.exe). +/// +public enum FirefoxDriverLogLevel { /// - /// Represents the valid values of logging levels available with the Firefox driver (geckodriver.exe). + /// Represents the Trace value, the most detailed logging level available. /// - public enum FirefoxDriverLogLevel - { - /// - /// Represents the Trace value, the most detailed logging level available. - /// - Trace, + Trace, - /// - /// Represents the Debug value - /// - Debug, + /// + /// Represents the Debug value + /// + Debug, - /// - /// Represents the Config value - /// - Config, + /// + /// Represents the Config value + /// + Config, - /// - /// Represents the Info value - /// - Info, + /// + /// Represents the Info value + /// + Info, - /// - /// Represents the Warn value - /// - Warn, + /// + /// Represents the Warn value + /// + Warn, - /// - /// Represents the Error value - /// - Error, + /// + /// Represents the Error value + /// + Error, - /// - /// Represents the Fatal value, the least detailed logging level available. - /// - Fatal, + /// + /// Represents the Fatal value, the least detailed logging level available. + /// + Fatal, - /// - /// Represents that the logging value is unspecified, and should be the default level. - /// - Default - } + /// + /// Represents that the logging value is unspecified, and should be the default level. + /// + Default } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs b/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs index a3cce2afed357..223f5b58156a0 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs @@ -23,240 +23,239 @@ using System.IO; using System.Text; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Exposes the service provided by the native FirefoxDriver executable. +/// +public sealed class FirefoxDriverService : DriverService { + private const string DefaultFirefoxDriverServiceFileName = "geckodriver"; + /// - /// Exposes the service provided by the native FirefoxDriver executable. + /// Initializes a new instance of the class. /// - public sealed class FirefoxDriverService : DriverService + /// The full path to the Firefox driver executable. + /// The file name of the Firefox driver executable. + /// The port on which the Firefox driver executable should listen. + private FirefoxDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, port, executableFileName) { - private const string DefaultFirefoxDriverServiceFileName = "geckodriver"; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the Firefox driver executable. - /// The file name of the Firefox driver executable. - /// The port on which the Firefox driver executable should listen. - private FirefoxDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, port, executableFileName) - { - } + } - /// - protected override DriverOptions GetDefaultDriverOptions() - { - return new FirefoxOptions(); - } + /// + protected override DriverOptions GetDefaultDriverOptions() + { + return new FirefoxOptions(); + } - /// - /// Gets or sets the location of the Firefox binary executable. - /// - /// A or value indicates no binary executable path to specify. - public string? FirefoxBinaryPath { get; set; } - - /// - /// Gets or sets the port used by the driver executable to communicate with the browser. - /// - /// A negative or zero value indicates no port value to specify. - public int BrowserCommunicationPort { get; set; } = -1; - - /// - /// Gets or sets the value of the IP address of the host adapter used by the driver - /// executable to communicate with the browser. - /// - /// A or value indicates no marionette host adapter to specify. - public string? BrowserCommunicationHost { get; set; } - - /// - /// Gets or sets the value of the IP address of the host adapter on which the - /// service should listen for connections. - /// - /// A or value indicates no host to specify. - public string? Host { get; set; } - - /// - /// Gets or sets a value indicating whether to connect to an already-running - /// instance of Firefox. - /// - public bool ConnectToRunningBrowser { get; set; } - - /// - /// Gets or sets a value indicating whether to open the Firefox Browser Toolbox - /// when Firefox is launched. - /// - public bool OpenBrowserToolbox { get; set; } - - /// - /// Gets or sets the level at which log output is displayed. - /// - /// - /// This is largely equivalent to setting the - /// property, except the log level is set when the driver launches, instead of - /// when the browser is launched, meaning that initial driver logging before - /// initiation of a session can be controlled. - /// - public FirefoxDriverLogLevel LogLevel { get; set; } = FirefoxDriverLogLevel.Default; - - /// - /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. - /// - protected override TimeSpan TerminationTimeout - { - // Use a very small timeout for terminating the Firefox driver, - // because the executable does not have a clean shutdown command, - // which means we have to kill the process. Using a short timeout - // gets us to the termination point much faster. - get => TimeSpan.FromMilliseconds(100); - } + /// + /// Gets or sets the location of the Firefox binary executable. + /// + /// A or value indicates no binary executable path to specify. + public string? FirefoxBinaryPath { get; set; } - /// - /// Gets a value indicating whether the service has a shutdown API that can be called to terminate - /// it gracefully before forcing a termination. - /// - protected override bool HasShutdown - { - // The Firefox driver executable does not have a clean shutdown command, - // which means we have to kill the process. - get => false; - } + /// + /// Gets or sets the port used by the driver executable to communicate with the browser. + /// + /// A negative or zero value indicates no port value to specify. + public int BrowserCommunicationPort { get; set; } = -1; + + /// + /// Gets or sets the value of the IP address of the host adapter used by the driver + /// executable to communicate with the browser. + /// + /// A or value indicates no marionette host adapter to specify. + public string? BrowserCommunicationHost { get; set; } + + /// + /// Gets or sets the value of the IP address of the host adapter on which the + /// service should listen for connections. + /// + /// A or value indicates no host to specify. + public string? Host { get; set; } - /// - /// Gets the command-line arguments for the driver service. - /// - protected override string CommandLineArguments + /// + /// Gets or sets a value indicating whether to connect to an already-running + /// instance of Firefox. + /// + public bool ConnectToRunningBrowser { get; set; } + + /// + /// Gets or sets a value indicating whether to open the Firefox Browser Toolbox + /// when Firefox is launched. + /// + public bool OpenBrowserToolbox { get; set; } + + /// + /// Gets or sets the level at which log output is displayed. + /// + /// + /// This is largely equivalent to setting the + /// property, except the log level is set when the driver launches, instead of + /// when the browser is launched, meaning that initial driver logging before + /// initiation of a session can be controlled. + /// + public FirefoxDriverLogLevel LogLevel { get; set; } = FirefoxDriverLogLevel.Default; + + /// + /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. + /// + protected override TimeSpan TerminationTimeout + { + // Use a very small timeout for terminating the Firefox driver, + // because the executable does not have a clean shutdown command, + // which means we have to kill the process. Using a short timeout + // gets us to the termination point much faster. + get => TimeSpan.FromMilliseconds(100); + } + + /// + /// Gets a value indicating whether the service has a shutdown API that can be called to terminate + /// it gracefully before forcing a termination. + /// + protected override bool HasShutdown + { + // The Firefox driver executable does not have a clean shutdown command, + // which means we have to kill the process. + get => false; + } + + /// + /// Gets the command-line arguments for the driver service. + /// + protected override string CommandLineArguments + { + get { - get + StringBuilder argsBuilder = new StringBuilder(); + if (this.ConnectToRunningBrowser) { - StringBuilder argsBuilder = new StringBuilder(); - if (this.ConnectToRunningBrowser) - { - argsBuilder.Append(" --connect-existing"); - } - else - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --websocket-port {0}", PortUtilities.FindFreePort())); - } - - if (this.BrowserCommunicationPort > 0) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --marionette-port {0}", this.BrowserCommunicationPort); - } - - if (!string.IsNullOrEmpty(this.BrowserCommunicationHost)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --marionette-host \"{0}\"", this.BrowserCommunicationHost); - } - - if (this.Port > 0) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --port {0}", this.Port); - } - - if (!string.IsNullOrEmpty(this.FirefoxBinaryPath)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --binary \"{0}\"", this.FirefoxBinaryPath); - } - - if (!string.IsNullOrEmpty(this.Host)) - { - argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --host \"{0}\"", this.Host); - } - - if (this.LogLevel != FirefoxDriverLogLevel.Default) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --log {0}", this.LogLevel.ToString().ToLowerInvariant())); - } - - if (this.OpenBrowserToolbox) - { - argsBuilder.Append(" --jsdebugger"); - } - - return argsBuilder.ToString().Trim(); + argsBuilder.Append(" --connect-existing"); + } + else + { + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --websocket-port {0}", PortUtilities.FindFreePort())); } - } - /// - /// Creates a default instance of the FirefoxDriverService. - /// - /// A FirefoxDriverService that implements default settings. - public static FirefoxDriverService CreateDefaultService() - { - return new FirefoxDriverService(null, null, PortUtilities.FindFreePort()); - } + if (this.BrowserCommunicationPort > 0) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --marionette-port {0}", this.BrowserCommunicationPort); + } + if (!string.IsNullOrEmpty(this.BrowserCommunicationHost)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --marionette-host \"{0}\"", this.BrowserCommunicationHost); + } - /// - /// Creates a default instance of the FirefoxDriverService using a specified path to the Firefox driver executable. - /// - /// The path to the executable or the directory containing the Firefox driver executable. - /// A FirefoxDriverService using a random port. - public static FirefoxDriverService CreateDefaultService(string? driverPath) - { - if (File.Exists(driverPath)) + if (this.Port > 0) { - string fileName = Path.GetFileName(driverPath); - string driverFolder = Path.GetDirectoryName(driverPath)!; + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --port {0}", this.Port); + } - return CreateDefaultService(driverFolder, fileName); + if (!string.IsNullOrEmpty(this.FirefoxBinaryPath)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --binary \"{0}\"", this.FirefoxBinaryPath); } - else + + if (!string.IsNullOrEmpty(this.Host)) + { + argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --host \"{0}\"", this.Host); + } + + if (this.LogLevel != FirefoxDriverLogLevel.Default) { - string fileName = FirefoxDriverServiceFileName(); - string? driverFolder = driverPath; + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --log {0}", this.LogLevel.ToString().ToLowerInvariant())); + } - return CreateDefaultService(driverFolder, fileName); + if (this.OpenBrowserToolbox) + { + argsBuilder.Append(" --jsdebugger"); } + + return argsBuilder.ToString().Trim(); } + } + + /// + /// Creates a default instance of the FirefoxDriverService. + /// + /// A FirefoxDriverService that implements default settings. + public static FirefoxDriverService CreateDefaultService() + { + return new FirefoxDriverService(null, null, PortUtilities.FindFreePort()); + } + - /// - /// Creates a default instance of the FirefoxDriverService using a specified path to the Firefox driver executable with the given name. - /// - /// The directory containing the Firefox driver executable. - /// The name of the Firefox driver executable file. - /// A FirefoxDriverService using a random port. - public static FirefoxDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + /// + /// Creates a default instance of the FirefoxDriverService using a specified path to the Firefox driver executable. + /// + /// The path to the executable or the directory containing the Firefox driver executable. + /// A FirefoxDriverService using a random port. + public static FirefoxDriverService CreateDefaultService(string? driverPath) + { + if (File.Exists(driverPath)) { - return new FirefoxDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); - } + string fileName = Path.GetFileName(driverPath); + string driverFolder = Path.GetDirectoryName(driverPath)!; - /// - /// Returns the Firefox driver filename for the currently running platform - /// - /// The file name of the Firefox driver service executable. - private static string FirefoxDriverServiceFileName() + return CreateDefaultService(driverFolder, fileName); + } + else { - string fileName = DefaultFirefoxDriverServiceFileName; + string fileName = FirefoxDriverServiceFileName(); + string? driverFolder = driverPath; - // Unfortunately, detecting the currently running platform isn't as - // straightforward as you might hope. - // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform - // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx - const PlatformID PlatformIDMonoUnix = (PlatformID)128; + return CreateDefaultService(driverFolder, fileName); + } + } - switch (Environment.OSVersion.Platform) - { - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.Win32Windows: - case PlatformID.WinCE: - fileName += ".exe"; - break; - - case PlatformID.MacOSX: - case PlatformID.Unix: - case PlatformIDMonoUnix: - break; - - // Don't handle the Xbox case. Let default handle it. - // case PlatformID.Xbox: - // break; - default: - throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); - } + /// + /// Creates a default instance of the FirefoxDriverService using a specified path to the Firefox driver executable with the given name. + /// + /// The directory containing the Firefox driver executable. + /// The name of the Firefox driver executable file. + /// A FirefoxDriverService using a random port. + public static FirefoxDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + { + return new FirefoxDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); + } - return fileName; + /// + /// Returns the Firefox driver filename for the currently running platform + /// + /// The file name of the Firefox driver service executable. + private static string FirefoxDriverServiceFileName() + { + string fileName = DefaultFirefoxDriverServiceFileName; + + // Unfortunately, detecting the currently running platform isn't as + // straightforward as you might hope. + // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform + // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx + const PlatformID PlatformIDMonoUnix = (PlatformID)128; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + case PlatformID.Win32S: + case PlatformID.Win32Windows: + case PlatformID.WinCE: + fileName += ".exe"; + break; + + case PlatformID.MacOSX: + case PlatformID.Unix: + case PlatformIDMonoUnix: + break; + + // Don't handle the Xbox case. Let default handle it. + // case PlatformID.Xbox: + // break; + default: + throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); } + + return fileName; } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxExtension.cs b/dotnet/src/webdriver/Firefox/FirefoxExtension.cs index c0b6460404c3b..aa4c0ce0f65a0 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxExtension.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxExtension.cs @@ -25,174 +25,173 @@ using System.Text.Json.Nodes; using System.Xml; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Provides the ability to install extensions into a . +/// +public class FirefoxExtension { + private const string EmNamespaceUri = "http://www.mozilla.org/2004/em-rdf#"; + private const string RdfManifestFileName = "install.rdf"; + private const string JsonManifestFileName = "manifest.json"; + + private readonly string extensionFileName; + private readonly string extensionResourceId; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the file containing the Firefox extension. + /// WebDriver attempts to resolve the parameter + /// by looking first for the specified file in the directory of the calling assembly, + /// then using the full path to the file, if a full path is provided. + /// If is . + public FirefoxExtension(string fileName) + : this(fileName, string.Empty) + { + } + /// - /// Provides the ability to install extensions into a . + /// Initializes a new instance of the class. /// - public class FirefoxExtension + /// The name of the file containing the Firefox extension. + /// The ID of the resource within the assembly containing the extension + /// if the file is not present in the file system. + /// WebDriver attempts to resolve the parameter + /// by looking first for the specified file in the directory of the calling assembly, + /// then using the full path to the file, if a full path is provided. If the file is + /// not found in the file system, WebDriver attempts to locate a resource in the + /// executing assembly with the name specified by the + /// parameter. + /// If or are . + internal FirefoxExtension(string fileName, string resourceId) { - private const string EmNamespaceUri = "http://www.mozilla.org/2004/em-rdf#"; - private const string RdfManifestFileName = "install.rdf"; - private const string JsonManifestFileName = "manifest.json"; - - private readonly string extensionFileName; - private readonly string extensionResourceId; - - /// - /// Initializes a new instance of the class. - /// - /// The name of the file containing the Firefox extension. - /// WebDriver attempts to resolve the parameter - /// by looking first for the specified file in the directory of the calling assembly, - /// then using the full path to the file, if a full path is provided. - /// If is . - public FirefoxExtension(string fileName) - : this(fileName, string.Empty) + this.extensionFileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); + this.extensionResourceId = resourceId ?? throw new ArgumentNullException(nameof(resourceId)); + } + + /// + /// Installs the extension into a profile directory. + /// + /// The Firefox profile directory into which to install the extension. + /// If is . + public void Install(string profileDirectory) + { + DirectoryInfo info = new DirectoryInfo(profileDirectory); + string stagingDirectoryName = Path.Combine(Path.GetTempPath(), info.Name + ".staging"); + string tempFileName = Path.Combine(stagingDirectoryName, Path.GetFileName(this.extensionFileName)); + if (Directory.Exists(tempFileName)) { + Directory.Delete(tempFileName, true); } - /// - /// Initializes a new instance of the class. - /// - /// The name of the file containing the Firefox extension. - /// The ID of the resource within the assembly containing the extension - /// if the file is not present in the file system. - /// WebDriver attempts to resolve the parameter - /// by looking first for the specified file in the directory of the calling assembly, - /// then using the full path to the file, if a full path is provided. If the file is - /// not found in the file system, WebDriver attempts to locate a resource in the - /// executing assembly with the name specified by the - /// parameter. - /// If or are . - internal FirefoxExtension(string fileName, string resourceId) + // First, expand the .xpi archive into a temporary location. + Directory.CreateDirectory(tempFileName); + Stream zipFileStream = ResourceUtilities.GetResourceStream(this.extensionFileName, this.extensionResourceId); + using (ZipArchive extensionZipArchive = new ZipArchive(zipFileStream, ZipArchiveMode.Read)) { - this.extensionFileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); - this.extensionResourceId = resourceId ?? throw new ArgumentNullException(nameof(resourceId)); + extensionZipArchive.ExtractToDirectory(tempFileName); } - /// - /// Installs the extension into a profile directory. - /// - /// The Firefox profile directory into which to install the extension. - /// If is . - public void Install(string profileDirectory) + // Then, copy the contents of the temporarly location into the + // proper location in the Firefox profile directory. + string id = GetExtensionId(tempFileName); + string extensionDirectory = Path.Combine(Path.Combine(profileDirectory, "extensions"), id); + if (Directory.Exists(extensionDirectory)) { - DirectoryInfo info = new DirectoryInfo(profileDirectory); - string stagingDirectoryName = Path.Combine(Path.GetTempPath(), info.Name + ".staging"); - string tempFileName = Path.Combine(stagingDirectoryName, Path.GetFileName(this.extensionFileName)); - if (Directory.Exists(tempFileName)) - { - Directory.Delete(tempFileName, true); - } + Directory.Delete(extensionDirectory, true); + } - // First, expand the .xpi archive into a temporary location. - Directory.CreateDirectory(tempFileName); - Stream zipFileStream = ResourceUtilities.GetResourceStream(this.extensionFileName, this.extensionResourceId); - using (ZipArchive extensionZipArchive = new ZipArchive(zipFileStream, ZipArchiveMode.Read)) - { - extensionZipArchive.ExtractToDirectory(tempFileName); - } + Directory.CreateDirectory(extensionDirectory); + FileUtilities.CopyDirectory(tempFileName, extensionDirectory); - // Then, copy the contents of the temporarly location into the - // proper location in the Firefox profile directory. - string id = GetExtensionId(tempFileName); - string extensionDirectory = Path.Combine(Path.Combine(profileDirectory, "extensions"), id); - if (Directory.Exists(extensionDirectory)) - { - Directory.Delete(extensionDirectory, true); - } + // By deleting the staging directory, we also delete the temporarily + // expanded extension, which we copied into the profile. + FileUtilities.DeleteDirectory(stagingDirectoryName); + } - Directory.CreateDirectory(extensionDirectory); - FileUtilities.CopyDirectory(tempFileName, extensionDirectory); + private static string GetExtensionId(string root) + { + // Checks if manifest.json or install.rdf file exists and extracts + // the addon/extenion id from the file accordingly + string manifestJsonPath = Path.Combine(root, JsonManifestFileName); + string installRdfPath = Path.Combine(root, RdfManifestFileName); - // By deleting the staging directory, we also delete the temporarily - // expanded extension, which we copied into the profile. - FileUtilities.DeleteDirectory(stagingDirectoryName); + if (File.Exists(installRdfPath)) + { + return ReadIdFromInstallRdf(root); } - private static string GetExtensionId(string root) + if (File.Exists(manifestJsonPath)) { - // Checks if manifest.json or install.rdf file exists and extracts - // the addon/extenion id from the file accordingly - string manifestJsonPath = Path.Combine(root, JsonManifestFileName); - string installRdfPath = Path.Combine(root, RdfManifestFileName); - - if (File.Exists(installRdfPath)) - { - return ReadIdFromInstallRdf(root); - } - - if (File.Exists(manifestJsonPath)) - { - return ReadIdFromManifestJson(root); - } - - throw new WebDriverException("Extension should contain either install.rdf or manifest.json metadata file"); + return ReadIdFromManifestJson(root); } - private static string ReadIdFromInstallRdf(string root) + throw new WebDriverException("Extension should contain either install.rdf or manifest.json metadata file"); + } + + private static string ReadIdFromInstallRdf(string root) + { + string id; + string installRdf = Path.Combine(root, "install.rdf"); + try { - string id; - string installRdf = Path.Combine(root, "install.rdf"); - try - { - XmlDocument rdfXmlDocument = new XmlDocument(); - rdfXmlDocument.Load(installRdf); + XmlDocument rdfXmlDocument = new XmlDocument(); + rdfXmlDocument.Load(installRdf); - XmlNamespaceManager rdfNamespaceManager = new XmlNamespaceManager(rdfXmlDocument.NameTable); - rdfNamespaceManager.AddNamespace("em", EmNamespaceUri); - rdfNamespaceManager.AddNamespace("RDF", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + XmlNamespaceManager rdfNamespaceManager = new XmlNamespaceManager(rdfXmlDocument.NameTable); + rdfNamespaceManager.AddNamespace("em", EmNamespaceUri); + rdfNamespaceManager.AddNamespace("RDF", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - XmlNode? node = rdfXmlDocument.SelectSingleNode("//em:id", rdfNamespaceManager); - if (node == null) - { - XmlNode? descriptionNode = rdfXmlDocument.SelectSingleNode("//RDF:Description", rdfNamespaceManager); - XmlAttribute? attribute = descriptionNode?.Attributes?["id", EmNamespaceUri]; - if (attribute == null) - { - throw new WebDriverException("Cannot locate node containing extension id: " + installRdf); - } - - id = attribute.Value; - } - else + XmlNode? node = rdfXmlDocument.SelectSingleNode("//em:id", rdfNamespaceManager); + if (node == null) + { + XmlNode? descriptionNode = rdfXmlDocument.SelectSingleNode("//RDF:Description", rdfNamespaceManager); + XmlAttribute? attribute = descriptionNode?.Attributes?["id", EmNamespaceUri]; + if (attribute == null) { - id = node.InnerText; + throw new WebDriverException("Cannot locate node containing extension id: " + installRdf); } - if (string.IsNullOrEmpty(id)) - { - throw new FileNotFoundException("Cannot install extension with ID: " + id); - } + id = attribute.Value; } - catch (Exception e) + else { - throw new WebDriverException("Error installing extension", e); + id = node.InnerText; } - return id; + if (string.IsNullOrEmpty(id)) + { + throw new FileNotFoundException("Cannot install extension with ID: " + id); + } } - - private static string ReadIdFromManifestJson(string root) + catch (Exception e) { - string id = string.Empty; - string manifestJsonPath = Path.Combine(root, JsonManifestFileName); + throw new WebDriverException("Error installing extension", e); + } - var manifestObject = JsonNode.Parse(File.ReadAllText(manifestJsonPath)); - if (manifestObject!["applications"]?["gecko"]?["id"] is { } idNode) - { - id = idNode.ToString().Trim(); - } + return id; + } - if (string.IsNullOrEmpty(id)) - { - string addInName = manifestObject["name"]!.ToString().Replace(" ", ""); - string addInVersion = manifestObject["version"]!.ToString(); - id = string.Format(CultureInfo.InvariantCulture, "{0}@{1}", addInName, addInVersion); - } + private static string ReadIdFromManifestJson(string root) + { + string id = string.Empty; + string manifestJsonPath = Path.Combine(root, JsonManifestFileName); - return id; + var manifestObject = JsonNode.Parse(File.ReadAllText(manifestJsonPath)); + if (manifestObject!["applications"]?["gecko"]?["id"] is { } idNode) + { + id = idNode.ToString().Trim(); } + + if (string.IsNullOrEmpty(id)) + { + string addInName = manifestObject["name"]!.ToString().Replace(" ", ""); + string addInVersion = manifestObject["version"]!.ToString(); + id = string.Format(CultureInfo.InvariantCulture, "{0}@{1}", addInName, addInVersion); + } + + return id; } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxOptions.cs b/dotnet/src/webdriver/Firefox/FirefoxOptions.cs index c21eb763039e2..c9f0664cbd4c4 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxOptions.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxOptions.cs @@ -20,343 +20,342 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Class to manage options specific to +/// +/// +/// Used with the marionette executable wires.exe. +/// +/// +/// +/// FirefoxOptions options = new FirefoxOptions(); +/// +/// +/// For use with FirefoxDriver: +/// +/// +/// FirefoxDriver driver = new FirefoxDriver(options); +/// +/// +/// For use with RemoteWebDriver: +/// +/// +/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); +/// +/// +public class FirefoxOptions : DriverOptions { + private const string BrowserNameValue = "firefox"; + + private const string IsMarionetteCapability = "marionette"; + private const string FirefoxLegacyProfileCapability = "firefox_profile"; + private const string FirefoxLegacyBinaryCapability = "firefox_binary"; + private const string FirefoxProfileCapability = "profile"; + private const string FirefoxBinaryCapability = "binary"; + private const string FirefoxArgumentsCapability = "args"; + private const string FirefoxLogCapability = "log"; + private const string FirefoxPrefsCapability = "prefs"; + private const string FirefoxEnvCapability = "env"; + private const string FirefoxOptionsCapability = "moz:firefoxOptions"; + private const string FirefoxEnableDevToolsProtocolCapability = "moz:debuggerAddress"; + private readonly List firefoxArguments = new List(); + private readonly Dictionary profilePreferences = new Dictionary(); + private readonly Dictionary additionalFirefoxOptions = new Dictionary(); + private readonly Dictionary environmentVariables = new Dictionary(); + /// - /// Class to manage options specific to + /// Initializes a new instance of the class. /// - /// - /// Used with the marionette executable wires.exe. - /// - /// - /// - /// FirefoxOptions options = new FirefoxOptions(); - /// - /// - /// For use with FirefoxDriver: - /// - /// - /// FirefoxDriver driver = new FirefoxDriver(options); - /// - /// - /// For use with RemoteWebDriver: - /// - /// - /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); - /// - /// - public class FirefoxOptions : DriverOptions + public FirefoxOptions() + : base() { - private const string BrowserNameValue = "firefox"; - - private const string IsMarionetteCapability = "marionette"; - private const string FirefoxLegacyProfileCapability = "firefox_profile"; - private const string FirefoxLegacyBinaryCapability = "firefox_binary"; - private const string FirefoxProfileCapability = "profile"; - private const string FirefoxBinaryCapability = "binary"; - private const string FirefoxArgumentsCapability = "args"; - private const string FirefoxLogCapability = "log"; - private const string FirefoxPrefsCapability = "prefs"; - private const string FirefoxEnvCapability = "env"; - private const string FirefoxOptionsCapability = "moz:firefoxOptions"; - private const string FirefoxEnableDevToolsProtocolCapability = "moz:debuggerAddress"; - private readonly List firefoxArguments = new List(); - private readonly Dictionary profilePreferences = new Dictionary(); - private readonly Dictionary additionalFirefoxOptions = new Dictionary(); - private readonly Dictionary environmentVariables = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - public FirefoxOptions() - : base() - { - this.BrowserName = BrowserNameValue; - this.AddKnownCapabilityName(FirefoxOptions.FirefoxOptionsCapability, "current FirefoxOptions class instance"); - this.AddKnownCapabilityName(FirefoxOptions.IsMarionetteCapability, "UseLegacyImplementation property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxProfileCapability, "Profile property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxBinaryCapability, "BrowserExecutableLocation property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxArgumentsCapability, "AddArguments method"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxPrefsCapability, "SetPreference method"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnvCapability, "SetEnvironmentVariable method"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxLogCapability, "LogLevel property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyProfileCapability, "Profile property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyBinaryCapability, "BrowserExecutableLocation property"); - this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnableDevToolsProtocolCapability, "EnableDevToolsProtocol property"); - // Firefox 129 onwards the CDP protocol will not be enabled by default. Setting this preference will enable it. - // https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/. - this.SetPreference("remote.active-protocols", 3); - } + this.BrowserName = BrowserNameValue; + this.AddKnownCapabilityName(FirefoxOptions.FirefoxOptionsCapability, "current FirefoxOptions class instance"); + this.AddKnownCapabilityName(FirefoxOptions.IsMarionetteCapability, "UseLegacyImplementation property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxProfileCapability, "Profile property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxBinaryCapability, "BrowserExecutableLocation property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxArgumentsCapability, "AddArguments method"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxPrefsCapability, "SetPreference method"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnvCapability, "SetEnvironmentVariable method"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxLogCapability, "LogLevel property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyProfileCapability, "Profile property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxLegacyBinaryCapability, "BrowserExecutableLocation property"); + this.AddKnownCapabilityName(FirefoxOptions.FirefoxEnableDevToolsProtocolCapability, "EnableDevToolsProtocol property"); + // Firefox 129 onwards the CDP protocol will not be enabled by default. Setting this preference will enable it. + // https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/. + this.SetPreference("remote.active-protocols", 3); + } + + /// + /// Gets or sets the object to be used with this instance. + /// + public FirefoxProfile? Profile { get; set; } + + /// + /// Gets or sets the path and file name of the Firefox browser executable. + /// + public override string? BinaryLocation { get; set; } + + /// + /// Gets or sets the path and file name of the Firefox browser executable. + /// + [Obsolete("Use BinaryLocation property instead of BrowserExecutableLocation. This one will be removed soon.")] + public string? BrowserExecutableLocation + { + get => this.BinaryLocation; + set => this.BinaryLocation = value; + } + + /// + /// Gets or sets the logging level of the Firefox driver. + /// + public FirefoxDriverLogLevel LogLevel { get; set; } = FirefoxDriverLogLevel.Default; + + /// + /// Gets or sets a value indicating whether to enable the DevTools protocol for the launched browser. + /// + public bool EnableDevToolsProtocol { get; set; } + + /// + /// Gets or sets the options for automating Firefox on Android. + /// + public FirefoxAndroidOptions? AndroidOptions { get; set; } - /// - /// Gets or sets the object to be used with this instance. - /// - public FirefoxProfile? Profile { get; set; } - - /// - /// Gets or sets the path and file name of the Firefox browser executable. - /// - public override string? BinaryLocation { get; set; } - - /// - /// Gets or sets the path and file name of the Firefox browser executable. - /// - [Obsolete("Use BinaryLocation property instead of BrowserExecutableLocation. This one will be removed soon.")] - public string? BrowserExecutableLocation + /// + /// Adds an argument to be used in launching the Firefox browser. + /// + /// The argument to add. + /// Arguments must be preceded by two dashes ("--"). + /// If is or . + public void AddArgument(string argumentName) + { + if (string.IsNullOrEmpty(argumentName)) { - get => this.BinaryLocation; - set => this.BinaryLocation = value; + throw new ArgumentException("argumentName must not be null or empty", nameof(argumentName)); } - /// - /// Gets or sets the logging level of the Firefox driver. - /// - public FirefoxDriverLogLevel LogLevel { get; set; } = FirefoxDriverLogLevel.Default; - - /// - /// Gets or sets a value indicating whether to enable the DevTools protocol for the launched browser. - /// - public bool EnableDevToolsProtocol { get; set; } - - /// - /// Gets or sets the options for automating Firefox on Android. - /// - public FirefoxAndroidOptions? AndroidOptions { get; set; } - - /// - /// Adds an argument to be used in launching the Firefox browser. - /// - /// The argument to add. - /// Arguments must be preceded by two dashes ("--"). - /// If is or . - public void AddArgument(string argumentName) - { - if (string.IsNullOrEmpty(argumentName)) - { - throw new ArgumentException("argumentName must not be null or empty", nameof(argumentName)); - } + this.AddArguments(argumentName); + } - this.AddArguments(argumentName); - } + /// + /// Adds a list arguments to be used in launching the Firefox browser. + /// + /// An array of arguments to add. + /// Each argument must be preceded by two dashes ("--"). + /// If is . + public void AddArguments(params string[] argumentsToAdd) + { + this.AddArguments((IEnumerable)argumentsToAdd); + } - /// - /// Adds a list arguments to be used in launching the Firefox browser. - /// - /// An array of arguments to add. - /// Each argument must be preceded by two dashes ("--"). - /// If is . - public void AddArguments(params string[] argumentsToAdd) + /// + /// Adds a list arguments to be used in launching the Firefox browser. + /// + /// An array of arguments to add. + /// If is . + public void AddArguments(IEnumerable argumentsToAdd) + { + if (argumentsToAdd == null) { - this.AddArguments((IEnumerable)argumentsToAdd); + throw new ArgumentNullException(nameof(argumentsToAdd), "argumentsToAdd must not be null"); } - /// - /// Adds a list arguments to be used in launching the Firefox browser. - /// - /// An array of arguments to add. - /// If is . - public void AddArguments(IEnumerable argumentsToAdd) - { - if (argumentsToAdd == null) - { - throw new ArgumentNullException(nameof(argumentsToAdd), "argumentsToAdd must not be null"); - } + this.firefoxArguments.AddRange(argumentsToAdd); + } - this.firefoxArguments.AddRange(argumentsToAdd); - } + /// + /// Sets a preference in the profile used by Firefox. + /// + /// Name of the preference to set. + /// Value of the preference to set. + /// If is or . + public void SetPreference(string preferenceName, bool preferenceValue) + { + this.SetPreferenceValue(preferenceName, preferenceValue); + } - /// - /// Sets a preference in the profile used by Firefox. - /// - /// Name of the preference to set. - /// Value of the preference to set. - /// If is or . - public void SetPreference(string preferenceName, bool preferenceValue) + /// + /// Sets a preference in the profile used by Firefox. + /// + /// Name of the preference to set. + /// Value of the preference to set. + /// If is or . + public void SetPreference(string preferenceName, int preferenceValue) + { + this.SetPreferenceValue(preferenceName, preferenceValue); + } + + /// + /// Sets a preference in the profile used by Firefox. + /// + /// Name of the preference to set. + /// Value of the preference to set. + /// If is or . + public void SetPreference(string preferenceName, long preferenceValue) + { + this.SetPreferenceValue(preferenceName, preferenceValue); + } + + /// + /// Sets a preference in the profile used by Firefox. + /// + /// Name of the preference to set. + /// Value of the preference to set. + /// If is or . + public void SetPreference(string preferenceName, double preferenceValue) + { + this.SetPreferenceValue(preferenceName, preferenceValue); + } + + /// + /// Sets a preference in the profile used by Firefox. + /// + /// Name of the preference to set. + /// Value of the preference to set. + /// If is or . + public void SetPreference(string preferenceName, string preferenceValue) + { + this.SetPreferenceValue(preferenceName, preferenceValue); + } + + /// + /// Sets an environment variable to be set in the operating system's environment under which the Firefox browser is launched. + /// + /// The name of the environment variable. + /// The value of the environment variable. + /// If is or . + public void SetEnvironmentVariable(string variableName, string? variableValue) + { + if (string.IsNullOrEmpty(variableName)) { - this.SetPreferenceValue(preferenceName, preferenceValue); + throw new ArgumentException("Environment variable name cannot be null or an empty string"); } - /// - /// Sets a preference in the profile used by Firefox. - /// - /// Name of the preference to set. - /// Value of the preference to set. - /// If is or . - public void SetPreference(string preferenceName, int preferenceValue) + this.environmentVariables[variableName] = variableValue ?? string.Empty; + } + + /// + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the Firefox driver. + /// + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// Calling this method adds capabilities to the Firefox-specific options object passed to + /// geckodriver.exe (property name 'moz:firefoxOptions'). + public void AddAdditionalFirefoxOption(string optionName, object optionValue) + { + this.ValidateCapabilityName(optionName); + this.additionalFirefoxOptions[optionName] = optionValue; + } + + /// + /// Returns DesiredCapabilities for Firefox with these options included as + /// capabilities. This does not copy the options. Further changes will be + /// reflected in the returned capabilities. + /// + /// The DesiredCapabilities for Firefox with these options. + public override ICapabilities ToCapabilities() + { + IWritableCapabilities capabilities = GenerateDesiredCapabilities(true); + Dictionary firefoxOptions = this.GenerateFirefoxOptionsDictionary(); + capabilities.SetCapability(FirefoxOptionsCapability, firefoxOptions); + if (this.EnableDevToolsProtocol) { - this.SetPreferenceValue(preferenceName, preferenceValue); + capabilities.SetCapability(FirefoxEnableDevToolsProtocolCapability, true); } - /// - /// Sets a preference in the profile used by Firefox. - /// - /// Name of the preference to set. - /// Value of the preference to set. - /// If is or . - public void SetPreference(string preferenceName, long preferenceValue) + return capabilities.AsReadOnly(); + } + + private Dictionary GenerateFirefoxOptionsDictionary() + { + Dictionary firefoxOptions = new Dictionary(); + + if (this.Profile != null) { - this.SetPreferenceValue(preferenceName, preferenceValue); + firefoxOptions[FirefoxProfileCapability] = this.Profile.ToBase64String(); } - /// - /// Sets a preference in the profile used by Firefox. - /// - /// Name of the preference to set. - /// Value of the preference to set. - /// If is or . - public void SetPreference(string preferenceName, double preferenceValue) + if (!string.IsNullOrEmpty(this.BinaryLocation)) { - this.SetPreferenceValue(preferenceName, preferenceValue); + firefoxOptions[FirefoxBinaryCapability] = this.BinaryLocation!; } - /// - /// Sets a preference in the profile used by Firefox. - /// - /// Name of the preference to set. - /// Value of the preference to set. - /// If is or . - public void SetPreference(string preferenceName, string preferenceValue) + if (this.LogLevel != FirefoxDriverLogLevel.Default) { - this.SetPreferenceValue(preferenceName, preferenceValue); + Dictionary logObject = new Dictionary(); + logObject["level"] = this.LogLevel.ToString().ToLowerInvariant(); + firefoxOptions[FirefoxLogCapability] = logObject; } - /// - /// Sets an environment variable to be set in the operating system's environment under which the Firefox browser is launched. - /// - /// The name of the environment variable. - /// The value of the environment variable. - /// If is or . - public void SetEnvironmentVariable(string variableName, string? variableValue) + if (this.firefoxArguments.Count > 0) { - if (string.IsNullOrEmpty(variableName)) - { - throw new ArgumentException("Environment variable name cannot be null or an empty string"); - } + List args = [.. this.firefoxArguments]; - this.environmentVariables[variableName] = variableValue ?? string.Empty; + firefoxOptions[FirefoxArgumentsCapability] = args; } - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the Firefox driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// Calling this method adds capabilities to the Firefox-specific options object passed to - /// geckodriver.exe (property name 'moz:firefoxOptions'). - public void AddAdditionalFirefoxOption(string optionName, object optionValue) + if (this.profilePreferences.Count > 0) { - this.ValidateCapabilityName(optionName); - this.additionalFirefoxOptions[optionName] = optionValue; + firefoxOptions[FirefoxPrefsCapability] = this.profilePreferences; } - /// - /// Returns DesiredCapabilities for Firefox with these options included as - /// capabilities. This does not copy the options. Further changes will be - /// reflected in the returned capabilities. - /// - /// The DesiredCapabilities for Firefox with these options. - public override ICapabilities ToCapabilities() + if (this.environmentVariables.Count > 0) { - IWritableCapabilities capabilities = GenerateDesiredCapabilities(true); - Dictionary firefoxOptions = this.GenerateFirefoxOptionsDictionary(); - capabilities.SetCapability(FirefoxOptionsCapability, firefoxOptions); - if (this.EnableDevToolsProtocol) - { - capabilities.SetCapability(FirefoxEnableDevToolsProtocolCapability, true); - } - - return capabilities.AsReadOnly(); + firefoxOptions[FirefoxEnvCapability] = this.environmentVariables; } - private Dictionary GenerateFirefoxOptionsDictionary() + if (this.AndroidOptions != null) { - Dictionary firefoxOptions = new Dictionary(); - - if (this.Profile != null) - { - firefoxOptions[FirefoxProfileCapability] = this.Profile.ToBase64String(); - } - - if (!string.IsNullOrEmpty(this.BinaryLocation)) - { - firefoxOptions[FirefoxBinaryCapability] = this.BinaryLocation!; - } - - if (this.LogLevel != FirefoxDriverLogLevel.Default) - { - Dictionary logObject = new Dictionary(); - logObject["level"] = this.LogLevel.ToString().ToLowerInvariant(); - firefoxOptions[FirefoxLogCapability] = logObject; - } - - if (this.firefoxArguments.Count > 0) - { - List args = [.. this.firefoxArguments]; - - firefoxOptions[FirefoxArgumentsCapability] = args; - } - - if (this.profilePreferences.Count > 0) - { - firefoxOptions[FirefoxPrefsCapability] = this.profilePreferences; - } - - if (this.environmentVariables.Count > 0) - { - firefoxOptions[FirefoxEnvCapability] = this.environmentVariables; - } - - if (this.AndroidOptions != null) - { - AddAndroidOptions(this.AndroidOptions, firefoxOptions); - } - - foreach (KeyValuePair pair in this.additionalFirefoxOptions) - { - firefoxOptions.Add(pair.Key, pair.Value); - } - - return firefoxOptions; + AddAndroidOptions(this.AndroidOptions, firefoxOptions); } - private void SetPreferenceValue(string preferenceName, object preferenceValue) + foreach (KeyValuePair pair in this.additionalFirefoxOptions) { - if (string.IsNullOrEmpty(preferenceName)) - { - throw new ArgumentException("Preference name may not be null an empty string.", nameof(preferenceName)); - } - - this.profilePreferences[preferenceName] = preferenceValue; + firefoxOptions.Add(pair.Key, pair.Value); } - private static void AddAndroidOptions(FirefoxAndroidOptions androidOptions, Dictionary firefoxOptions) + return firefoxOptions; + } + + private void SetPreferenceValue(string preferenceName, object preferenceValue) + { + if (string.IsNullOrEmpty(preferenceName)) { - firefoxOptions["androidPackage"] = androidOptions.AndroidPackage; + throw new ArgumentException("Preference name may not be null an empty string.", nameof(preferenceName)); + } - if (!string.IsNullOrEmpty(androidOptions.AndroidDeviceSerial)) - { - firefoxOptions["androidDeviceSerial"] = androidOptions.AndroidDeviceSerial!; - } + this.profilePreferences[preferenceName] = preferenceValue; + } - if (!string.IsNullOrEmpty(androidOptions.AndroidActivity)) - { - firefoxOptions["androidActivity"] = androidOptions.AndroidActivity!; - } + private static void AddAndroidOptions(FirefoxAndroidOptions androidOptions, Dictionary firefoxOptions) + { + firefoxOptions["androidPackage"] = androidOptions.AndroidPackage; - if (androidOptions.AndroidIntentArguments.Count > 0) - { - List args = [.. androidOptions.AndroidIntentArguments]; + if (!string.IsNullOrEmpty(androidOptions.AndroidDeviceSerial)) + { + firefoxOptions["androidDeviceSerial"] = androidOptions.AndroidDeviceSerial!; + } + + if (!string.IsNullOrEmpty(androidOptions.AndroidActivity)) + { + firefoxOptions["androidActivity"] = androidOptions.AndroidActivity!; + } + + if (androidOptions.AndroidIntentArguments.Count > 0) + { + List args = [.. androidOptions.AndroidIntentArguments]; - firefoxOptions["androidIntentArguments"] = args; - } + firefoxOptions["androidIntentArguments"] = args; } } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxProfile.cs b/dotnet/src/webdriver/Firefox/FirefoxProfile.cs index e25117344d718..ad25a78b0727a 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxProfile.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxProfile.cs @@ -25,326 +25,325 @@ using System.IO.Compression; using System.Text.Json; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Provides the ability to edit the preferences associated with a Firefox profile. +/// +public class FirefoxProfile { + private const string UserPreferencesFileName = "user.js"; + private readonly string? sourceProfileDir; + private readonly bool deleteSource; + private readonly Preferences profilePreferences; + private readonly Dictionary extensions = new Dictionary(); + /// - /// Provides the ability to edit the preferences associated with a Firefox profile. + /// Initializes a new instance of the class. /// - public class FirefoxProfile + public FirefoxProfile() + : this(null) { - private const string UserPreferencesFileName = "user.js"; - private readonly string? sourceProfileDir; - private readonly bool deleteSource; - private readonly Preferences profilePreferences; - private readonly Dictionary extensions = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - public FirefoxProfile() - : this(null) - { - } + } - /// - /// Initializes a new instance of the class using a - /// specific profile directory. - /// - /// The directory containing the profile. - public FirefoxProfile(string? profileDirectory) - : this(profileDirectory, false) - { - } + /// + /// Initializes a new instance of the class using a + /// specific profile directory. + /// + /// The directory containing the profile. + public FirefoxProfile(string? profileDirectory) + : this(profileDirectory, false) + { + } - /// - /// Initializes a new instance of the class using a - /// specific profile directory. - /// - /// The directory containing the profile. - /// Delete the source directory of the profile upon cleaning. - public FirefoxProfile(string? profileDirectory, bool deleteSourceOnClean) - { - this.sourceProfileDir = profileDirectory; - this.deleteSource = deleteSourceOnClean; - this.profilePreferences = this.ReadDefaultPreferences(); - this.profilePreferences.AppendPreferences(this.ReadExistingPreferences()); - } + /// + /// Initializes a new instance of the class using a + /// specific profile directory. + /// + /// The directory containing the profile. + /// Delete the source directory of the profile upon cleaning. + public FirefoxProfile(string? profileDirectory, bool deleteSourceOnClean) + { + this.sourceProfileDir = profileDirectory; + this.deleteSource = deleteSourceOnClean; + this.profilePreferences = this.ReadDefaultPreferences(); + this.profilePreferences.AppendPreferences(this.ReadExistingPreferences()); + } - /// - /// Gets the directory containing the profile. - /// - public string? ProfileDirectory { get; private set; } - - /// - /// Gets or sets a value indicating whether to delete this profile after use with - /// the . - /// - public bool DeleteAfterUse { get; set; } = true; - - /// - /// Converts a base64-encoded string into a . - /// - /// The base64-encoded string containing the profile contents. - /// The constructed . - public static FirefoxProfile FromBase64String(string base64) - { - string destinationDirectory = FileUtilities.GenerateRandomTempDirectoryName("webdriver.{0}.duplicated"); - byte[] zipContent = Convert.FromBase64String(base64); - using (MemoryStream zipStream = new MemoryStream(zipContent)) - { - using (ZipArchive profileZipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read)) - { - profileZipArchive.ExtractToDirectory(destinationDirectory); - } - } + /// + /// Gets the directory containing the profile. + /// + public string? ProfileDirectory { get; private set; } - return new FirefoxProfile(destinationDirectory, true); - } + /// + /// Gets or sets a value indicating whether to delete this profile after use with + /// the . + /// + public bool DeleteAfterUse { get; set; } = true; - /// - /// Adds a Firefox Extension to this profile - /// - /// The path to the new extension - /// If is . - public void AddExtension(string extensionToInstall) + /// + /// Converts a base64-encoded string into a . + /// + /// The base64-encoded string containing the profile contents. + /// The constructed . + public static FirefoxProfile FromBase64String(string base64) + { + string destinationDirectory = FileUtilities.GenerateRandomTempDirectoryName("webdriver.{0}.duplicated"); + byte[] zipContent = Convert.FromBase64String(base64); + using (MemoryStream zipStream = new MemoryStream(zipContent)) { - if (extensionToInstall is null) + using (ZipArchive profileZipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read)) { - throw new ArgumentNullException(nameof(extensionToInstall)); + profileZipArchive.ExtractToDirectory(destinationDirectory); } - - this.extensions.Add(Path.GetFileNameWithoutExtension(extensionToInstall), new FirefoxExtension(extensionToInstall)); } - /// - /// Sets a preference in the profile. - /// - /// The name of the preference to add. - /// A value to add to the profile. - /// If or are . - public void SetPreference(string name, string value) + return new FirefoxProfile(destinationDirectory, true); + } + + /// + /// Adds a Firefox Extension to this profile + /// + /// The path to the new extension + /// If is . + public void AddExtension(string extensionToInstall) + { + if (extensionToInstall is null) { - this.profilePreferences.SetPreference(name, value); + throw new ArgumentNullException(nameof(extensionToInstall)); } - /// - /// Sets a preference in the profile. - /// - /// The name of the preference to add. - /// A value to add to the profile. - /// If is . - public void SetPreference(string name, int value) + this.extensions.Add(Path.GetFileNameWithoutExtension(extensionToInstall), new FirefoxExtension(extensionToInstall)); + } + + /// + /// Sets a preference in the profile. + /// + /// The name of the preference to add. + /// A value to add to the profile. + /// If or are . + public void SetPreference(string name, string value) + { + this.profilePreferences.SetPreference(name, value); + } + + /// + /// Sets a preference in the profile. + /// + /// The name of the preference to add. + /// A value to add to the profile. + /// If is . + public void SetPreference(string name, int value) + { + this.profilePreferences.SetPreference(name, value); + } + + /// + /// Sets a preference in the profile. + /// + /// The name of the preference to add. + /// A value to add to the profile. + /// If is . + public void SetPreference(string name, bool value) + { + this.profilePreferences.SetPreference(name, value); + } + + /// + /// Writes this in-memory representation of a profile to disk. + /// + [MemberNotNull(nameof(ProfileDirectory))] + public void WriteToDisk() + { + this.ProfileDirectory = GenerateProfileDirectoryName(); + if (!string.IsNullOrEmpty(this.sourceProfileDir)) { - this.profilePreferences.SetPreference(name, value); + FileUtilities.CopyDirectory(this.sourceProfileDir!, this.ProfileDirectory); } - - /// - /// Sets a preference in the profile. - /// - /// The name of the preference to add. - /// A value to add to the profile. - /// If is . - public void SetPreference(string name, bool value) + else { - this.profilePreferences.SetPreference(name, value); + Directory.CreateDirectory(this.ProfileDirectory); } - /// - /// Writes this in-memory representation of a profile to disk. - /// - [MemberNotNull(nameof(ProfileDirectory))] - public void WriteToDisk() - { - this.ProfileDirectory = GenerateProfileDirectoryName(); - if (!string.IsNullOrEmpty(this.sourceProfileDir)) - { - FileUtilities.CopyDirectory(this.sourceProfileDir!, this.ProfileDirectory); - } - else - { - Directory.CreateDirectory(this.ProfileDirectory); - } + this.InstallExtensions(this.ProfileDirectory); + this.DeleteLockFiles(this.ProfileDirectory); + this.DeleteExtensionsCache(this.ProfileDirectory); + this.UpdateUserPreferences(this.ProfileDirectory); + } - this.InstallExtensions(this.ProfileDirectory); - this.DeleteLockFiles(this.ProfileDirectory); - this.DeleteExtensionsCache(this.ProfileDirectory); - this.UpdateUserPreferences(this.ProfileDirectory); + /// + /// Cleans this Firefox profile. + /// + /// If this profile is a named profile that existed prior to + /// launching Firefox, the method removes the WebDriver + /// Firefox extension. If the profile is an anonymous profile, the profile + /// is deleted. + public void Clean() + { + if (this.DeleteAfterUse && !string.IsNullOrEmpty(this.ProfileDirectory) && Directory.Exists(this.ProfileDirectory)) + { + FileUtilities.DeleteDirectory(this.ProfileDirectory); } - /// - /// Cleans this Firefox profile. - /// - /// If this profile is a named profile that existed prior to - /// launching Firefox, the method removes the WebDriver - /// Firefox extension. If the profile is an anonymous profile, the profile - /// is deleted. - public void Clean() + if (this.deleteSource && !string.IsNullOrEmpty(this.sourceProfileDir) && Directory.Exists(this.sourceProfileDir)) { - if (this.DeleteAfterUse && !string.IsNullOrEmpty(this.ProfileDirectory) && Directory.Exists(this.ProfileDirectory)) - { - FileUtilities.DeleteDirectory(this.ProfileDirectory); - } - - if (this.deleteSource && !string.IsNullOrEmpty(this.sourceProfileDir) && Directory.Exists(this.sourceProfileDir)) - { - FileUtilities.DeleteDirectory(this.sourceProfileDir); - } + FileUtilities.DeleteDirectory(this.sourceProfileDir); } + } - /// - /// Converts the profile into a base64-encoded string. - /// - /// A base64-encoded string containing the contents of the profile. - public string ToBase64String() - { - string base64zip; - this.WriteToDisk(); + /// + /// Converts the profile into a base64-encoded string. + /// + /// A base64-encoded string containing the contents of the profile. + public string ToBase64String() + { + string base64zip; + this.WriteToDisk(); - using (MemoryStream profileMemoryStream = new MemoryStream()) + using (MemoryStream profileMemoryStream = new MemoryStream()) + { + using (ZipArchive profileZipArchive = new ZipArchive(profileMemoryStream, ZipArchiveMode.Create, true)) { - using (ZipArchive profileZipArchive = new ZipArchive(profileMemoryStream, ZipArchiveMode.Create, true)) + string[] files = Directory.GetFiles(this.ProfileDirectory, "*.*", SearchOption.AllDirectories); + foreach (string file in files) { - string[] files = Directory.GetFiles(this.ProfileDirectory, "*.*", SearchOption.AllDirectories); - foreach (string file in files) - { - string fileNameInZip = file.Substring(this.ProfileDirectory.Length + 1).Replace(Path.DirectorySeparatorChar, '/'); - profileZipArchive.CreateEntryFromFile(file, fileNameInZip); - } + string fileNameInZip = file.Substring(this.ProfileDirectory.Length + 1).Replace(Path.DirectorySeparatorChar, '/'); + profileZipArchive.CreateEntryFromFile(file, fileNameInZip); } - - base64zip = Convert.ToBase64String(profileMemoryStream.ToArray()); - this.Clean(); } - return base64zip; + base64zip = Convert.ToBase64String(profileMemoryStream.ToArray()); + this.Clean(); } - /// - /// Generates a random directory name for the profile. - /// - /// A random directory name for the profile. - private static string GenerateProfileDirectoryName() + return base64zip; + } + + /// + /// Generates a random directory name for the profile. + /// + /// A random directory name for the profile. + private static string GenerateProfileDirectoryName() + { + return FileUtilities.GenerateRandomTempDirectoryName("anonymous.{0}.webdriver-profile"); + } + + /// + /// Deletes the lock files for a profile. + /// + private void DeleteLockFiles(string profileDirectory) + { + File.Delete(Path.Combine(profileDirectory, ".parentlock")); + File.Delete(Path.Combine(profileDirectory, "parent.lock")); + } + + /// + /// Installs all extensions in the profile in the directory on disk. + /// + private void InstallExtensions(string profileDirectory) + { + foreach (string extensionKey in this.extensions.Keys) { - return FileUtilities.GenerateRandomTempDirectoryName("anonymous.{0}.webdriver-profile"); + this.extensions[extensionKey].Install(profileDirectory); } + } - /// - /// Deletes the lock files for a profile. - /// - private void DeleteLockFiles(string profileDirectory) + /// + /// Deletes the cache of extensions for this profile, if the cache exists. + /// + /// If the extensions cache does not exist for this profile, the + /// method performs no operations, but + /// succeeds. + private void DeleteExtensionsCache(string profileDirectory) + { + DirectoryInfo ex = new DirectoryInfo(Path.Combine(profileDirectory, "extensions")); + string cacheFile = Path.Combine(ex.Parent!.FullName, "extensions.cache"); + if (File.Exists(cacheFile)) { - File.Delete(Path.Combine(profileDirectory, ".parentlock")); - File.Delete(Path.Combine(profileDirectory, "parent.lock")); + File.Delete(cacheFile); } + } - /// - /// Installs all extensions in the profile in the directory on disk. - /// - private void InstallExtensions(string profileDirectory) + /// + /// Writes the user preferences to the profile. + /// + private void UpdateUserPreferences(string profileDirectory) + { + string userPrefs = Path.Combine(profileDirectory, UserPreferencesFileName); + if (File.Exists(userPrefs)) { - foreach (string extensionKey in this.extensions.Keys) + try { - this.extensions[extensionKey].Install(profileDirectory); + File.Delete(userPrefs); } - } - - /// - /// Deletes the cache of extensions for this profile, if the cache exists. - /// - /// If the extensions cache does not exist for this profile, the - /// method performs no operations, but - /// succeeds. - private void DeleteExtensionsCache(string profileDirectory) - { - DirectoryInfo ex = new DirectoryInfo(Path.Combine(profileDirectory, "extensions")); - string cacheFile = Path.Combine(ex.Parent!.FullName, "extensions.cache"); - if (File.Exists(cacheFile)) + catch (Exception e) { - File.Delete(cacheFile); + throw new WebDriverException("Cannot delete existing user preferences", e); } } - /// - /// Writes the user preferences to the profile. - /// - private void UpdateUserPreferences(string profileDirectory) + string homePage = this.profilePreferences.GetPreference("browser.startup.homepage"); + if (!string.IsNullOrEmpty(homePage)) { - string userPrefs = Path.Combine(profileDirectory, UserPreferencesFileName); - if (File.Exists(userPrefs)) + this.profilePreferences.SetPreference("startup.homepage_welcome_url", string.Empty); + if (homePage != "about:blank") { - try - { - File.Delete(userPrefs); - } - catch (Exception e) - { - throw new WebDriverException("Cannot delete existing user preferences", e); - } + this.profilePreferences.SetPreference("browser.startup.page", 1); } - - string homePage = this.profilePreferences.GetPreference("browser.startup.homepage"); - if (!string.IsNullOrEmpty(homePage)) - { - this.profilePreferences.SetPreference("startup.homepage_welcome_url", string.Empty); - if (homePage != "about:blank") - { - this.profilePreferences.SetPreference("browser.startup.page", 1); - } - } - - this.profilePreferences.WriteToFile(userPrefs); } - private Preferences ReadDefaultPreferences() + this.profilePreferences.WriteToFile(userPrefs); + } + + private Preferences ReadDefaultPreferences() + { + using (Stream defaultPrefsStream = ResourceUtilities.GetResourceStream("webdriver_prefs.json", "webdriver_prefs.json")) { - using (Stream defaultPrefsStream = ResourceUtilities.GetResourceStream("webdriver_prefs.json", "webdriver_prefs.json")) - { - using JsonDocument defaultPreferences = JsonDocument.Parse(defaultPrefsStream); + using JsonDocument defaultPreferences = JsonDocument.Parse(defaultPrefsStream); - JsonElement immutableDefaultPreferences = defaultPreferences.RootElement.GetProperty("frozen"); - JsonElement editableDefaultPreferences = defaultPreferences.RootElement.GetProperty("mutable"); + JsonElement immutableDefaultPreferences = defaultPreferences.RootElement.GetProperty("frozen"); + JsonElement editableDefaultPreferences = defaultPreferences.RootElement.GetProperty("mutable"); - return new Preferences(immutableDefaultPreferences, editableDefaultPreferences); - } + return new Preferences(immutableDefaultPreferences, editableDefaultPreferences); } + } - /// - /// Reads the existing preferences from the profile. - /// - /// A containing key-value pairs representing the preferences. - /// Assumes that we only really care about the preferences, not the comments - private Dictionary ReadExistingPreferences() - { - Dictionary prefs = new Dictionary(); + /// + /// Reads the existing preferences from the profile. + /// + /// A containing key-value pairs representing the preferences. + /// Assumes that we only really care about the preferences, not the comments + private Dictionary ReadExistingPreferences() + { + Dictionary prefs = new Dictionary(); - try + try + { + if (!string.IsNullOrEmpty(this.sourceProfileDir)) { - if (!string.IsNullOrEmpty(this.sourceProfileDir)) + string userPrefs = Path.Combine(this.sourceProfileDir, UserPreferencesFileName); + if (File.Exists(userPrefs)) { - string userPrefs = Path.Combine(this.sourceProfileDir, UserPreferencesFileName); - if (File.Exists(userPrefs)) + string[] fileLines = File.ReadAllLines(userPrefs); + foreach (string line in fileLines) { - string[] fileLines = File.ReadAllLines(userPrefs); - foreach (string line in fileLines) + if (line.StartsWith("user_pref(\"", StringComparison.OrdinalIgnoreCase)) { - if (line.StartsWith("user_pref(\"", StringComparison.OrdinalIgnoreCase)) - { - string parsedLine = line.Substring("user_pref(\"".Length); - parsedLine = parsedLine.Substring(0, parsedLine.Length - ");".Length); - string[] parts = line.Split(new string[] { "," }, StringSplitOptions.None); - parts[0] = parts[0].Substring(0, parts[0].Length - 1); - prefs.Add(parts[0].Trim(), parts[1].Trim()); - } + string parsedLine = line.Substring("user_pref(\"".Length); + parsedLine = parsedLine.Substring(0, parsedLine.Length - ");".Length); + string[] parts = line.Split(new string[] { "," }, StringSplitOptions.None); + parts[0] = parts[0].Substring(0, parts[0].Length - 1); + prefs.Add(parts[0].Trim(), parts[1].Trim()); } } } } - catch (IOException e) - { - throw new WebDriverException(string.Empty, e); - } - - return prefs; } + catch (IOException e) + { + throw new WebDriverException(string.Empty, e); + } + + return prefs; } } diff --git a/dotnet/src/webdriver/Firefox/FirefoxProfileManager.cs b/dotnet/src/webdriver/Firefox/FirefoxProfileManager.cs index 561b0512fdf4e..b01264d1a8bdd 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxProfileManager.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxProfileManager.cs @@ -23,84 +23,83 @@ using System.Collections.ObjectModel; using System.IO; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Allows the user to enumerate and access existing named Firefox profiles. +/// +public class FirefoxProfileManager { + private Dictionary profiles = new Dictionary(); + /// - /// Allows the user to enumerate and access existing named Firefox profiles. + /// Initializes a new instance of the class. /// - public class FirefoxProfileManager + public FirefoxProfileManager() { - private Dictionary profiles = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - public FirefoxProfileManager() - { - string appDataDirectory = GetApplicationDataDirectory(); - this.ReadProfiles(appDataDirectory); - } + string appDataDirectory = GetApplicationDataDirectory(); + this.ReadProfiles(appDataDirectory); + } - /// - /// Gets a containing FirefoxProfiles - /// representing the existing named profiles for Firefox. - /// - public ReadOnlyCollection ExistingProfiles => new List(this.profiles.Keys).AsReadOnly(); + /// + /// Gets a containing FirefoxProfiles + /// representing the existing named profiles for Firefox. + /// + public ReadOnlyCollection ExistingProfiles => new List(this.profiles.Keys).AsReadOnly(); - /// - /// Gets a with a given name. - /// - /// The name of the profile to get. - /// A with a given name. - /// Returns if no profile with the given name exists. - public FirefoxProfile? GetProfile(string? profileName) + /// + /// Gets a with a given name. + /// + /// The name of the profile to get. + /// A with a given name. + /// Returns if no profile with the given name exists. + public FirefoxProfile? GetProfile(string? profileName) + { + if (profileName is not null && this.profiles.TryGetValue(profileName, out string? profile)) { - if (profileName is not null && this.profiles.TryGetValue(profileName, out string? profile)) - { - return new FirefoxProfile(profile); - } - - return null; + return new FirefoxProfile(profile); } - private static string GetApplicationDataDirectory() + return null; + } + + private static string GetApplicationDataDirectory() + { + string appDataDirectory = Environment.OSVersion.Platform switch { - string appDataDirectory = Environment.OSVersion.Platform switch - { - PlatformID.Unix => Path.Combine(".mozilla", "firefox"), - PlatformID.MacOSX => Path.Combine("Library", Path.Combine("Application Support", "Firefox")), - _ => Path.Combine("Mozilla", "Firefox"), - }; + PlatformID.Unix => Path.Combine(".mozilla", "firefox"), + PlatformID.MacOSX => Path.Combine("Library", Path.Combine("Application Support", "Firefox")), + _ => Path.Combine("Mozilla", "Firefox"), + }; - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appDataDirectory); - } + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appDataDirectory); + } - private void ReadProfiles(string appDataDirectory) + private void ReadProfiles(string appDataDirectory) + { + string profilesIniFile = Path.Combine(appDataDirectory, "profiles.ini"); + if (File.Exists(profilesIniFile)) { - string profilesIniFile = Path.Combine(appDataDirectory, "profiles.ini"); - if (File.Exists(profilesIniFile)) + IniFileReader reader = new IniFileReader(profilesIniFile); + ReadOnlyCollection sectionNames = reader.SectionNames; + foreach (string sectionName in sectionNames) { - IniFileReader reader = new IniFileReader(profilesIniFile); - ReadOnlyCollection sectionNames = reader.SectionNames; - foreach (string sectionName in sectionNames) + if (sectionName.StartsWith("profile", StringComparison.OrdinalIgnoreCase)) { - if (sectionName.StartsWith("profile", StringComparison.OrdinalIgnoreCase)) + string name = reader.GetValue(sectionName, "name"); + bool isRelative = reader.GetValue(sectionName, "isrelative") == "1"; + string profilePath = reader.GetValue(sectionName, "path"); + string fullPath; + if (isRelative) { - string name = reader.GetValue(sectionName, "name"); - bool isRelative = reader.GetValue(sectionName, "isrelative") == "1"; - string profilePath = reader.GetValue(sectionName, "path"); - string fullPath; - if (isRelative) - { - fullPath = Path.Combine(appDataDirectory, profilePath); - } - else - { - fullPath = profilePath; - } - - this.profiles.Add(name, fullPath); + fullPath = Path.Combine(appDataDirectory, profilePath); } + else + { + fullPath = profilePath; + } + + this.profiles.Add(name, fullPath); } } } diff --git a/dotnet/src/webdriver/Firefox/Internal/IniFileReader.cs b/dotnet/src/webdriver/Firefox/Internal/IniFileReader.cs index ec5ee2d2ba8ea..805f17d9724f4 100644 --- a/dotnet/src/webdriver/Firefox/Internal/IniFileReader.cs +++ b/dotnet/src/webdriver/Firefox/Internal/IniFileReader.cs @@ -22,117 +22,116 @@ using System.Collections.ObjectModel; using System.IO; -namespace OpenQA.Selenium.Firefox.Internal +namespace OpenQA.Selenium.Firefox.Internal; + +/// +/// Parses and reads an INI file. +/// +internal sealed class IniFileReader { + private readonly Dictionary> iniFileStore = new Dictionary>(); + /// - /// Parses and reads an INI file. + /// Initializes a new instance of the class. /// - internal sealed class IniFileReader + /// The full path to the .INI file to be read. + /// If is or . + /// If no file exists at file path . + public IniFileReader(string fileName) { - private readonly Dictionary> iniFileStore = new Dictionary>(); - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the .INI file to be read. - /// If is or . - /// If no file exists at file path . - public IniFileReader(string fileName) + if (string.IsNullOrEmpty(fileName)) { - if (string.IsNullOrEmpty(fileName)) - { - throw new ArgumentNullException(nameof(fileName), "File name must not be null or empty"); - } + throw new ArgumentNullException(nameof(fileName), "File name must not be null or empty"); + } - if (!File.Exists(fileName)) - { - throw new FileNotFoundException("INI file not found", fileName); - } + if (!File.Exists(fileName)) + { + throw new FileNotFoundException("INI file not found", fileName); + } - Dictionary section = new Dictionary(); - string sectionName = string.Empty; + Dictionary section = new Dictionary(); + string sectionName = string.Empty; - string[] iniFileContent = File.ReadAllLines(fileName); - foreach (string iniFileLine in iniFileContent) + string[] iniFileContent = File.ReadAllLines(fileName); + foreach (string iniFileLine in iniFileContent) + { + if (!string.IsNullOrWhiteSpace(iniFileLine) && !iniFileLine.StartsWith(";", StringComparison.OrdinalIgnoreCase)) { - if (!string.IsNullOrWhiteSpace(iniFileLine) && !iniFileLine.StartsWith(";", StringComparison.OrdinalIgnoreCase)) + if (iniFileLine.StartsWith("[", StringComparison.OrdinalIgnoreCase) && iniFileLine.EndsWith("]", StringComparison.OrdinalIgnoreCase)) { - if (iniFileLine.StartsWith("[", StringComparison.OrdinalIgnoreCase) && iniFileLine.EndsWith("]", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(sectionName)) { - if (!string.IsNullOrEmpty(sectionName)) - { - this.iniFileStore.Add(sectionName, section); - } - - sectionName = iniFileLine.Substring(1, iniFileLine.Length - 2).ToUpperInvariant(); - section = new Dictionary(); + this.iniFileStore.Add(sectionName, section); } - else + + sectionName = iniFileLine.Substring(1, iniFileLine.Length - 2).ToUpperInvariant(); + section = new Dictionary(); + } + else + { + string[] entryParts = iniFileLine.Split(new char[] { '=' }, 2); + string name = entryParts[0].ToUpperInvariant(); + string value = string.Empty; + if (entryParts.Length > 1) { - string[] entryParts = iniFileLine.Split(new char[] { '=' }, 2); - string name = entryParts[0].ToUpperInvariant(); - string value = string.Empty; - if (entryParts.Length > 1) - { - value = entryParts[1]; - } - - section.Add(name, value); + value = entryParts[1]; } + + section.Add(name, value); } } - - this.iniFileStore.Add(sectionName, section); } - /// - /// Gets a containing the names of the sections in the .INI file. - /// - public ReadOnlyCollection SectionNames => new ReadOnlyCollection(new List(this.iniFileStore.Keys)); - - /// - /// Gets a value from the .INI file. - /// - /// The section in which to find the key-value pair. - /// The key of the key-value pair. - /// The value associated with the given section and key. - /// - /// If is or . - /// -or- - /// If is or . - /// - /// - /// If no section named exists. - /// -or- - ///If the section does not contain a value named . - /// - public string GetValue(string sectionName, string valueName) - { - if (string.IsNullOrEmpty(sectionName)) - { - throw new ArgumentNullException(nameof(sectionName), "Section name cannot be null or empty"); - } + this.iniFileStore.Add(sectionName, section); + } - string lowerCaseSectionName = sectionName.ToUpperInvariant(); + /// + /// Gets a containing the names of the sections in the .INI file. + /// + public ReadOnlyCollection SectionNames => new ReadOnlyCollection(new List(this.iniFileStore.Keys)); - if (string.IsNullOrEmpty(valueName)) - { - throw new ArgumentNullException(nameof(valueName), "Value name cannot be null or empty"); - } + /// + /// Gets a value from the .INI file. + /// + /// The section in which to find the key-value pair. + /// The key of the key-value pair. + /// The value associated with the given section and key. + /// + /// If is or . + /// -or- + /// If is or . + /// + /// + /// If no section named exists. + /// -or- + ///If the section does not contain a value named . + /// + public string GetValue(string sectionName, string valueName) + { + if (string.IsNullOrEmpty(sectionName)) + { + throw new ArgumentNullException(nameof(sectionName), "Section name cannot be null or empty"); + } - string lowerCaseValueName = valueName.ToUpperInvariant(); + string lowerCaseSectionName = sectionName.ToUpperInvariant(); - if (!this.iniFileStore.TryGetValue(lowerCaseSectionName, out Dictionary? section)) - { - throw new ArgumentException("Section does not exist: " + sectionName, nameof(sectionName)); - } + if (string.IsNullOrEmpty(valueName)) + { + throw new ArgumentNullException(nameof(valueName), "Value name cannot be null or empty"); + } - if (!section.TryGetValue(lowerCaseValueName, out string? value)) - { - throw new ArgumentException("Value does not exist: " + valueName, nameof(valueName)); - } + string lowerCaseValueName = valueName.ToUpperInvariant(); - return value; + if (!this.iniFileStore.TryGetValue(lowerCaseSectionName, out Dictionary? section)) + { + throw new ArgumentException("Section does not exist: " + sectionName, nameof(sectionName)); } + + if (!section.TryGetValue(lowerCaseValueName, out string? value)) + { + throw new ArgumentException("Value does not exist: " + valueName, nameof(valueName)); + } + + return value; } } diff --git a/dotnet/src/webdriver/Firefox/Preferences.cs b/dotnet/src/webdriver/Firefox/Preferences.cs index a616c702ec86f..f02f61f9134f2 100644 --- a/dotnet/src/webdriver/Firefox/Preferences.cs +++ b/dotnet/src/webdriver/Firefox/Preferences.cs @@ -23,182 +23,181 @@ using System.IO; using System.Text.Json; -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +/// +/// Represents the preferences used by a profile in Firefox. +/// +internal class Preferences { + private readonly Dictionary preferences = new Dictionary(); + private readonly HashSet immutablePreferences = new HashSet(); + /// - /// Represents the preferences used by a profile in Firefox. + /// Initializes a new instance of the class. /// - internal class Preferences + /// A set of preferences that cannot be modified once set. + /// A set of default preferences. + public Preferences(JsonElement defaultImmutablePreferences, JsonElement defaultPreferences) { - private readonly Dictionary preferences = new Dictionary(); - private readonly HashSet immutablePreferences = new HashSet(); - - /// - /// Initializes a new instance of the class. - /// - /// A set of preferences that cannot be modified once set. - /// A set of default preferences. - public Preferences(JsonElement defaultImmutablePreferences, JsonElement defaultPreferences) + foreach (JsonProperty pref in defaultImmutablePreferences.EnumerateObject()) { - foreach (JsonProperty pref in defaultImmutablePreferences.EnumerateObject()) - { - this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value); - this.preferences[pref.Name] = pref.Value.GetRawText(); - this.immutablePreferences.Add(pref.Name); - } - - foreach (JsonProperty pref in defaultPreferences.EnumerateObject()) - { - this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value); - this.preferences[pref.Name] = pref.Value.GetRawText(); - } + this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value); + this.preferences[pref.Name] = pref.Value.GetRawText(); + this.immutablePreferences.Add(pref.Name); } - /// - /// Sets a preference. - /// - /// The name of the preference to set. - /// A value give the preference. - /// If the preference already exists in the currently-set list of preferences, - /// the value will be updated. - /// If or are . - /// - /// If is wrapped with double-quotes. - /// -or- - /// If the specified preference is immutable. - /// - internal void SetPreference(string key, string value) + foreach (JsonProperty pref in defaultPreferences.EnumerateObject()) { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (IsWrappedAsString(value)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Preference values must be plain strings: {0}: {1}", key, value)); - } - - this.ThrowIfPreferenceIsImmutable(key, value); - this.preferences[key] = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", value); + this.ThrowIfPreferenceIsImmutable(pref.Name, pref.Value); + this.preferences[pref.Name] = pref.Value.GetRawText(); } + } - /// - /// Sets a preference. - /// - /// The name of the preference to set. - /// A value give the preference. - /// If the preference already exists in the currently-set list of preferences, - /// the value will be updated. - /// If is . - /// If the specified preference is immutable. - internal void SetPreference(string key, int value) + /// + /// Sets a preference. + /// + /// The name of the preference to set. + /// A value give the preference. + /// If the preference already exists in the currently-set list of preferences, + /// the value will be updated. + /// If or are . + /// + /// If is wrapped with double-quotes. + /// -or- + /// If the specified preference is immutable. + /// + internal void SetPreference(string key, string value) + { + if (key is null) { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - this.ThrowIfPreferenceIsImmutable(key, value); - this.preferences[key] = value.ToString(CultureInfo.InvariantCulture); + throw new ArgumentNullException(nameof(key)); } - /// - /// Sets a preference. - /// - /// The name of the preference to set. - /// A value give the preference. - /// If the preference already exists in the currently-set list of preferences, - /// the value will be updated. - /// If is . - /// If the specified preference is immutable. - internal void SetPreference(string key, bool value) + if (value is null) { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - this.ThrowIfPreferenceIsImmutable(key, value); - this.preferences[key] = value ? "true" : "false"; + throw new ArgumentNullException(nameof(value)); } - /// - /// Gets a preference from the list of preferences. - /// - /// The name of the preference to retrieve. - /// The value of the preference, or an empty string if the preference is not set. - /// If is . - internal string GetPreference(string preferenceName) + if (IsWrappedAsString(value)) { - if (this.preferences.ContainsKey(preferenceName)) - { - return this.preferences[preferenceName]; - } + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Preference values must be plain strings: {0}: {1}", key, value)); + } + + this.ThrowIfPreferenceIsImmutable(key, value); + this.preferences[key] = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", value); + } - return string.Empty; + /// + /// Sets a preference. + /// + /// The name of the preference to set. + /// A value give the preference. + /// If the preference already exists in the currently-set list of preferences, + /// the value will be updated. + /// If is . + /// If the specified preference is immutable. + internal void SetPreference(string key, int value) + { + if (key is null) + { + throw new ArgumentNullException(nameof(key)); } - /// - /// Appends this set of preferences to the specified set of preferences. - /// - /// A dictionary containing the preferences to which to - /// append these values. - /// If the preference already exists in , - /// the value will be updated. - internal void AppendPreferences(Dictionary preferencesToAdd) + this.ThrowIfPreferenceIsImmutable(key, value); + this.preferences[key] = value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Sets a preference. + /// + /// The name of the preference to set. + /// A value give the preference. + /// If the preference already exists in the currently-set list of preferences, + /// the value will be updated. + /// If is . + /// If the specified preference is immutable. + internal void SetPreference(string key, bool value) + { + if (key is null) { - // This allows the user to add additional preferences, or update ones that already - // exist. - foreach (KeyValuePair preferenceToAdd in preferencesToAdd) - { - if (this.IsSettablePreference(preferenceToAdd.Key)) - { - this.preferences[preferenceToAdd.Key] = preferenceToAdd.Value; - } - } + throw new ArgumentNullException(nameof(key)); } - /// - /// Writes the preferences to a file. - /// - /// The full path to the file to be written. - internal void WriteToFile(string filePath) + this.ThrowIfPreferenceIsImmutable(key, value); + this.preferences[key] = value ? "true" : "false"; + } + + /// + /// Gets a preference from the list of preferences. + /// + /// The name of the preference to retrieve. + /// The value of the preference, or an empty string if the preference is not set. + /// If is . + internal string GetPreference(string preferenceName) + { + if (this.preferences.ContainsKey(preferenceName)) { - using (TextWriter writer = File.CreateText(filePath)) - { - foreach (KeyValuePair preference in this.preferences) - { - string escapedValue = preference.Value.Replace(@"\", @"\\"); - writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "user_pref(\"{0}\", {1});", preference.Key, escapedValue)); - } - } + return this.preferences[preferenceName]; } - private static bool IsWrappedAsString(string value) + return string.Empty; + } + + /// + /// Appends this set of preferences to the specified set of preferences. + /// + /// A dictionary containing the preferences to which to + /// append these values. + /// If the preference already exists in , + /// the value will be updated. + internal void AppendPreferences(Dictionary preferencesToAdd) + { + // This allows the user to add additional preferences, or update ones that already + // exist. + foreach (KeyValuePair preferenceToAdd in preferencesToAdd) { - // Assume we a string is stringified (i.e. wrapped in " ") when - // the first character == " and the last character == " - return value.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && value.EndsWith("\"", StringComparison.OrdinalIgnoreCase); + if (this.IsSettablePreference(preferenceToAdd.Key)) + { + this.preferences[preferenceToAdd.Key] = preferenceToAdd.Value; + } } + } - private void ThrowIfPreferenceIsImmutable(string preferenceName, TValue value) + /// + /// Writes the preferences to a file. + /// + /// The full path to the file to be written. + internal void WriteToFile(string filePath) + { + using (TextWriter writer = File.CreateText(filePath)) { - if (this.immutablePreferences.Contains(preferenceName)) + foreach (KeyValuePair preference in this.preferences) { - string message = string.Format(CultureInfo.InvariantCulture, "Preference {0} may not be overridden: frozen value={1}, requested value={2}", preferenceName, this.preferences[preferenceName], value?.ToString()); - throw new ArgumentException(message); + string escapedValue = preference.Value.Replace(@"\", @"\\"); + writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "user_pref(\"{0}\", {1});", preference.Key, escapedValue)); } } + } + + private static bool IsWrappedAsString(string value) + { + // Assume we a string is stringified (i.e. wrapped in " ") when + // the first character == " and the last character == " + return value.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && value.EndsWith("\"", StringComparison.OrdinalIgnoreCase); + } - private bool IsSettablePreference(string preferenceName) + private void ThrowIfPreferenceIsImmutable(string preferenceName, TValue value) + { + if (this.immutablePreferences.Contains(preferenceName)) { - return !this.immutablePreferences.Contains(preferenceName); + string message = string.Format(CultureInfo.InvariantCulture, "Preference {0} may not be overridden: frozen value={1}, requested value={2}", preferenceName, this.preferences[preferenceName], value?.ToString()); + throw new ArgumentException(message); } } + + private bool IsSettablePreference(string preferenceName) + { + return !this.immutablePreferences.Contains(preferenceName); + } } diff --git a/dotnet/src/webdriver/HttpCommandInfo.cs b/dotnet/src/webdriver/HttpCommandInfo.cs index 56c8beef152a8..38e7b382d697f 100644 --- a/dotnet/src/webdriver/HttpCommandInfo.cs +++ b/dotnet/src/webdriver/HttpCommandInfo.cs @@ -20,120 +20,119 @@ using System; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides the execution information for a . +/// +public class HttpCommandInfo : CommandInfo { /// - /// Provides the execution information for a . + /// POST verb for the command info + /// + public const string PostCommand = "POST"; + + /// + /// GET verb for the command info + /// + public const string GetCommand = "GET"; + + /// + /// DELETE verb for the command info + /// + public const string DeleteCommand = "DELETE"; + + private const string SessionIdPropertyName = "sessionId"; + + /// + /// Initializes a new instance of the class /// - public class HttpCommandInfo : CommandInfo + /// Method of the Command + /// Relative URL path to the resource used to execute the command + /// If or are . + public HttpCommandInfo(string method, string resourcePath) { - /// - /// POST verb for the command info - /// - public const string PostCommand = "POST"; - - /// - /// GET verb for the command info - /// - public const string GetCommand = "GET"; - - /// - /// DELETE verb for the command info - /// - public const string DeleteCommand = "DELETE"; - - private const string SessionIdPropertyName = "sessionId"; - - /// - /// Initializes a new instance of the class - /// - /// Method of the Command - /// Relative URL path to the resource used to execute the command - /// If or are . - public HttpCommandInfo(string method, string resourcePath) - { - this.ResourcePath = resourcePath ?? throw new ArgumentNullException(nameof(resourcePath)); - this.Method = method ?? throw new ArgumentNullException(nameof(method)); - } + this.ResourcePath = resourcePath ?? throw new ArgumentNullException(nameof(resourcePath)); + this.Method = method ?? throw new ArgumentNullException(nameof(method)); + } - /// - /// Gets the URL representing the path to the resource. - /// - public string ResourcePath { get; } + /// + /// Gets the URL representing the path to the resource. + /// + public string ResourcePath { get; } - /// - /// Gets the HTTP method associated with the command. - /// - public string Method { get; } + /// + /// Gets the HTTP method associated with the command. + /// + public string Method { get; } - /// - /// Gets the unique identifier for this command within the scope of its protocol definition - /// - public override string CommandIdentifier - { - get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.Method, this.ResourcePath); } - } + /// + /// Gets the unique identifier for this command within the scope of its protocol definition + /// + public override string CommandIdentifier + { + get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.Method, this.ResourcePath); } + } - /// - /// Creates the full URI associated with this command, substituting command - /// parameters for tokens in the URI template. - /// - /// The base URI associated with the command. - /// The command containing the parameters with which - /// to substitute the tokens in the template. - /// The full URI for the command, with the parameters of the command - /// substituted for the tokens in the template. - public Uri CreateCommandUri(Uri baseUri, Command commandToExecute) + /// + /// Creates the full URI associated with this command, substituting command + /// parameters for tokens in the URI template. + /// + /// The base URI associated with the command. + /// The command containing the parameters with which + /// to substitute the tokens in the template. + /// The full URI for the command, with the parameters of the command + /// substituted for the tokens in the template. + public Uri CreateCommandUri(Uri baseUri, Command commandToExecute) + { + string[] urlParts = this.ResourcePath.Split(["/"], StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < urlParts.Length; i++) { - string[] urlParts = this.ResourcePath.Split(["/"], StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < urlParts.Length; i++) + string urlPart = urlParts[i]; + if (urlPart.StartsWith("{", StringComparison.OrdinalIgnoreCase) && urlPart.EndsWith("}", StringComparison.OrdinalIgnoreCase)) { - string urlPart = urlParts[i]; - if (urlPart.StartsWith("{", StringComparison.OrdinalIgnoreCase) && urlPart.EndsWith("}", StringComparison.OrdinalIgnoreCase)) - { - urlParts[i] = GetCommandPropertyValue(urlPart, commandToExecute); - } + urlParts[i] = GetCommandPropertyValue(urlPart, commandToExecute); } - - string relativeUrlString = string.Join("/", urlParts); - Uri relativeUri = new Uri(relativeUrlString, UriKind.Relative); - if (!Uri.TryCreate(baseUri, relativeUri, out Uri? fullUri)) - { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create URI from base {0} and relative path {1}", baseUri?.ToString(), relativeUrlString)); - } - - return fullUri; } - private static string GetCommandPropertyValue(string propertyName, Command commandToExecute) + string relativeUrlString = string.Join("/", urlParts); + Uri relativeUri = new Uri(relativeUrlString, UriKind.Relative); + if (!Uri.TryCreate(baseUri, relativeUri, out Uri? fullUri)) { - string propertyValue = string.Empty; + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create URI from base {0} and relative path {1}", baseUri?.ToString(), relativeUrlString)); + } - // Strip the curly braces - propertyName = propertyName.Substring(1, propertyName.Length - 2); + return fullUri; + } + + private static string GetCommandPropertyValue(string propertyName, Command commandToExecute) + { + string propertyValue = string.Empty; - if (propertyName == SessionIdPropertyName) + // Strip the curly braces + propertyName = propertyName.Substring(1, propertyName.Length - 2); + + if (propertyName == SessionIdPropertyName) + { + if (commandToExecute.SessionId != null) { - if (commandToExecute.SessionId != null) - { - propertyValue = commandToExecute.SessionId.ToString(); - } + propertyValue = commandToExecute.SessionId.ToString(); } - else if (commandToExecute.Parameters != null && commandToExecute.Parameters.Count > 0) + } + else if (commandToExecute.Parameters != null && commandToExecute.Parameters.Count > 0) + { + // Extract the URL parameter, and remove it from the parameters dictionary + // so it doesn't get transmitted as a JSON parameter. + if (commandToExecute.Parameters.TryGetValue(propertyName, out var propertyValueObject)) { - // Extract the URL parameter, and remove it from the parameters dictionary - // so it doesn't get transmitted as a JSON parameter. - if (commandToExecute.Parameters.TryGetValue(propertyName, out var propertyValueObject)) + if (propertyValueObject != null) { - if (propertyValueObject != null) - { - propertyValue = propertyValueObject.ToString()!; - commandToExecute.Parameters.Remove(propertyName); - } + propertyValue = propertyValueObject.ToString()!; + commandToExecute.Parameters.Remove(propertyName); } } - - return propertyValue; } + + return propertyValue; } } diff --git a/dotnet/src/webdriver/HttpRequestData.cs b/dotnet/src/webdriver/HttpRequestData.cs index 50f74d2429ff8..70f070bc50477 100644 --- a/dotnet/src/webdriver/HttpRequestData.cs +++ b/dotnet/src/webdriver/HttpRequestData.cs @@ -19,43 +19,42 @@ using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the response data for an intercepted HTTP call. +/// +public class HttpRequestData { /// - /// Represents the response data for an intercepted HTTP call. + /// Initializes a new instance of the type. /// - public class HttpRequestData + public HttpRequestData() { - /// - /// Initializes a new instance of the type. - /// - public HttpRequestData() - { - } - - /// - /// Gets the method of the HTTP request. - /// - public string? Method { get; set; } - - /// - /// Gets the URL of the HTTP request. - /// - public string? Url { get; set; } - - /// - /// Gets the POST data of the HTTP request, if any. - /// - public string? PostData { get; set; } - - /// - /// Gets the headers of the HTTP request. - /// - public Dictionary? Headers { get; set; } - - /// - /// Gets the ID of the HTTP request. - /// - public string? RequestId { get; internal set; } } + + /// + /// Gets the method of the HTTP request. + /// + public string? Method { get; set; } + + /// + /// Gets the URL of the HTTP request. + /// + public string? Url { get; set; } + + /// + /// Gets the POST data of the HTTP request, if any. + /// + public string? PostData { get; set; } + + /// + /// Gets the headers of the HTTP request. + /// + public Dictionary? Headers { get; set; } + + /// + /// Gets the ID of the HTTP request. + /// + public string? RequestId { get; internal set; } } diff --git a/dotnet/src/webdriver/HttpResponseContent.cs b/dotnet/src/webdriver/HttpResponseContent.cs index e178462ebc514..d0852ec4d6d5d 100644 --- a/dotnet/src/webdriver/HttpResponseContent.cs +++ b/dotnet/src/webdriver/HttpResponseContent.cs @@ -20,49 +20,48 @@ using System; using System.Text; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the content of an HTTP response. +/// +public class HttpResponseContent { + private readonly byte[] content; + /// - /// Represents the content of an HTTP response. + /// Initializes a new instance of the class. /// - public class HttpResponseContent + /// The byte array representing the content of the response. + public HttpResponseContent(byte[] content) { - private readonly byte[] content; - - /// - /// Initializes a new instance of the class. - /// - /// The byte array representing the content of the response. - public HttpResponseContent(byte[] content) - { - this.content = content ?? throw new ArgumentNullException(nameof(content)); - } + this.content = content ?? throw new ArgumentNullException(nameof(content)); + } - /// - /// Initializes a new instance of the class. - /// - /// The UTF8 encoded string representing the content of the response. - public HttpResponseContent(string content) - { - this.content = Encoding.UTF8.GetBytes(content); - } + /// + /// Initializes a new instance of the class. + /// + /// The UTF8 encoded string representing the content of the response. + public HttpResponseContent(string content) + { + this.content = Encoding.UTF8.GetBytes(content); + } - /// - /// Reads the content of the response as a UTF8 encoded string. - /// - /// The content of the response as a string. - public string ReadAsString() - { - return Encoding.UTF8.GetString(content); - } + /// + /// Reads the content of the response as a UTF8 encoded string. + /// + /// The content of the response as a string. + public string ReadAsString() + { + return Encoding.UTF8.GetString(content); + } - /// - /// Reads the content of the response as a byte array. - /// - /// The content of the response as a byte array. - public byte[] ReadAsByteArray() - { - return content; - } + /// + /// Reads the content of the response as a byte array. + /// + /// The content of the response as a byte array. + public byte[] ReadAsByteArray() + { + return content; } } diff --git a/dotnet/src/webdriver/HttpResponseData.cs b/dotnet/src/webdriver/HttpResponseData.cs index ac6f12e6dd670..97d82e6b3604a 100644 --- a/dotnet/src/webdriver/HttpResponseData.cs +++ b/dotnet/src/webdriver/HttpResponseData.cs @@ -20,68 +20,67 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the response data for an intercepted HTTP call. +/// +public class HttpResponseData { /// - /// Represents the response data for an intercepted HTTP call. + /// Initializes a new instance of the type. /// - public class HttpResponseData + public HttpResponseData() { - /// - /// Initializes a new instance of the type. - /// - public HttpResponseData() - { - } + } - /// - /// Gets or sets the ID of the request that generated this response. - /// - public string? RequestId { get; set; } + /// + /// Gets or sets the ID of the request that generated this response. + /// + public string? RequestId { get; set; } - /// - /// Gets or sets the URL of the HTTP response. - /// - public string? Url { get; set; } + /// + /// Gets or sets the URL of the HTTP response. + /// + public string? Url { get; set; } - /// - /// Gets or sets the numeric status code of the HTTP response. - /// - public long StatusCode { get; set; } + /// + /// Gets or sets the numeric status code of the HTTP response. + /// + public long StatusCode { get; set; } - /// - /// Gets or sets the body of the HTTP response. - /// - [DisallowNull] - public string? Body - { - get => this.Content?.ReadAsString(); - set => this.Content = new HttpResponseContent(value); - } + /// + /// Gets or sets the body of the HTTP response. + /// + [DisallowNull] + public string? Body + { + get => this.Content?.ReadAsString(); + set => this.Content = new HttpResponseContent(value); + } - /// - /// Gets or sets the content of the HTTP response. - /// - public HttpResponseContent? Content { get; set; } + /// + /// Gets or sets the content of the HTTP response. + /// + public HttpResponseContent? Content { get; set; } - /// - /// Gets or sets the type of resource for this response. - /// - public string? ResourceType { get; set; } + /// + /// Gets or sets the type of resource for this response. + /// + public string? ResourceType { get; set; } - /// - /// Gets or sets the reason for an error response. - /// - public string? ErrorReason { get; set; } + /// + /// Gets or sets the reason for an error response. + /// + public string? ErrorReason { get; set; } - /// - /// Gets the headers of the HTTP response. - /// - public Dictionary Headers { get; } = new Dictionary(); + /// + /// Gets the headers of the HTTP response. + /// + public Dictionary Headers { get; } = new Dictionary(); - /// - /// Gets the cookie headers of the HTTP response. - /// - public List CookieHeaders { get; } = new List(); - } + /// + /// Gets the cookie headers of the HTTP response. + /// + public List CookieHeaders { get; } = new List(); } diff --git a/dotnet/src/webdriver/IActionExecutor.cs b/dotnet/src/webdriver/IActionExecutor.cs index 99960f2a328d2..88442a976d4a0 100644 --- a/dotnet/src/webdriver/IActionExecutor.cs +++ b/dotnet/src/webdriver/IActionExecutor.cs @@ -21,29 +21,28 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface allowing execution of W3C Specification-compliant actions. +/// +public interface IActionExecutor { /// - /// Interface allowing execution of W3C Specification-compliant actions. + /// Gets a value indicating whether this object is a valid action executor. /// - public interface IActionExecutor - { - /// - /// Gets a value indicating whether this object is a valid action executor. - /// - bool IsActionExecutor { get; } + bool IsActionExecutor { get; } - /// - /// Performs the specified list of actions with this action executor. - /// - /// The list of action sequences to perform. - /// If is . - /// If an element in is . - void PerformActions(IList actionSequenceList); + /// + /// Performs the specified list of actions with this action executor. + /// + /// The list of action sequences to perform. + /// If is . + /// If an element in is . + void PerformActions(IList actionSequenceList); - /// - /// Resets the input state of the action executor. - /// - void ResetInputState(); - } + /// + /// Resets the input state of the action executor. + /// + void ResetInputState(); } diff --git a/dotnet/src/webdriver/IAlert.cs b/dotnet/src/webdriver/IAlert.cs index eef9b7418f1fc..c888256d04008 100644 --- a/dotnet/src/webdriver/IAlert.cs +++ b/dotnet/src/webdriver/IAlert.cs @@ -19,33 +19,32 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can manipulate JavaScript alerts. +/// +public interface IAlert { /// - /// Defines the interface through which the user can manipulate JavaScript alerts. + /// Gets the text of the alert. /// - public interface IAlert - { - /// - /// Gets the text of the alert. - /// - string? Text { get; } + string? Text { get; } - /// - /// Dismisses the alert. - /// - void Dismiss(); + /// + /// Dismisses the alert. + /// + void Dismiss(); - /// - /// Accepts the alert. - /// - void Accept(); + /// + /// Accepts the alert. + /// + void Accept(); - /// - /// Sends keys to the alert. - /// - /// The keystrokes to send. - /// If is . - void SendKeys(string keysToSend); - } + /// + /// Sends keys to the alert. + /// + /// The keystrokes to send. + /// If is . + void SendKeys(string keysToSend); } diff --git a/dotnet/src/webdriver/IAllowsFileDetection.cs b/dotnet/src/webdriver/IAllowsFileDetection.cs index 4913873186e5a..85db1a182ba1c 100644 --- a/dotnet/src/webdriver/IAllowsFileDetection.cs +++ b/dotnet/src/webdriver/IAllowsFileDetection.cs @@ -19,19 +19,18 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface determining whether the driver implementation allows detection of files +/// when sending keystrokes to a file upload element. +/// +public interface IAllowsFileDetection { /// - /// Interface determining whether the driver implementation allows detection of files - /// when sending keystrokes to a file upload element. + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. /// - public interface IAllowsFileDetection - { - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// If the value is set to . - IFileDetector FileDetector { get; set; } - } + /// If the value is set to . + IFileDetector FileDetector { get; set; } } diff --git a/dotnet/src/webdriver/ICapabilities.cs b/dotnet/src/webdriver/ICapabilities.cs index 0e550748eddbf..d175a3c2ab693 100644 --- a/dotnet/src/webdriver/ICapabilities.cs +++ b/dotnet/src/webdriver/ICapabilities.cs @@ -19,36 +19,35 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Capabilities of the browser that you are going to use +/// +public interface ICapabilities { /// - /// Capabilities of the browser that you are going to use + /// Gets the capability value with the specified name. /// - public interface ICapabilities - { - /// - /// Gets the capability value with the specified name. - /// - /// The name of the capability to get. - /// The value of the capability. - /// - /// The specified capability name is not in the set of capabilities. - /// - object this[string capabilityName] { get; } + /// The name of the capability to get. + /// The value of the capability. + /// + /// The specified capability name is not in the set of capabilities. + /// + object this[string capabilityName] { get; } - /// - /// Gets a value indicating whether the browser has a given capability. - /// - /// The capability to get. - /// Returns if the browser has the capability; otherwise, . - bool HasCapability(string capability); + /// + /// Gets a value indicating whether the browser has a given capability. + /// + /// The capability to get. + /// Returns if the browser has the capability; otherwise, . + bool HasCapability(string capability); - /// - /// Gets a capability of the browser. - /// - /// The capability to get. - /// An object associated with the capability, or - /// if the capability is not set on the browser. - object? GetCapability(string capability); - } + /// + /// Gets a capability of the browser. + /// + /// The capability to get. + /// An object associated with the capability, or + /// if the capability is not set on the browser. + object? GetCapability(string capability); } diff --git a/dotnet/src/webdriver/ICommandExecutor.cs b/dotnet/src/webdriver/ICommandExecutor.cs index d79a53a80a41d..f6feaf85b2d49 100644 --- a/dotnet/src/webdriver/ICommandExecutor.cs +++ b/dotnet/src/webdriver/ICommandExecutor.cs @@ -21,36 +21,35 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a way to send commands to the remote server +/// +public interface ICommandExecutor : IDisposable { /// - /// Provides a way to send commands to the remote server + /// Attempts to add a command to the repository of commands known to this executor. /// - public interface ICommandExecutor : IDisposable - { - /// - /// Attempts to add a command to the repository of commands known to this executor. - /// - /// The name of the command to attempt to add. - /// The describing the command to add. - /// if the new command has been added successfully; otherwise, . - bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info); + /// The name of the command to attempt to add. + /// The describing the command to add. + /// if the new command has been added successfully; otherwise, . + bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info); - /// - /// Executes a command - /// - /// The command you wish to execute - /// A response from the browser - /// If is . - Response Execute(Command commandToExecute); + /// + /// Executes a command + /// + /// The command you wish to execute + /// A response from the browser + /// If is . + Response Execute(Command commandToExecute); - /// - /// Executes a command as an asynchronous task. - /// - /// The command you wish to execute - /// A task object representing the asynchronous operation - /// If is . - Task ExecuteAsync(Command commandToExecute); - } + /// + /// Executes a command as an asynchronous task. + /// + /// The command you wish to execute + /// A task object representing the asynchronous operation + /// If is . + Task ExecuteAsync(Command commandToExecute); } diff --git a/dotnet/src/webdriver/ICookieJar.cs b/dotnet/src/webdriver/ICookieJar.cs index ed01a83236b44..592efb55c97cf 100644 --- a/dotnet/src/webdriver/ICookieJar.cs +++ b/dotnet/src/webdriver/ICookieJar.cs @@ -20,51 +20,50 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an interface allowing the user to manipulate cookies on the current page. +/// +public interface ICookieJar { /// - /// Defines an interface allowing the user to manipulate cookies on the current page. + /// Gets all cookies defined for the current page. /// - public interface ICookieJar - { - /// - /// Gets all cookies defined for the current page. - /// - ReadOnlyCollection AllCookies { get; } + ReadOnlyCollection AllCookies { get; } - /// - /// Adds a cookie to the current page. - /// - /// The object to be added. - /// If is . - void AddCookie(Cookie cookie); + /// + /// Adds a cookie to the current page. + /// + /// The object to be added. + /// If is . + void AddCookie(Cookie cookie); - /// - /// Gets a cookie with the specified name. - /// - /// The name of the cookie to retrieve. - /// The containing the name. Returns - /// if no cookie with the specified name is found. - /// If is or . - Cookie? GetCookieNamed(string name); + /// + /// Gets a cookie with the specified name. + /// + /// The name of the cookie to retrieve. + /// The containing the name. Returns + /// if no cookie with the specified name is found. + /// If is or . + Cookie? GetCookieNamed(string name); - /// - /// Deletes the specified cookie from the page. - /// - /// The to be deleted. - /// If is . - void DeleteCookie(Cookie cookie); + /// + /// Deletes the specified cookie from the page. + /// + /// The to be deleted. + /// If is . + void DeleteCookie(Cookie cookie); - /// - /// Deletes the cookie with the specified name from the page. - /// - /// The name of the cookie to be deleted. - /// If is or . - void DeleteCookieNamed(string name); + /// + /// Deletes the cookie with the specified name from the page. + /// + /// The name of the cookie to be deleted. + /// If is or . + void DeleteCookieNamed(string name); - /// - /// Deletes all cookies from the page. - /// - void DeleteAllCookies(); - } + /// + /// Deletes all cookies from the page. + /// + void DeleteAllCookies(); } diff --git a/dotnet/src/webdriver/ICredentials.cs b/dotnet/src/webdriver/ICredentials.cs index 971c3dcb821e8..0129becaaaabe 100644 --- a/dotnet/src/webdriver/ICredentials.cs +++ b/dotnet/src/webdriver/ICredentials.cs @@ -17,13 +17,12 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Marker interface describing a set of credentials to be used with network requests. +/// This allows the ability to expand beyond simple user name/password security mechanisms. +/// +public interface ICredentials { - /// - /// Marker interface describing a set of credentials to be used with network requests. - /// This allows the ability to expand beyond simple user name/password security mechanisms. - /// - public interface ICredentials - { - } } diff --git a/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs b/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs index 5003d2ff82377..cd62238f4a2d3 100644 --- a/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs +++ b/dotnet/src/webdriver/ICustomDriverCommandExecutor.cs @@ -20,34 +20,33 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Exposes an interface to allow drivers to register and execute custom commands. +/// +public interface ICustomDriverCommandExecutor { /// - /// Exposes an interface to allow drivers to register and execute custom commands. + /// Executes a command with this driver. /// - public interface ICustomDriverCommandExecutor - { - /// - /// Executes a command with this driver. - /// - /// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type. - /// A containing the names and values of the parameters of the command. - /// An object that contains the value returned by the command, if any. - /// The command returned an exceptional value. - object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters); + /// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type. + /// A containing the names and values of the parameters of the command. + /// An object that contains the value returned by the command, if any. + /// The command returned an exceptional value. + object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters); - /// - /// Registers a set of commands to be executed with this driver instance. - /// - /// An where the keys are the names of the commands to register, and the values are the objects describing the commands. - void RegisterCustomDriverCommands(IReadOnlyDictionary commands); + /// + /// Registers a set of commands to be executed with this driver instance. + /// + /// An where the keys are the names of the commands to register, and the values are the objects describing the commands. + void RegisterCustomDriverCommands(IReadOnlyDictionary commands); - /// - /// Registers a command to be executed with this driver instance. - /// - /// The unique name of the command to register. - /// The object describing the command. - /// if the command was registered; otherwise, . - bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo); - } + /// + /// Registers a command to be executed with this driver instance. + /// + /// The unique name of the command to register. + /// The object describing the command. + /// if the command was registered; otherwise, . + bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo); } diff --git a/dotnet/src/webdriver/IE/InternetExplorerDriver.cs b/dotnet/src/webdriver/IE/InternetExplorerDriver.cs index 02ba97eb5bf8f..8ead0709b7db9 100644 --- a/dotnet/src/webdriver/IE/InternetExplorerDriver.cs +++ b/dotnet/src/webdriver/IE/InternetExplorerDriver.cs @@ -21,190 +21,189 @@ using System; using System.IO; -namespace OpenQA.Selenium.IE +namespace OpenQA.Selenium.IE; + +/// +/// Provides a way to access Internet Explorer to run your tests by creating a InternetExplorerDriver instance +/// +/// +/// When the WebDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and +/// start your test. +/// +/// +/// +/// [TestFixture] +/// public class Testing +/// { +/// private IWebDriver driver; +/// +/// [SetUp] +/// public void SetUp() +/// { +/// driver = new InternetExplorerDriver(); +/// } +/// +/// [Test] +/// public void TestGoogle() +/// { +/// driver.Navigate().GoToUrl("http://www.google.co.uk/"); +/// /* +/// * Rest of the test +/// */ +/// } +/// +/// [TearDown] +/// public void TearDown() +/// { +/// driver.Quit(); +/// driver.Dispose(); +/// } +/// } +/// +/// +public class InternetExplorerDriver : WebDriver { /// - /// Provides a way to access Internet Explorer to run your tests by creating a InternetExplorerDriver instance + /// Initializes a new instance of the class. /// - /// - /// When the WebDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and - /// start your test. - /// - /// - /// - /// [TestFixture] - /// public class Testing - /// { - /// private IWebDriver driver; - /// - /// [SetUp] - /// public void SetUp() - /// { - /// driver = new InternetExplorerDriver(); - /// } - /// - /// [Test] - /// public void TestGoogle() - /// { - /// driver.Navigate().GoToUrl("http://www.google.co.uk/"); - /// /* - /// * Rest of the test - /// */ - /// } - /// - /// [TearDown] - /// public void TearDown() - /// { - /// driver.Quit(); - /// driver.Dispose(); - /// } - /// } - /// - /// - public class InternetExplorerDriver : WebDriver + public InternetExplorerDriver() + : this(new InternetExplorerOptions()) { - /// - /// Initializes a new instance of the class. - /// - public InternetExplorerDriver() - : this(new InternetExplorerOptions()) - { - } + } - /// - /// Initializes a new instance of the class with the desired - /// options. - /// - /// The used to initialize the driver. - /// If is . - public InternetExplorerDriver(InternetExplorerOptions options) - : this(InternetExplorerDriverService.CreateDefaultService(), options) - { - } + /// + /// Initializes a new instance of the class with the desired + /// options. + /// + /// The used to initialize the driver. + /// If is . + public InternetExplorerDriver(InternetExplorerOptions options) + : this(InternetExplorerDriverService.CreateDefaultService(), options) + { + } - /// - /// Initializes a new instance of the class using the specified driver service. - /// - /// The used to initialize the driver. - /// If is . - public InternetExplorerDriver(InternetExplorerDriverService service) - : this(service, new InternetExplorerOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified driver service. + /// + /// The used to initialize the driver. + /// If is . + public InternetExplorerDriver(InternetExplorerDriverService service) + : this(service, new InternetExplorerOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing IEDriverServer.exe. - /// - /// The full path to the directory containing IEDriverServer.exe. - public InternetExplorerDriver(string internetExplorerDriverServerDirectory) - : this(internetExplorerDriverServerDirectory, new InternetExplorerOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing IEDriverServer.exe. + /// + /// The full path to the directory containing IEDriverServer.exe. + public InternetExplorerDriver(string internetExplorerDriverServerDirectory) + : this(internetExplorerDriverServerDirectory, new InternetExplorerOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing IEDriverServer.exe and options. - /// - /// The full path to the directory containing IEDriverServer.exe. - /// The used to initialize the driver. - /// If is . - public InternetExplorerDriver(string internetExplorerDriverServerDirectory, InternetExplorerOptions options) - : this(internetExplorerDriverServerDirectory, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing IEDriverServer.exe and options. + /// + /// The full path to the directory containing IEDriverServer.exe. + /// The used to initialize the driver. + /// If is . + public InternetExplorerDriver(string internetExplorerDriverServerDirectory, InternetExplorerOptions options) + : this(internetExplorerDriverServerDirectory, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing IEDriverServer.exe, options, and command timeout. - /// - /// The full path to the directory containing IEDriverServer.exe. - /// The used to initialize the driver. - /// The maximum amount of time to wait for each command. - /// If is . - public InternetExplorerDriver(string internetExplorerDriverServerDirectory, InternetExplorerOptions options, TimeSpan commandTimeout) - : this(InternetExplorerDriverService.CreateDefaultService(internetExplorerDriverServerDirectory), options, commandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing IEDriverServer.exe, options, and command timeout. + /// + /// The full path to the directory containing IEDriverServer.exe. + /// The used to initialize the driver. + /// The maximum amount of time to wait for each command. + /// If is . + public InternetExplorerDriver(string internetExplorerDriverServerDirectory, InternetExplorerOptions options, TimeSpan commandTimeout) + : this(InternetExplorerDriverService.CreateDefaultService(internetExplorerDriverServerDirectory), options, commandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified - /// and options. - /// - /// The to use. - /// The used to initialize the driver. - /// If or are . - public InternetExplorerDriver(InternetExplorerDriverService service, InternetExplorerOptions options) - : this(service, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified + /// and options. + /// + /// The to use. + /// The used to initialize the driver. + /// If or are . + public InternetExplorerDriver(InternetExplorerDriverService service, InternetExplorerOptions options) + : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + { + } + + /// + /// Initializes a new instance of the class using the specified + /// , , and command timeout. + /// + /// The to use. + /// The used to initialize the driver. + /// The maximum amount of time to wait for each command. + /// If or are . + public InternetExplorerDriver(InternetExplorerDriverService service, InternetExplorerOptions options, TimeSpan commandTimeout) + : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + { + } - /// - /// Initializes a new instance of the class using the specified - /// , , and command timeout. - /// - /// The to use. - /// The used to initialize the driver. - /// The maximum amount of time to wait for each command. - /// If or are . - public InternetExplorerDriver(InternetExplorerDriverService service, InternetExplorerOptions options, TimeSpan commandTimeout) - : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + /// + /// Uses DriverFinder to set Service attributes if necessary when creating the command executor + /// + /// + /// + /// + /// + private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + { + if (service is null) { + throw new ArgumentNullException(nameof(service)); } - /// - /// Uses DriverFinder to set Service attributes if necessary when creating the command executor - /// - /// - /// - /// - /// - private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + if (options is null) { - if (service is null) - { - throw new ArgumentNullException(nameof(service)); - } - - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (service.DriverServicePath == null) - { - DriverFinder finder = new DriverFinder(options); - string fullServicePath = finder.GetDriverPath(); - service.DriverServicePath = Path.GetDirectoryName(fullServicePath); - service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); - } - return new DriverServiceCommandExecutor(service, commandTimeout); + throw new ArgumentNullException(nameof(options)); } - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// The IE driver does not allow a file detector to be set, - /// as the server component of the IE driver (IEDriverServer.exe) only - /// allows uploads from the local computer environment. Attempting to set - /// this property has no effect, but does not throw an exception. If you - /// are attempting to run the IE driver remotely, use - /// in conjunction with a standalone WebDriver server. - public override IFileDetector FileDetector + if (service.DriverServicePath == null) { - get => base.FileDetector; - set { } + DriverFinder finder = new DriverFinder(options); + string fullServicePath = finder.GetDriverPath(); + service.DriverServicePath = Path.GetDirectoryName(fullServicePath); + service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); } + return new DriverServiceCommandExecutor(service, commandTimeout); + } - private static ICapabilities ConvertOptionsToCapabilities(InternetExplorerOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options), "options must not be null"); - } + /// + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. + /// + /// The IE driver does not allow a file detector to be set, + /// as the server component of the IE driver (IEDriverServer.exe) only + /// allows uploads from the local computer environment. Attempting to set + /// this property has no effect, but does not throw an exception. If you + /// are attempting to run the IE driver remotely, use + /// in conjunction with a standalone WebDriver server. + public override IFileDetector FileDetector + { + get => base.FileDetector; + set { } + } - return options.ToCapabilities(); + private static ICapabilities ConvertOptionsToCapabilities(InternetExplorerOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options), "options must not be null"); } + + return options.ToCapabilities(); } } diff --git a/dotnet/src/webdriver/IE/InternetExplorerDriverLogLevel.cs b/dotnet/src/webdriver/IE/InternetExplorerDriverLogLevel.cs index 840c7497bcf7e..011b1486eb24d 100644 --- a/dotnet/src/webdriver/IE/InternetExplorerDriverLogLevel.cs +++ b/dotnet/src/webdriver/IE/InternetExplorerDriverLogLevel.cs @@ -17,41 +17,40 @@ // under the License. // -namespace OpenQA.Selenium.IE +namespace OpenQA.Selenium.IE; + +/// +/// Represents the valid values of logging levels available with the IEDriverServer.exe. +/// +public enum InternetExplorerDriverLogLevel { /// - /// Represents the valid values of logging levels available with the IEDriverServer.exe. + /// Represents the Trace value, the most detailed logging level available. /// - public enum InternetExplorerDriverLogLevel - { - /// - /// Represents the Trace value, the most detailed logging level available. - /// - Trace, + Trace, - /// - /// Represents the Debug value - /// - Debug, + /// + /// Represents the Debug value + /// + Debug, - /// - /// Represents the Info value - /// - Info, + /// + /// Represents the Info value + /// + Info, - /// - /// Represents the Warn value - /// - Warn, + /// + /// Represents the Warn value + /// + Warn, - /// - /// Represents the Error value - /// - Error, + /// + /// Represents the Error value + /// + Error, - /// - /// Represents the Fatal value, the least detailed logging level available. - /// - Fatal - } + /// + /// Represents the Fatal value, the least detailed logging level available. + /// + Fatal } diff --git a/dotnet/src/webdriver/IE/InternetExplorerDriverService.cs b/dotnet/src/webdriver/IE/InternetExplorerDriverService.cs index 2f5a7f10864e3..39240d9f31d2e 100644 --- a/dotnet/src/webdriver/IE/InternetExplorerDriverService.cs +++ b/dotnet/src/webdriver/IE/InternetExplorerDriverService.cs @@ -22,147 +22,146 @@ using System.IO; using System.Text; -namespace OpenQA.Selenium.IE +namespace OpenQA.Selenium.IE; + +/// +/// Exposes the service provided by the native IEDriverServer executable. +/// +public sealed class InternetExplorerDriverService : DriverService { + private const string InternetExplorerDriverServiceFileName = "IEDriverServer.exe"; + /// - /// Exposes the service provided by the native IEDriverServer executable. + /// Initializes a new instance of the class. /// - public sealed class InternetExplorerDriverService : DriverService + /// The full path to the IEDriverServer executable. + /// The file name of the IEDriverServer executable. + /// The port on which the IEDriverServer executable should listen. + private InternetExplorerDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, port, executableFileName) { - private const string InternetExplorerDriverServiceFileName = "IEDriverServer.exe"; - - /// - /// Initializes a new instance of the class. - /// - /// The full path to the IEDriverServer executable. - /// The file name of the IEDriverServer executable. - /// The port on which the IEDriverServer executable should listen. - private InternetExplorerDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, port, executableFileName) - { - } + } - /// - protected override DriverOptions GetDefaultDriverOptions() - { - return new InternetExplorerOptions(); - } + /// + protected override DriverOptions GetDefaultDriverOptions() + { + return new InternetExplorerOptions(); + } + + /// + /// Gets or sets the value of the host adapter on which the IEDriverServer should listen for connections. + /// + public string? Host { get; set; } + + /// + /// Gets or sets the location of the log file written to by the IEDriverServer. + /// + public string? LogFile { get; set; } - /// - /// Gets or sets the value of the host adapter on which the IEDriverServer should listen for connections. - /// - public string? Host { get; set; } - - /// - /// Gets or sets the location of the log file written to by the IEDriverServer. - /// - public string? LogFile { get; set; } - - /// - /// Gets or sets the logging level used by the IEDriverServer. Defaults to . - /// - public InternetExplorerDriverLogLevel LoggingLevel { get; set; } = InternetExplorerDriverLogLevel.Fatal; - - /// - /// Gets or sets the path to which the supporting library of the IEDriverServer.exe is extracted. - /// Defaults to the temp directory if this property is or . - /// - /// - /// The IEDriverServer.exe requires extraction of a supporting library to perform some of its functions. Setting - /// This library is extracted to the temp directory if this property is not set. If the property is set, it must - /// be set to a valid directory. - /// - public string? LibraryExtractionPath { get; set; } - - /// - /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the IEDriverServer. - /// If or , only the local loopback address can connect. - /// - public string? WhitelistedIPAddresses { get; set; } - - /// - /// Gets the command-line arguments for the driver service. - /// - protected override string CommandLineArguments + /// + /// Gets or sets the logging level used by the IEDriverServer. Defaults to . + /// + public InternetExplorerDriverLogLevel LoggingLevel { get; set; } = InternetExplorerDriverLogLevel.Fatal; + + /// + /// Gets or sets the path to which the supporting library of the IEDriverServer.exe is extracted. + /// Defaults to the temp directory if this property is or . + /// + /// + /// The IEDriverServer.exe requires extraction of a supporting library to perform some of its functions. Setting + /// This library is extracted to the temp directory if this property is not set. If the property is set, it must + /// be set to a valid directory. + /// + public string? LibraryExtractionPath { get; set; } + + /// + /// Gets or sets the comma-delimited list of IP addresses that are approved to connect to this instance of the IEDriverServer. + /// If or , only the local loopback address can connect. + /// + public string? WhitelistedIPAddresses { get; set; } + + /// + /// Gets the command-line arguments for the driver service. + /// + protected override string CommandLineArguments + { + get { - get + StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); + if (!string.IsNullOrEmpty(this.Host)) { - StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); - if (!string.IsNullOrEmpty(this.Host)) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -host={0}", this.Host)); - } - - if (!string.IsNullOrEmpty(this.LogFile)) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -log-file=\"{0}\"", this.LogFile)); - } - - if (!string.IsNullOrEmpty(this.LibraryExtractionPath)) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -extract-path=\"{0}\"", this.LibraryExtractionPath)); - } - - if (this.LoggingLevel != InternetExplorerDriverLogLevel.Fatal) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -log-level={0}", this.LoggingLevel.ToString().ToUpperInvariant())); - } - - if (!string.IsNullOrEmpty(this.WhitelistedIPAddresses)) - { - argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -whitelisted-ips={0}", this.WhitelistedIPAddresses)); - } - - if (this.SuppressInitialDiagnosticInformation) - { - argsBuilder.Append(" -silent"); - } - - return argsBuilder.ToString(); + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -host={0}", this.Host)); } - } - /// - /// Creates a default instance of the InternetExplorerDriverService. - /// - /// A InternetExplorerDriverService that implements default settings. - public static InternetExplorerDriverService CreateDefaultService() - { - return new InternetExplorerDriverService(null, null, PortUtilities.FindFreePort()); - } + if (!string.IsNullOrEmpty(this.LogFile)) + { + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -log-file=\"{0}\"", this.LogFile)); + } - /// - /// Creates a default instance of the InternetExplorerDriverService using a specified path to the IEDriverServer executable. - /// - /// The path to the executable or the directory containing the IEDriverServer executable. - /// A InternetExplorerDriverService using a random port. - public static InternetExplorerDriverService CreateDefaultService(string? driverPath) - { - if (File.Exists(driverPath)) + if (!string.IsNullOrEmpty(this.LibraryExtractionPath)) { - string fileName = Path.GetFileName(driverPath); - string driverFolder = Path.GetDirectoryName(driverPath)!; + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -extract-path=\"{0}\"", this.LibraryExtractionPath)); + } - return CreateDefaultService(driverFolder, fileName); + if (this.LoggingLevel != InternetExplorerDriverLogLevel.Fatal) + { + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -log-level={0}", this.LoggingLevel.ToString().ToUpperInvariant())); } - else + + if (!string.IsNullOrEmpty(this.WhitelistedIPAddresses)) { - string fileName = InternetExplorerDriverServiceFileName; - string? driverFolder = driverPath; + argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " -whitelisted-ips={0}", this.WhitelistedIPAddresses)); + } - return CreateDefaultService(driverFolder, fileName); + if (this.SuppressInitialDiagnosticInformation) + { + argsBuilder.Append(" -silent"); } + + return argsBuilder.ToString(); } + } + + /// + /// Creates a default instance of the InternetExplorerDriverService. + /// + /// A InternetExplorerDriverService that implements default settings. + public static InternetExplorerDriverService CreateDefaultService() + { + return new InternetExplorerDriverService(null, null, PortUtilities.FindFreePort()); + } + + /// + /// Creates a default instance of the InternetExplorerDriverService using a specified path to the IEDriverServer executable. + /// + /// The path to the executable or the directory containing the IEDriverServer executable. + /// A InternetExplorerDriverService using a random port. + public static InternetExplorerDriverService CreateDefaultService(string? driverPath) + { + if (File.Exists(driverPath)) + { + string fileName = Path.GetFileName(driverPath); + string driverFolder = Path.GetDirectoryName(driverPath)!; - /// - /// Creates a default instance of the InternetExplorerDriverService using a specified path to the IEDriverServer executable with the given name. - /// - /// The directory containing the IEDriverServer executable. - /// The name of the IEDriverServer executable file. - /// A InternetExplorerDriverService using a random port. - public static InternetExplorerDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + return CreateDefaultService(driverFolder, fileName); + } + else { - return new InternetExplorerDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); + string fileName = InternetExplorerDriverServiceFileName; + string? driverFolder = driverPath; + + return CreateDefaultService(driverFolder, fileName); } } + + /// + /// Creates a default instance of the InternetExplorerDriverService using a specified path to the IEDriverServer executable with the given name. + /// + /// The directory containing the IEDriverServer executable. + /// The name of the IEDriverServer executable file. + /// A InternetExplorerDriverService using a random port. + public static InternetExplorerDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + { + return new InternetExplorerDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); + } } diff --git a/dotnet/src/webdriver/IE/InternetExplorerOptions.cs b/dotnet/src/webdriver/IE/InternetExplorerOptions.cs index 598ac6d2b503e..41c20a20f3be4 100644 --- a/dotnet/src/webdriver/IE/InternetExplorerOptions.cs +++ b/dotnet/src/webdriver/IE/InternetExplorerOptions.cs @@ -20,364 +20,363 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.IE +namespace OpenQA.Selenium.IE; + +/// +/// Specifies the scroll behavior of elements scrolled into view in the IE driver. +/// +public enum InternetExplorerElementScrollBehavior +{ + /// + /// Indicates the behavior is unspecified. + /// + Default, + + /// + /// Scrolls elements to align with the top of the viewport. + /// + Top, + + /// + /// Scrolls elements to align with the bottom of the viewport. + /// + Bottom +} + +/// +/// Class to manage options specific to +/// +/// +/// +/// InternetExplorerOptions options = new InternetExplorerOptions(); +/// options.IntroduceInstabilityByIgnoringProtectedModeSettings = true; +/// +/// +/// For use with InternetExplorerDriver: +/// +/// +/// InternetExplorerDriver driver = new InternetExplorerDriver(options); +/// +/// +/// For use with RemoteWebDriver: +/// +/// +/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); +/// +/// +public class InternetExplorerOptions : DriverOptions { /// - /// Specifies the scroll behavior of elements scrolled into view in the IE driver. + /// Gets the name of the capability used to store IE options in + /// an object. + /// + public static readonly string Capability = "se:ieOptions"; + + private const string BrowserNameValue = "internet explorer"; + + private const string IgnoreProtectedModeSettingsCapability = "ignoreProtectedModeSettings"; + private const string IgnoreZoomSettingCapability = "ignoreZoomSetting"; + private const string InitialBrowserUrlCapability = "initialBrowserUrl"; + private const string EnablePersistentHoverCapability = "enablePersistentHover"; + private const string ElementScrollBehaviorCapability = "elementScrollBehavior"; + private const string RequireWindowFocusCapability = "requireWindowFocus"; + private const string BrowserAttachTimeoutCapability = "browserAttachTimeout"; + private const string BrowserCommandLineSwitchesCapability = "ie.browserCommandLineSwitches"; + private const string ForceCreateProcessApiCapability = "ie.forceCreateProcessApi"; + private const string UsePerProcessProxyCapability = "ie.usePerProcessProxy"; + private const string EnsureCleanSessionCapability = "ie.ensureCleanSession"; + private const string ForceShellWindowsApiCapability = "ie.forceShellWindowsApi"; + private const string FileUploadDialogTimeoutCapability = "ie.fileUploadDialogTimeout"; + private const string EnableFullPageScreenshotCapability = "ie.enableFullPageScreenshot"; + private const string EdgeExecutablePathCapability = "ie.edgepath"; + private const string LegacyFileUploadDialogHandlingCapability = "ie.useLegacyFileUploadDialogHandling"; + private const string AttachToEdgeChromeCapability = "ie.edgechromium"; + private const string IgnoreProcessMatchCapability = "ie.ignoreprocessmatch"; + private readonly bool enableFullPageScreenshot = true; + private readonly Dictionary additionalInternetExplorerOptions = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + public InternetExplorerOptions() : base() + { + this.BrowserName = BrowserNameValue; + this.PlatformName = "windows"; + this.AddKnownCapabilityName(Capability, "current InterentExplorerOptions class instance"); + this.AddKnownCapabilityName(IgnoreProtectedModeSettingsCapability, "IntroduceInstabilityByIgnoringProtectedModeSettings property"); + this.AddKnownCapabilityName(IgnoreZoomSettingCapability, "IgnoreZoomLevel property"); + this.AddKnownCapabilityName(CapabilityType.HasNativeEvents, "EnableNativeEvents property"); + this.AddKnownCapabilityName(InitialBrowserUrlCapability, "InitialBrowserUrl property"); + this.AddKnownCapabilityName(ElementScrollBehaviorCapability, "ElementScrollBehavior property"); + this.AddKnownCapabilityName(CapabilityType.UnexpectedAlertBehavior, "UnhandledPromptBehavior property"); + this.AddKnownCapabilityName(EnablePersistentHoverCapability, "EnablePersistentHover property"); + this.AddKnownCapabilityName(RequireWindowFocusCapability, "RequireWindowFocus property"); + this.AddKnownCapabilityName(BrowserAttachTimeoutCapability, "BrowserAttachTimeout property"); + this.AddKnownCapabilityName(ForceCreateProcessApiCapability, "ForceCreateProcessApi property"); + this.AddKnownCapabilityName(ForceShellWindowsApiCapability, "ForceShellWindowsApi property"); + this.AddKnownCapabilityName(BrowserCommandLineSwitchesCapability, "BrowserComaandLineArguments property"); + this.AddKnownCapabilityName(UsePerProcessProxyCapability, "UsePerProcessProxy property"); + this.AddKnownCapabilityName(EnsureCleanSessionCapability, "EnsureCleanSession property"); + this.AddKnownCapabilityName(FileUploadDialogTimeoutCapability, "FileUploadDialogTimeout property"); + this.AddKnownCapabilityName(EnableFullPageScreenshotCapability, "EnableFullPageScreenshot property"); + this.AddKnownCapabilityName(LegacyFileUploadDialogHandlingCapability, "LegacyFileUploadDialogHanlding property"); + this.AddKnownCapabilityName(AttachToEdgeChromeCapability, "AttachToEdgeChrome property"); + this.AddKnownCapabilityName(EdgeExecutablePathCapability, "EdgeExecutablePath property"); + this.AddKnownCapabilityName(IgnoreProcessMatchCapability, "IgnoreProcessMatch property"); + } + + /// + /// Gets or sets a value indicating whether to ignore the settings of the Internet Explorer Protected Mode. + /// + public bool IntroduceInstabilityByIgnoringProtectedModeSettings { get; set; } + + /// + /// Gets or sets a value indicating whether to ignore the zoom level of Internet Explorer . + /// + public bool IgnoreZoomLevel { get; set; } + + /// + /// Gets or sets a value indicating whether to use native events in interacting with elements. + /// + public bool EnableNativeEvents { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to require the browser window to have focus before interacting with elements. + /// + public bool RequireWindowFocus { get; set; } + + /// + /// Gets or sets the initial URL displayed when IE is launched. If not set, the browser launches + /// with the internal startup page for the WebDriver server. + /// + /// + /// By setting the to + /// and this property to a correct URL, you can launch IE in the Internet Protected Mode zone. This can be helpful + /// to avoid the flakiness introduced by ignoring the Protected Mode settings. Nevertheless, setting Protected Mode + /// zone settings to the same value in the IE configuration is the preferred method. + /// + public string? InitialBrowserUrl { get; set; } + + /// + /// Gets or sets the value for describing how elements are scrolled into view in the IE driver. Defaults + /// to scrolling the element to the top of the viewport. + /// + public InternetExplorerElementScrollBehavior ElementScrollBehavior { get; set; } = InternetExplorerElementScrollBehavior.Default; + + /// + /// Gets or sets a value indicating whether to enable persistently sending WM_MOUSEMOVE messages + /// to the IE window during a mouse hover. + /// + public bool EnablePersistentHover { get; set; } = true; + + /// + /// Gets or sets the amount of time the driver will attempt to look for a newly launched instance + /// of Internet Explorer. + /// + public TimeSpan BrowserAttachTimeout { get; set; } = TimeSpan.MinValue; + + /// + /// Gets or sets the amount of time the driver will attempt to look for the file selection + /// dialog when attempting to upload a file. + /// + public TimeSpan FileUploadDialogTimeout { get; set; } = TimeSpan.MinValue; + + /// + /// Gets or sets a value indicating whether to force the use of the Windows CreateProcess API + /// when launching Internet Explorer. The default value is . + /// + public bool ForceCreateProcessApi { get; set; } + + /// + /// Gets or sets a value indicating whether to force the use of the Windows ShellWindows API + /// when attaching to Internet Explorer. The default value is . + /// + public bool ForceShellWindowsApi { get; set; } + + /// + /// Gets or sets the command line arguments used in launching Internet Explorer when the + /// Windows CreateProcess API is used. This property only has an effect when the + /// is . + /// + public string? BrowserCommandLineArguments { get; set; } + + /// + /// Gets or sets a value indicating whether to use the supplied + /// settings on a per-process basis, not updating the system installed proxy setting. + /// This property is only valid when setting a , where the + /// property is either , + /// , or , and is + /// otherwise ignored. Defaults to . + /// + public bool UsePerProcessProxy { get; set; } + + /// + /// Gets or sets a value indicating whether to clear the Internet Explorer cache + /// before launching the browser. When set to , clears the + /// system cache for all instances of Internet Explorer, even those already running + /// when the driven instance is launched. Defaults to . + /// + public bool EnsureCleanSession { get; set; } + + /// + /// Gets or sets a value indicating whether to use the legacy handling for file upload dialogs. + /// + public bool LegacyFileUploadDialogHanlding { get; set; } + + /// + /// Gets or sets a value indicating whether to attach to Edge Chrome browser. + /// + public bool AttachToEdgeChrome { get; set; } + + /// + /// Gets or sets a value indicating whether to ignore process id match with IE Mode on Edge. + /// + public bool IgnoreProcessMatch { get; set; } + + /// + /// Gets or sets the path to the Edge Browser Executable. /// - public enum InternetExplorerElementScrollBehavior + public string? EdgeExecutablePath { get; set; } + + /// + /// Provides a means to add additional capabilities not yet added as type safe options + /// for the Internet Explorer driver. + /// + /// The name of the capability to add. + /// The value of the capability to add. + /// + /// thrown when attempting to add a capability for which there is already a type safe option, or + /// when is or the empty string. + /// + /// Calling + /// where has already been added will overwrite the + /// existing value with the new value in . + /// Calling this method adds capabilities to the IE-specific options object passed to + /// IEDriverServer.exe (property name 'se:ieOptions'). + public void AddAdditionalInternetExplorerOption(string optionName, object optionValue) { - /// - /// Indicates the behavior is unspecified. - /// - Default, - - /// - /// Scrolls elements to align with the top of the viewport. - /// - Top, - - /// - /// Scrolls elements to align with the bottom of the viewport. - /// - Bottom + this.ValidateCapabilityName(optionName); + this.additionalInternetExplorerOptions[optionName] = optionValue; } /// - /// Class to manage options specific to + /// Returns DesiredCapabilities for IE with these options included as + /// capabilities. This copies the options. Further changes will not be + /// reflected in the returned capabilities. /// - /// - /// - /// InternetExplorerOptions options = new InternetExplorerOptions(); - /// options.IntroduceInstabilityByIgnoringProtectedModeSettings = true; - /// - /// - /// For use with InternetExplorerDriver: - /// - /// - /// InternetExplorerDriver driver = new InternetExplorerDriver(options); - /// - /// - /// For use with RemoteWebDriver: - /// - /// - /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); - /// - /// - public class InternetExplorerOptions : DriverOptions + /// The DesiredCapabilities for IE with these options. + public override ICapabilities ToCapabilities() { - /// - /// Gets the name of the capability used to store IE options in - /// an object. - /// - public static readonly string Capability = "se:ieOptions"; - - private const string BrowserNameValue = "internet explorer"; - - private const string IgnoreProtectedModeSettingsCapability = "ignoreProtectedModeSettings"; - private const string IgnoreZoomSettingCapability = "ignoreZoomSetting"; - private const string InitialBrowserUrlCapability = "initialBrowserUrl"; - private const string EnablePersistentHoverCapability = "enablePersistentHover"; - private const string ElementScrollBehaviorCapability = "elementScrollBehavior"; - private const string RequireWindowFocusCapability = "requireWindowFocus"; - private const string BrowserAttachTimeoutCapability = "browserAttachTimeout"; - private const string BrowserCommandLineSwitchesCapability = "ie.browserCommandLineSwitches"; - private const string ForceCreateProcessApiCapability = "ie.forceCreateProcessApi"; - private const string UsePerProcessProxyCapability = "ie.usePerProcessProxy"; - private const string EnsureCleanSessionCapability = "ie.ensureCleanSession"; - private const string ForceShellWindowsApiCapability = "ie.forceShellWindowsApi"; - private const string FileUploadDialogTimeoutCapability = "ie.fileUploadDialogTimeout"; - private const string EnableFullPageScreenshotCapability = "ie.enableFullPageScreenshot"; - private const string EdgeExecutablePathCapability = "ie.edgepath"; - private const string LegacyFileUploadDialogHandlingCapability = "ie.useLegacyFileUploadDialogHandling"; - private const string AttachToEdgeChromeCapability = "ie.edgechromium"; - private const string IgnoreProcessMatchCapability = "ie.ignoreprocessmatch"; - private readonly bool enableFullPageScreenshot = true; - private readonly Dictionary additionalInternetExplorerOptions = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - public InternetExplorerOptions() : base() + IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(true); + + Dictionary internetExplorerOptions = this.BuildInternetExplorerOptionsDictionary(); + capabilities.SetCapability(InternetExplorerOptions.Capability, internetExplorerOptions); + + return capabilities.AsReadOnly(); + } + + private Dictionary BuildInternetExplorerOptionsDictionary() + { + Dictionary internetExplorerOptionsDictionary = new Dictionary(); + internetExplorerOptionsDictionary[CapabilityType.HasNativeEvents] = this.EnableNativeEvents; + internetExplorerOptionsDictionary[EnablePersistentHoverCapability] = this.EnablePersistentHover; + + if (this.RequireWindowFocus) { - this.BrowserName = BrowserNameValue; - this.PlatformName = "windows"; - this.AddKnownCapabilityName(Capability, "current InterentExplorerOptions class instance"); - this.AddKnownCapabilityName(IgnoreProtectedModeSettingsCapability, "IntroduceInstabilityByIgnoringProtectedModeSettings property"); - this.AddKnownCapabilityName(IgnoreZoomSettingCapability, "IgnoreZoomLevel property"); - this.AddKnownCapabilityName(CapabilityType.HasNativeEvents, "EnableNativeEvents property"); - this.AddKnownCapabilityName(InitialBrowserUrlCapability, "InitialBrowserUrl property"); - this.AddKnownCapabilityName(ElementScrollBehaviorCapability, "ElementScrollBehavior property"); - this.AddKnownCapabilityName(CapabilityType.UnexpectedAlertBehavior, "UnhandledPromptBehavior property"); - this.AddKnownCapabilityName(EnablePersistentHoverCapability, "EnablePersistentHover property"); - this.AddKnownCapabilityName(RequireWindowFocusCapability, "RequireWindowFocus property"); - this.AddKnownCapabilityName(BrowserAttachTimeoutCapability, "BrowserAttachTimeout property"); - this.AddKnownCapabilityName(ForceCreateProcessApiCapability, "ForceCreateProcessApi property"); - this.AddKnownCapabilityName(ForceShellWindowsApiCapability, "ForceShellWindowsApi property"); - this.AddKnownCapabilityName(BrowserCommandLineSwitchesCapability, "BrowserComaandLineArguments property"); - this.AddKnownCapabilityName(UsePerProcessProxyCapability, "UsePerProcessProxy property"); - this.AddKnownCapabilityName(EnsureCleanSessionCapability, "EnsureCleanSession property"); - this.AddKnownCapabilityName(FileUploadDialogTimeoutCapability, "FileUploadDialogTimeout property"); - this.AddKnownCapabilityName(EnableFullPageScreenshotCapability, "EnableFullPageScreenshot property"); - this.AddKnownCapabilityName(LegacyFileUploadDialogHandlingCapability, "LegacyFileUploadDialogHanlding property"); - this.AddKnownCapabilityName(AttachToEdgeChromeCapability, "AttachToEdgeChrome property"); - this.AddKnownCapabilityName(EdgeExecutablePathCapability, "EdgeExecutablePath property"); - this.AddKnownCapabilityName(IgnoreProcessMatchCapability, "IgnoreProcessMatch property"); + internetExplorerOptionsDictionary[RequireWindowFocusCapability] = true; } - /// - /// Gets or sets a value indicating whether to ignore the settings of the Internet Explorer Protected Mode. - /// - public bool IntroduceInstabilityByIgnoringProtectedModeSettings { get; set; } - - /// - /// Gets or sets a value indicating whether to ignore the zoom level of Internet Explorer . - /// - public bool IgnoreZoomLevel { get; set; } - - /// - /// Gets or sets a value indicating whether to use native events in interacting with elements. - /// - public bool EnableNativeEvents { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to require the browser window to have focus before interacting with elements. - /// - public bool RequireWindowFocus { get; set; } - - /// - /// Gets or sets the initial URL displayed when IE is launched. If not set, the browser launches - /// with the internal startup page for the WebDriver server. - /// - /// - /// By setting the to - /// and this property to a correct URL, you can launch IE in the Internet Protected Mode zone. This can be helpful - /// to avoid the flakiness introduced by ignoring the Protected Mode settings. Nevertheless, setting Protected Mode - /// zone settings to the same value in the IE configuration is the preferred method. - /// - public string? InitialBrowserUrl { get; set; } - - /// - /// Gets or sets the value for describing how elements are scrolled into view in the IE driver. Defaults - /// to scrolling the element to the top of the viewport. - /// - public InternetExplorerElementScrollBehavior ElementScrollBehavior { get; set; } = InternetExplorerElementScrollBehavior.Default; - - /// - /// Gets or sets a value indicating whether to enable persistently sending WM_MOUSEMOVE messages - /// to the IE window during a mouse hover. - /// - public bool EnablePersistentHover { get; set; } = true; - - /// - /// Gets or sets the amount of time the driver will attempt to look for a newly launched instance - /// of Internet Explorer. - /// - public TimeSpan BrowserAttachTimeout { get; set; } = TimeSpan.MinValue; - - /// - /// Gets or sets the amount of time the driver will attempt to look for the file selection - /// dialog when attempting to upload a file. - /// - public TimeSpan FileUploadDialogTimeout { get; set; } = TimeSpan.MinValue; - - /// - /// Gets or sets a value indicating whether to force the use of the Windows CreateProcess API - /// when launching Internet Explorer. The default value is . - /// - public bool ForceCreateProcessApi { get; set; } - - /// - /// Gets or sets a value indicating whether to force the use of the Windows ShellWindows API - /// when attaching to Internet Explorer. The default value is . - /// - public bool ForceShellWindowsApi { get; set; } - - /// - /// Gets or sets the command line arguments used in launching Internet Explorer when the - /// Windows CreateProcess API is used. This property only has an effect when the - /// is . - /// - public string? BrowserCommandLineArguments { get; set; } - - /// - /// Gets or sets a value indicating whether to use the supplied - /// settings on a per-process basis, not updating the system installed proxy setting. - /// This property is only valid when setting a , where the - /// property is either , - /// , or , and is - /// otherwise ignored. Defaults to . - /// - public bool UsePerProcessProxy { get; set; } - - /// - /// Gets or sets a value indicating whether to clear the Internet Explorer cache - /// before launching the browser. When set to , clears the - /// system cache for all instances of Internet Explorer, even those already running - /// when the driven instance is launched. Defaults to . - /// - public bool EnsureCleanSession { get; set; } - - /// - /// Gets or sets a value indicating whether to use the legacy handling for file upload dialogs. - /// - public bool LegacyFileUploadDialogHanlding { get; set; } - - /// - /// Gets or sets a value indicating whether to attach to Edge Chrome browser. - /// - public bool AttachToEdgeChrome { get; set; } - - /// - /// Gets or sets a value indicating whether to ignore process id match with IE Mode on Edge. - /// - public bool IgnoreProcessMatch { get; set; } - - /// - /// Gets or sets the path to the Edge Browser Executable. - /// - public string? EdgeExecutablePath { get; set; } - - /// - /// Provides a means to add additional capabilities not yet added as type safe options - /// for the Internet Explorer driver. - /// - /// The name of the capability to add. - /// The value of the capability to add. - /// - /// thrown when attempting to add a capability for which there is already a type safe option, or - /// when is or the empty string. - /// - /// Calling - /// where has already been added will overwrite the - /// existing value with the new value in . - /// Calling this method adds capabilities to the IE-specific options object passed to - /// IEDriverServer.exe (property name 'se:ieOptions'). - public void AddAdditionalInternetExplorerOption(string optionName, object optionValue) + if (this.IntroduceInstabilityByIgnoringProtectedModeSettings) { - this.ValidateCapabilityName(optionName); - this.additionalInternetExplorerOptions[optionName] = optionValue; + internetExplorerOptionsDictionary[IgnoreProtectedModeSettingsCapability] = true; } - /// - /// Returns DesiredCapabilities for IE with these options included as - /// capabilities. This copies the options. Further changes will not be - /// reflected in the returned capabilities. - /// - /// The DesiredCapabilities for IE with these options. - public override ICapabilities ToCapabilities() + if (this.IgnoreZoomLevel) { - IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(true); - - Dictionary internetExplorerOptions = this.BuildInternetExplorerOptionsDictionary(); - capabilities.SetCapability(InternetExplorerOptions.Capability, internetExplorerOptions); - - return capabilities.AsReadOnly(); + internetExplorerOptionsDictionary[IgnoreZoomSettingCapability] = true; } - private Dictionary BuildInternetExplorerOptionsDictionary() + if (!string.IsNullOrEmpty(this.InitialBrowserUrl)) { - Dictionary internetExplorerOptionsDictionary = new Dictionary(); - internetExplorerOptionsDictionary[CapabilityType.HasNativeEvents] = this.EnableNativeEvents; - internetExplorerOptionsDictionary[EnablePersistentHoverCapability] = this.EnablePersistentHover; - - if (this.RequireWindowFocus) - { - internetExplorerOptionsDictionary[RequireWindowFocusCapability] = true; - } - - if (this.IntroduceInstabilityByIgnoringProtectedModeSettings) - { - internetExplorerOptionsDictionary[IgnoreProtectedModeSettingsCapability] = true; - } - - if (this.IgnoreZoomLevel) - { - internetExplorerOptionsDictionary[IgnoreZoomSettingCapability] = true; - } - - if (!string.IsNullOrEmpty(this.InitialBrowserUrl)) - { - internetExplorerOptionsDictionary[InitialBrowserUrlCapability] = this.InitialBrowserUrl!; - } + internetExplorerOptionsDictionary[InitialBrowserUrlCapability] = this.InitialBrowserUrl!; + } - if (this.ElementScrollBehavior != InternetExplorerElementScrollBehavior.Default) + if (this.ElementScrollBehavior != InternetExplorerElementScrollBehavior.Default) + { + if (this.ElementScrollBehavior == InternetExplorerElementScrollBehavior.Bottom) { - if (this.ElementScrollBehavior == InternetExplorerElementScrollBehavior.Bottom) - { - internetExplorerOptionsDictionary[ElementScrollBehaviorCapability] = 1; - } - else - { - internetExplorerOptionsDictionary[ElementScrollBehaviorCapability] = 0; - } + internetExplorerOptionsDictionary[ElementScrollBehaviorCapability] = 1; } - - if (this.BrowserAttachTimeout != TimeSpan.MinValue) + else { - internetExplorerOptionsDictionary[BrowserAttachTimeoutCapability] = Convert.ToInt32(this.BrowserAttachTimeout.TotalMilliseconds); + internetExplorerOptionsDictionary[ElementScrollBehaviorCapability] = 0; } + } - if (this.FileUploadDialogTimeout != TimeSpan.MinValue) - { - internetExplorerOptionsDictionary[FileUploadDialogTimeoutCapability] = Convert.ToInt32(this.FileUploadDialogTimeout.TotalMilliseconds); - } + if (this.BrowserAttachTimeout != TimeSpan.MinValue) + { + internetExplorerOptionsDictionary[BrowserAttachTimeoutCapability] = Convert.ToInt32(this.BrowserAttachTimeout.TotalMilliseconds); + } - if (this.ForceCreateProcessApi) - { - internetExplorerOptionsDictionary[ForceCreateProcessApiCapability] = true; - if (!string.IsNullOrEmpty(this.BrowserCommandLineArguments)) - { - internetExplorerOptionsDictionary[BrowserCommandLineSwitchesCapability] = this.BrowserCommandLineArguments!; - } - } + if (this.FileUploadDialogTimeout != TimeSpan.MinValue) + { + internetExplorerOptionsDictionary[FileUploadDialogTimeoutCapability] = Convert.ToInt32(this.FileUploadDialogTimeout.TotalMilliseconds); + } - if (this.ForceShellWindowsApi) + if (this.ForceCreateProcessApi) + { + internetExplorerOptionsDictionary[ForceCreateProcessApiCapability] = true; + if (!string.IsNullOrEmpty(this.BrowserCommandLineArguments)) { - internetExplorerOptionsDictionary[ForceShellWindowsApiCapability] = true; + internetExplorerOptionsDictionary[BrowserCommandLineSwitchesCapability] = this.BrowserCommandLineArguments!; } + } - if (this.Proxy != null) - { - internetExplorerOptionsDictionary[UsePerProcessProxyCapability] = this.UsePerProcessProxy; - } + if (this.ForceShellWindowsApi) + { + internetExplorerOptionsDictionary[ForceShellWindowsApiCapability] = true; + } - if (this.EnsureCleanSession) - { - internetExplorerOptionsDictionary[EnsureCleanSessionCapability] = true; - } + if (this.Proxy != null) + { + internetExplorerOptionsDictionary[UsePerProcessProxyCapability] = this.UsePerProcessProxy; + } - if (!this.enableFullPageScreenshot) - { - internetExplorerOptionsDictionary[EnableFullPageScreenshotCapability] = false; - } + if (this.EnsureCleanSession) + { + internetExplorerOptionsDictionary[EnsureCleanSessionCapability] = true; + } - if (this.LegacyFileUploadDialogHanlding) - { - internetExplorerOptionsDictionary[LegacyFileUploadDialogHandlingCapability] = true; - } + if (!this.enableFullPageScreenshot) + { + internetExplorerOptionsDictionary[EnableFullPageScreenshotCapability] = false; + } - if (this.AttachToEdgeChrome) - { - internetExplorerOptionsDictionary[AttachToEdgeChromeCapability] = true; - } + if (this.LegacyFileUploadDialogHanlding) + { + internetExplorerOptionsDictionary[LegacyFileUploadDialogHandlingCapability] = true; + } - if (this.IgnoreProcessMatch) - { - internetExplorerOptionsDictionary[IgnoreProcessMatchCapability] = true; - } + if (this.AttachToEdgeChrome) + { + internetExplorerOptionsDictionary[AttachToEdgeChromeCapability] = true; + } - if (!string.IsNullOrEmpty(this.EdgeExecutablePath)) - { - internetExplorerOptionsDictionary[EdgeExecutablePathCapability] = this.EdgeExecutablePath!; - } + if (this.IgnoreProcessMatch) + { + internetExplorerOptionsDictionary[IgnoreProcessMatchCapability] = true; + } - foreach (KeyValuePair pair in this.additionalInternetExplorerOptions) - { - internetExplorerOptionsDictionary[pair.Key] = pair.Value; - } + if (!string.IsNullOrEmpty(this.EdgeExecutablePath)) + { + internetExplorerOptionsDictionary[EdgeExecutablePathCapability] = this.EdgeExecutablePath!; + } - return internetExplorerOptionsDictionary; + foreach (KeyValuePair pair in this.additionalInternetExplorerOptions) + { + internetExplorerOptionsDictionary[pair.Key] = pair.Value; } + + return internetExplorerOptionsDictionary; } } diff --git a/dotnet/src/webdriver/IFileDetector.cs b/dotnet/src/webdriver/IFileDetector.cs index d8f7e5686454f..fa57d0735e8e5 100644 --- a/dotnet/src/webdriver/IFileDetector.cs +++ b/dotnet/src/webdriver/IFileDetector.cs @@ -19,20 +19,19 @@ using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an object responsible for detecting sequences of keystrokes +/// representing file paths and names. +/// +public interface IFileDetector { /// - /// Defines an object responsible for detecting sequences of keystrokes - /// representing file paths and names. + /// Returns a value indicating whether a specified key sequence represents + /// a file name and path. /// - public interface IFileDetector - { - /// - /// Returns a value indicating whether a specified key sequence represents - /// a file name and path. - /// - /// The sequence to test for file existence. - /// if the key sequence represents a file; otherwise, . - bool IsFile([NotNullWhen(true)] string? keySequence); - } + /// The sequence to test for file existence. + /// if the key sequence represents a file; otherwise, . + bool IsFile([NotNullWhen(true)] string? keySequence); } diff --git a/dotnet/src/webdriver/IHasCapabilities.cs b/dotnet/src/webdriver/IHasCapabilities.cs index e15d8038ae80c..69b2d07b106e7 100644 --- a/dotnet/src/webdriver/IHasCapabilities.cs +++ b/dotnet/src/webdriver/IHasCapabilities.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can determine the capabilities of a driver. +/// +public interface IHasCapabilities { /// - /// Defines the interface through which the user can determine the capabilities of a driver. + /// Gets the object describing the driver's capabilities. /// - public interface IHasCapabilities - { - /// - /// Gets the object describing the driver's capabilities. - /// - ICapabilities Capabilities { get; } - } + ICapabilities Capabilities { get; } } diff --git a/dotnet/src/webdriver/IHasCommandExecutor.cs b/dotnet/src/webdriver/IHasCommandExecutor.cs index e9aa61f6f3da1..a2ffaac454179 100644 --- a/dotnet/src/webdriver/IHasCommandExecutor.cs +++ b/dotnet/src/webdriver/IHasCommandExecutor.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// An interface indicating that the implementing class has a command executor. +/// +public interface IHasCommandExecutor { /// - /// An interface indicating that the implementing class has a command executor. + /// Gets the command executor. /// - public interface IHasCommandExecutor - { - /// - /// Gets the command executor. - /// - ICommandExecutor CommandExecutor { get; } - } + ICommandExecutor CommandExecutor { get; } } diff --git a/dotnet/src/webdriver/IHasDownloads.cs b/dotnet/src/webdriver/IHasDownloads.cs index a01d377758fa5..ed4cdcba7a9a7 100644 --- a/dotnet/src/webdriver/IHasDownloads.cs +++ b/dotnet/src/webdriver/IHasDownloads.cs @@ -19,29 +19,28 @@ using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface indicating the driver can handle downloading remote files. +/// +public interface IHasDownloads { /// - /// Interface indicating the driver can handle downloading remote files. + /// Retrieves the downloadable files. /// - public interface IHasDownloads - { - /// - /// Retrieves the downloadable files. - /// - /// A read-only list of file names available for download. - IReadOnlyList GetDownloadableFiles(); + /// A read-only list of file names available for download. + IReadOnlyList GetDownloadableFiles(); - /// - /// Downloads a file with the specified file name and returns a dictionary containing the downloaded file's data. - /// - /// The name of the file to be downloaded. - /// The location to save the downloaded file. - void DownloadFile(string fileName, string targetDirectory); + /// + /// Downloads a file with the specified file name and returns a dictionary containing the downloaded file's data. + /// + /// The name of the file to be downloaded. + /// The location to save the downloaded file. + void DownloadFile(string fileName, string targetDirectory); - /// - /// Deletes the downloadable files. - /// - void DeleteDownloadableFiles(); - } + /// + /// Deletes the downloadable files. + /// + void DeleteDownloadableFiles(); } diff --git a/dotnet/src/webdriver/IHasSessionId.cs b/dotnet/src/webdriver/IHasSessionId.cs index 100aabaaf316a..accc3c4d575aa 100644 --- a/dotnet/src/webdriver/IHasSessionId.cs +++ b/dotnet/src/webdriver/IHasSessionId.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface indicating the driver has a Session ID. +/// +public interface IHasSessionId { /// - /// Interface indicating the driver has a Session ID. + /// Gets the session ID of the current session. /// - public interface IHasSessionId - { - /// - /// Gets the session ID of the current session. - /// - SessionId SessionId { get; } - } + SessionId SessionId { get; } } diff --git a/dotnet/src/webdriver/IJavaScriptEngine.cs b/dotnet/src/webdriver/IJavaScriptEngine.cs index 110a1935f3113..0ae8bf8cafb7f 100644 --- a/dotnet/src/webdriver/IJavaScriptEngine.cs +++ b/dotnet/src/webdriver/IJavaScriptEngine.cs @@ -21,145 +21,144 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an interface allowing the user to manage settings in the browser's JavaScript engine. +/// +public interface IJavaScriptEngine : IDisposable { /// - /// Defines an interface allowing the user to manage settings in the browser's JavaScript engine. - /// - public interface IJavaScriptEngine : IDisposable - { - /// - /// Occurs when a JavaScript callback with a named binding is executed. - /// - event EventHandler? JavaScriptCallbackExecuted; - - /// - /// Occurs when an exception is thrown by JavaScript being executed in the browser. - /// - event EventHandler? JavaScriptExceptionThrown; - - /// - /// Occurs when methods on the JavaScript console are called. - /// - event EventHandler? JavaScriptConsoleApiCalled; - - /// - /// Occurs when a value of an attribute in an element is being changed. - /// - event EventHandler? DomMutated; - - /// - /// Gets the read-only list of initialization scripts added for this JavaScript engine. - /// - IReadOnlyList InitializationScripts { get; } - - /// - /// Gets the read-only list of binding callbacks added for this JavaScript engine. - /// - IReadOnlyList ScriptCallbackBindings { get; } - - /// - /// Asynchronously starts monitoring for events from the browser's JavaScript engine. - /// - /// A task that represents the asynchronous operation. - Task StartEventMonitoring(); - - /// - /// Stops monitoring for events from the browser's JavaScript engine. - /// - void StopEventMonitoring(); - - /// - /// Enables monitoring for DOM changes. - /// - /// A task that represents the asynchronous operation. - Task EnableDomMutationMonitoring(); - - /// - /// Disables monitoring for DOM changes. - /// - /// A task that represents the asynchronous operation. - Task DisableDomMutationMonitoring(); - - /// - /// Asynchronously adds JavaScript to be loaded on every document load. - /// - /// The friendly name by which to refer to this initialization script. - /// The JavaScript to be loaded on every page. - /// A task containing an object representing the script to be loaded on each page. - /// If or are . - Task AddInitializationScript(string scriptName, string script); - - /// - /// Asynchronously removes JavaScript from being loaded on every document load. - /// - /// The friendly name of the initialization script to be removed. - /// A task that represents the asynchronous operation. - /// If is . - Task RemoveInitializationScript(string scriptName); - - /// - /// Asynchronously removes all initialization scripts from being - /// loaded on every document load. - /// - /// A task that represents the asynchronous operation. - Task ClearInitializationScripts(); - - /// - /// Pins a JavaScript snippet for execution in the browser without transmitting the - /// entire script across the wire for every execution. - /// - /// The JavaScript to pin - /// A task containing a object to use to execute the script. - /// If is . - Task PinScript(string script); - - /// - /// Unpins a previously pinned script from the browser. - /// - /// The object to unpin. - /// A task that represents the asynchronous operation. - /// If is . - Task UnpinScript(PinnedScript script); - - /// - /// Asynchronously adds a binding to a callback method that will raise - /// an event when the named binding is called by JavaScript executing - /// in the browser. - /// - /// The name of the callback that will trigger events when called by JavaScript executing in the browser. - /// A task that represents the asynchronous operation. - /// If is . - /// If A binding with the specified name already exists. - Task AddScriptCallbackBinding(string bindingName); - - /// - /// Asynchronously removes a binding to a JavaScript callback. - /// - /// The name of the callback to be removed. - /// A task that represents the asynchronous operation. - /// If is . - Task RemoveScriptCallbackBinding(string bindingName); - - /// - /// Asynchronously removes all bindings to JavaScript callbacks. - /// - /// A task that represents the asynchronous operation. - Task ClearScriptCallbackBindings(); - - /// - /// Asynchronously removes all bindings to JavaScript callbacks and all - /// initialization scripts from being loaded for each document. - /// - /// A task that represents the asynchronous operation. - Task ClearAll(); - - /// - /// Asynchronously removes all bindings to JavaScript callbacks, all - /// initialization scripts from being loaded for each document, and - /// stops listening for events. - /// - /// A task that represents the asynchronous operation. - Task Reset(); - } + /// Occurs when a JavaScript callback with a named binding is executed. + /// + event EventHandler? JavaScriptCallbackExecuted; + + /// + /// Occurs when an exception is thrown by JavaScript being executed in the browser. + /// + event EventHandler? JavaScriptExceptionThrown; + + /// + /// Occurs when methods on the JavaScript console are called. + /// + event EventHandler? JavaScriptConsoleApiCalled; + + /// + /// Occurs when a value of an attribute in an element is being changed. + /// + event EventHandler? DomMutated; + + /// + /// Gets the read-only list of initialization scripts added for this JavaScript engine. + /// + IReadOnlyList InitializationScripts { get; } + + /// + /// Gets the read-only list of binding callbacks added for this JavaScript engine. + /// + IReadOnlyList ScriptCallbackBindings { get; } + + /// + /// Asynchronously starts monitoring for events from the browser's JavaScript engine. + /// + /// A task that represents the asynchronous operation. + Task StartEventMonitoring(); + + /// + /// Stops monitoring for events from the browser's JavaScript engine. + /// + void StopEventMonitoring(); + + /// + /// Enables monitoring for DOM changes. + /// + /// A task that represents the asynchronous operation. + Task EnableDomMutationMonitoring(); + + /// + /// Disables monitoring for DOM changes. + /// + /// A task that represents the asynchronous operation. + Task DisableDomMutationMonitoring(); + + /// + /// Asynchronously adds JavaScript to be loaded on every document load. + /// + /// The friendly name by which to refer to this initialization script. + /// The JavaScript to be loaded on every page. + /// A task containing an object representing the script to be loaded on each page. + /// If or are . + Task AddInitializationScript(string scriptName, string script); + + /// + /// Asynchronously removes JavaScript from being loaded on every document load. + /// + /// The friendly name of the initialization script to be removed. + /// A task that represents the asynchronous operation. + /// If is . + Task RemoveInitializationScript(string scriptName); + + /// + /// Asynchronously removes all initialization scripts from being + /// loaded on every document load. + /// + /// A task that represents the asynchronous operation. + Task ClearInitializationScripts(); + + /// + /// Pins a JavaScript snippet for execution in the browser without transmitting the + /// entire script across the wire for every execution. + /// + /// The JavaScript to pin + /// A task containing a object to use to execute the script. + /// If is . + Task PinScript(string script); + + /// + /// Unpins a previously pinned script from the browser. + /// + /// The object to unpin. + /// A task that represents the asynchronous operation. + /// If is . + Task UnpinScript(PinnedScript script); + + /// + /// Asynchronously adds a binding to a callback method that will raise + /// an event when the named binding is called by JavaScript executing + /// in the browser. + /// + /// The name of the callback that will trigger events when called by JavaScript executing in the browser. + /// A task that represents the asynchronous operation. + /// If is . + /// If A binding with the specified name already exists. + Task AddScriptCallbackBinding(string bindingName); + + /// + /// Asynchronously removes a binding to a JavaScript callback. + /// + /// The name of the callback to be removed. + /// A task that represents the asynchronous operation. + /// If is . + Task RemoveScriptCallbackBinding(string bindingName); + + /// + /// Asynchronously removes all bindings to JavaScript callbacks. + /// + /// A task that represents the asynchronous operation. + Task ClearScriptCallbackBindings(); + + /// + /// Asynchronously removes all bindings to JavaScript callbacks and all + /// initialization scripts from being loaded for each document. + /// + /// A task that represents the asynchronous operation. + Task ClearAll(); + + /// + /// Asynchronously removes all bindings to JavaScript callbacks, all + /// initialization scripts from being loaded for each document, and + /// stops listening for events. + /// + /// A task that represents the asynchronous operation. + Task Reset(); } diff --git a/dotnet/src/webdriver/IJavascriptExecutor.cs b/dotnet/src/webdriver/IJavascriptExecutor.cs index 6a86f91ad8c27..5b1fe570e02b3 100644 --- a/dotnet/src/webdriver/IJavascriptExecutor.cs +++ b/dotnet/src/webdriver/IJavascriptExecutor.cs @@ -20,94 +20,93 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can execute JavaScript. +/// +public interface IJavaScriptExecutor { /// - /// Defines the interface through which the user can execute JavaScript. + /// Executes JavaScript in the context of the currently selected frame or window. /// - public interface IJavaScriptExecutor - { - /// - /// Executes JavaScript in the context of the currently selected frame or window. - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// - /// - /// The ExecuteScript method executes JavaScript - /// in the context of the currently selected frame or window. This means that "document" - /// will refer to the current document. If the script has a return value, then the following - /// steps will be taken: - /// - /// - /// - /// For an HTML element, this method returns a - /// For a number, a is returned - /// For a boolean, a is returned - /// For all other cases a is returned. - /// For an array,we check the first element, and attempt to return a - /// of that type, following the rules above. Nested lists are not - /// supported. - /// If the value is null or there is no return value, - /// is returned. - /// - /// - /// - /// Arguments must be a number (which will be converted to a ), - /// a , a or a , - /// or a . - /// An exception will be thrown if the arguments do not meet these criteria. - /// The arguments will be made available to the JavaScript via the "arguments" magic - /// variable, as if the function were called via "Function.apply" - /// - /// - object? ExecuteScript(string script, params object?[] args); + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// + /// + /// The ExecuteScript method executes JavaScript + /// in the context of the currently selected frame or window. This means that "document" + /// will refer to the current document. If the script has a return value, then the following + /// steps will be taken: + /// + /// + /// + /// For an HTML element, this method returns a + /// For a number, a is returned + /// For a boolean, a is returned + /// For all other cases a is returned. + /// For an array,we check the first element, and attempt to return a + /// of that type, following the rules above. Nested lists are not + /// supported. + /// If the value is null or there is no return value, + /// is returned. + /// + /// + /// + /// Arguments must be a number (which will be converted to a ), + /// a , a or a , + /// or a . + /// An exception will be thrown if the arguments do not meet these criteria. + /// The arguments will be made available to the JavaScript via the "arguments" magic + /// variable, as if the function were called via "Function.apply" + /// + /// + object? ExecuteScript(string script, params object?[] args); - /// - /// Executes JavaScript in the context of the currently selected frame or window. - /// - /// A object containing the code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// - /// - /// The ExecuteScript method executes JavaScript - /// in the context of the currently selected frame or window. This means that "document" - /// will refer to the current document. If the script has a return value, then the following - /// steps will be taken: - /// - /// - /// - /// For an HTML element, this method returns a - /// For a number, a is returned - /// For a boolean, a is returned - /// For all other cases a is returned. - /// For an array,we check the first element, and attempt to return a - /// of that type, following the rules above. Nested lists are not - /// supported. - /// If the value is null or there is no return value, - /// is returned. - /// - /// - /// - /// Arguments must be a number (which will be converted to a ), - /// a , a or a , - /// or a . - /// An exception will be thrown if the arguments do not meet these criteria. - /// The arguments will be made available to the JavaScript via the "arguments" magic - /// variable, as if the function were called via "Function.apply" - /// - /// - /// If is . - object? ExecuteScript(PinnedScript script, params object?[] args); + /// + /// Executes JavaScript in the context of the currently selected frame or window. + /// + /// A object containing the code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// + /// + /// The ExecuteScript method executes JavaScript + /// in the context of the currently selected frame or window. This means that "document" + /// will refer to the current document. If the script has a return value, then the following + /// steps will be taken: + /// + /// + /// + /// For an HTML element, this method returns a + /// For a number, a is returned + /// For a boolean, a is returned + /// For all other cases a is returned. + /// For an array,we check the first element, and attempt to return a + /// of that type, following the rules above. Nested lists are not + /// supported. + /// If the value is null or there is no return value, + /// is returned. + /// + /// + /// + /// Arguments must be a number (which will be converted to a ), + /// a , a or a , + /// or a . + /// An exception will be thrown if the arguments do not meet these criteria. + /// The arguments will be made available to the JavaScript via the "arguments" magic + /// variable, as if the function were called via "Function.apply" + /// + /// + /// If is . + object? ExecuteScript(PinnedScript script, params object?[] args); - /// - /// Executes JavaScript asynchronously in the context of the currently selected frame or window. - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - object? ExecuteAsyncScript(string script, params object?[] args); - } + /// + /// Executes JavaScript asynchronously in the context of the currently selected frame or window. + /// + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + object? ExecuteAsyncScript(string script, params object?[] args); } diff --git a/dotnet/src/webdriver/ILocatable.cs b/dotnet/src/webdriver/ILocatable.cs index eeb0bfa0ef475..16597199281a4 100644 --- a/dotnet/src/webdriver/ILocatable.cs +++ b/dotnet/src/webdriver/ILocatable.cs @@ -20,23 +20,22 @@ using OpenQA.Selenium.Interactions.Internal; using System.Drawing; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can discover where an element is on the screen. +/// +public interface ILocatable { /// - /// Defines the interface through which the user can discover where an element is on the screen. + /// Gets the location of an element on the screen, scrolling it into view + /// if it is not currently on the screen. /// - public interface ILocatable - { - /// - /// Gets the location of an element on the screen, scrolling it into view - /// if it is not currently on the screen. - /// - Point LocationOnScreenOnceScrolledIntoView { get; } + Point LocationOnScreenOnceScrolledIntoView { get; } - /// - /// Gets the coordinates identifying the location of this element using - /// various frames of reference. - /// - ICoordinates Coordinates { get; } - } + /// + /// Gets the coordinates identifying the location of this element using + /// various frames of reference. + /// + ICoordinates Coordinates { get; } } diff --git a/dotnet/src/webdriver/ILogs.cs b/dotnet/src/webdriver/ILogs.cs index ad78ac357c24e..eda0da9efa6f9 100644 --- a/dotnet/src/webdriver/ILogs.cs +++ b/dotnet/src/webdriver/ILogs.cs @@ -20,25 +20,24 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface allowing handling of driver logs. +/// +public interface ILogs { /// - /// Interface allowing handling of driver logs. + /// Gets the list of available log types for this driver. /// - public interface ILogs - { - /// - /// Gets the list of available log types for this driver. - /// - ReadOnlyCollection AvailableLogTypes { get; } + ReadOnlyCollection AvailableLogTypes { get; } - /// - /// Gets the set of objects for a specified log. - /// - /// The log for which to retrieve the log entries. - /// Log types can be found in the class. - /// The list of objects for the specified log. - /// If is . - ReadOnlyCollection GetLog(string logKind); - } + /// + /// Gets the set of objects for a specified log. + /// + /// The log for which to retrieve the log entries. + /// Log types can be found in the class. + /// The list of objects for the specified log. + /// If is . + ReadOnlyCollection GetLog(string logKind); } diff --git a/dotnet/src/webdriver/INavigation.cs b/dotnet/src/webdriver/INavigation.cs index 89cd974405a9f..58b909723ad96 100644 --- a/dotnet/src/webdriver/INavigation.cs +++ b/dotnet/src/webdriver/INavigation.cs @@ -20,94 +20,93 @@ using System; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an interface allowing the user to access the browser's history and to +/// navigate to a given URL. +/// +public interface INavigation { /// - /// Defines an interface allowing the user to access the browser's history and to - /// navigate to a given URL. + /// Move back a single entry in the browser's history. /// - public interface INavigation - { - /// - /// Move back a single entry in the browser's history. - /// - void Back(); + void Back(); - /// - /// Move back a single entry in the browser's history as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - Task BackAsync(); + /// + /// Move back a single entry in the browser's history as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + Task BackAsync(); - /// - /// Move a single "item" forward in the browser's history. - /// - /// Does nothing if we are on the latest page viewed. - void Forward(); + /// + /// Move a single "item" forward in the browser's history. + /// + /// Does nothing if we are on the latest page viewed. + void Forward(); - /// - /// Move a single "item" forward in the browser's history as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - Task ForwardAsync(); + /// + /// Move a single "item" forward in the browser's history as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + Task ForwardAsync(); - /// - /// Load a new web page in the current browser window. - /// - /// The URL to load. It is best to use a fully qualified URL - /// - /// Calling the method will load a new web page in the current browser window. - /// This is done using an HTTP GET operation, and the method will block until the - /// load is complete. This will follow redirects issued either by the server or - /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" - /// for any duration of time, it is best to wait until this timeout is over, since - /// should the underlying page change while your test is executing the results of - /// future calls against this interface will be against the freshly loaded page. - /// - /// If is . - void GoToUrl(string url); + /// + /// Load a new web page in the current browser window. + /// + /// The URL to load. It is best to use a fully qualified URL + /// + /// Calling the method will load a new web page in the current browser window. + /// This is done using an HTTP GET operation, and the method will block until the + /// load is complete. This will follow redirects issued either by the server or + /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" + /// for any duration of time, it is best to wait until this timeout is over, since + /// should the underlying page change while your test is executing the results of + /// future calls against this interface will be against the freshly loaded page. + /// + /// If is . + void GoToUrl(string url); - /// - /// Navigate to a url as an asynchronous task. - /// - /// String of where you want the browser to go. - /// A task object representing the asynchronous operation. - /// If is . - Task GoToUrlAsync(string url); + /// + /// Navigate to a url as an asynchronous task. + /// + /// String of where you want the browser to go. + /// A task object representing the asynchronous operation. + /// If is . + Task GoToUrlAsync(string url); - /// - /// Load a new web page in the current browser window. - /// - /// The URL to load. - /// - /// Calling the method will load a new web page in the current browser window. - /// This is done using an HTTP GET operation, and the method will block until the - /// load is complete. This will follow redirects issued either by the server or - /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" - /// for any duration of time, it is best to wait until this timeout is over, since - /// should the underlying page change while your test is executing the results of - /// future calls against this interface will be against the freshly loaded page. - /// - /// If is . - void GoToUrl(Uri url); + /// + /// Load a new web page in the current browser window. + /// + /// The URL to load. + /// + /// Calling the method will load a new web page in the current browser window. + /// This is done using an HTTP GET operation, and the method will block until the + /// load is complete. This will follow redirects issued either by the server or + /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" + /// for any duration of time, it is best to wait until this timeout is over, since + /// should the underlying page change while your test is executing the results of + /// future calls against this interface will be against the freshly loaded page. + /// + /// If is . + void GoToUrl(Uri url); - /// - /// Navigate to a url as an asynchronous task. - /// - /// Uri object of where you want the browser to go. - /// A task object representing the asynchronous operation. - /// If is . - Task GoToUrlAsync(Uri url); + /// + /// Navigate to a url as an asynchronous task. + /// + /// Uri object of where you want the browser to go. + /// A task object representing the asynchronous operation. + /// If is . + Task GoToUrlAsync(Uri url); - /// - /// Refreshes the current page. - /// - void Refresh(); + /// + /// Refreshes the current page. + /// + void Refresh(); - /// - /// Reload the current page as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - Task RefreshAsync(); - } + /// + /// Reload the current page as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + Task RefreshAsync(); } diff --git a/dotnet/src/webdriver/INetwork.cs b/dotnet/src/webdriver/INetwork.cs index 0b6f527265539..4e816b95aa487 100644 --- a/dotnet/src/webdriver/INetwork.cs +++ b/dotnet/src/webdriver/INetwork.cs @@ -21,73 +21,72 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an interface allowing the user to manage network communication by the browser. +/// +public interface INetwork { /// - /// Defines an interface allowing the user to manage network communication by the browser. + /// Occurs when a browser sends a network request. /// - public interface INetwork - { - /// - /// Occurs when a browser sends a network request. - /// - event EventHandler? NetworkRequestSent; + event EventHandler? NetworkRequestSent; - /// - /// Occurs when a browser receives a network response. - /// - event EventHandler? NetworkResponseReceived; + /// + /// Occurs when a browser receives a network response. + /// + event EventHandler? NetworkResponseReceived; - /// - /// Adds a to examine incoming network requests, - /// and optionally modify the request or provide a response. - /// - /// The to add. - void AddRequestHandler(NetworkRequestHandler handler); + /// + /// Adds a to examine incoming network requests, + /// and optionally modify the request or provide a response. + /// + /// The to add. + void AddRequestHandler(NetworkRequestHandler handler); - /// - /// Clears all added instances. - /// - void ClearRequestHandlers(); + /// + /// Clears all added instances. + /// + void ClearRequestHandlers(); - /// - /// Adds a to supply authentication - /// credentials for network requests. - /// - /// The to add. - void AddAuthenticationHandler(NetworkAuthenticationHandler handler); + /// + /// Adds a to supply authentication + /// credentials for network requests. + /// + /// The to add. + void AddAuthenticationHandler(NetworkAuthenticationHandler handler); - /// - /// Clears all added instances. - /// - void ClearAuthenticationHandlers(); + /// + /// Clears all added instances. + /// + void ClearAuthenticationHandlers(); - /// - /// Adds a to examine received network responses, - /// and optionally modify the response. - /// - /// The to add. - void AddResponseHandler(NetworkResponseHandler handler); + /// + /// Adds a to examine received network responses, + /// and optionally modify the response. + /// + /// The to add. + void AddResponseHandler(NetworkResponseHandler handler); - /// - /// Clears all added instances. - /// - void ClearResponseHandlers(); + /// + /// Clears all added instances. + /// + void ClearResponseHandlers(); - /// - /// Asynchronously starts monitoring for network traffic. - /// - /// A task that represents the asynchronous operation. - [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] - [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] - Task StartMonitoring(); + /// + /// Asynchronously starts monitoring for network traffic. + /// + /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] + Task StartMonitoring(); - /// - /// Asynchronously stops monitoring for network traffic. - /// - /// A task that represents the asynchronous operation. - [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] - [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] - Task StopMonitoring(); - } + /// + /// Asynchronously stops monitoring for network traffic. + /// + /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] + Task StopMonitoring(); } diff --git a/dotnet/src/webdriver/IOptions.cs b/dotnet/src/webdriver/IOptions.cs index 78fd14514ba37..28bee685e1f65 100644 --- a/dotnet/src/webdriver/IOptions.cs +++ b/dotnet/src/webdriver/IOptions.cs @@ -17,39 +17,38 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines an interface allowing the user to set options on the browser. +/// +public interface IOptions { /// - /// Defines an interface allowing the user to set options on the browser. + /// Gets an object allowing the user to manipulate cookies on the page. /// - public interface IOptions - { - /// - /// Gets an object allowing the user to manipulate cookies on the page. - /// - ICookieJar Cookies { get; } + ICookieJar Cookies { get; } - /// - /// Gets an object allowing the user to manipulate the currently-focused browser window. - /// - /// "Currently-focused" is defined as the browser window having the window handle - /// returned when IWebDriver.CurrentWindowHandle is called. - IWindow Window { get; } + /// + /// Gets an object allowing the user to manipulate the currently-focused browser window. + /// + /// "Currently-focused" is defined as the browser window having the window handle + /// returned when IWebDriver.CurrentWindowHandle is called. + IWindow Window { get; } - /// - /// Gets an object allowing the user to examine the logs for this driver instance. - /// - ILogs Logs { get; } + /// + /// Gets an object allowing the user to examine the logs for this driver instance. + /// + ILogs Logs { get; } - /// - /// Gets an object allowing the user to manage network communication by the browser. - /// - INetwork Network { get; } + /// + /// Gets an object allowing the user to manage network communication by the browser. + /// + INetwork Network { get; } - /// - /// Provides access to the timeouts defined for this driver. - /// - /// An object implementing the interface. - ITimeouts Timeouts(); - } + /// + /// Provides access to the timeouts defined for this driver. + /// + /// An object implementing the interface. + ITimeouts Timeouts(); } diff --git a/dotnet/src/webdriver/IRotatable.cs b/dotnet/src/webdriver/IRotatable.cs index e67b0d401a9dc..a37d32ada497b 100644 --- a/dotnet/src/webdriver/IRotatable.cs +++ b/dotnet/src/webdriver/IRotatable.cs @@ -17,18 +17,17 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents rotation of the browser view for orientation-sensitive devices. +/// When using this with a real device, the device should not be moved so that +/// the built-in sensors do not interfere. +/// +public interface IRotatable { /// - /// Represents rotation of the browser view for orientation-sensitive devices. - /// When using this with a real device, the device should not be moved so that - /// the built-in sensors do not interfere. + /// Gets or sets the screen orientation of the browser on the device. /// - public interface IRotatable - { - /// - /// Gets or sets the screen orientation of the browser on the device. - /// - ScreenOrientation Orientation { get; set; } - } + ScreenOrientation Orientation { get; set; } } diff --git a/dotnet/src/webdriver/ISearchContext.cs b/dotnet/src/webdriver/ISearchContext.cs index 53c5f33ae36da..c3892654ae327 100644 --- a/dotnet/src/webdriver/ISearchContext.cs +++ b/dotnet/src/webdriver/ISearchContext.cs @@ -20,29 +20,28 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface used to search for elements. +/// +public interface ISearchContext { /// - /// Defines the interface used to search for elements. + /// Finds the first using the given method. /// - public interface ISearchContext - { - /// - /// Finds the first using the given method. - /// - /// The locating mechanism to use. - /// The first matching on the current context. - /// If is . - /// If no element matches the criteria. - IWebElement FindElement(By by); + /// The locating mechanism to use. + /// The first matching on the current context. + /// If is . + /// If no element matches the criteria. + IWebElement FindElement(By by); - /// - /// Finds all IWebElements within the current context - /// using the given mechanism. - /// - /// The locating mechanism to use. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - ReadOnlyCollection FindElements(By by); - } + /// + /// Finds all IWebElements within the current context + /// using the given mechanism. + /// + /// The locating mechanism to use. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + ReadOnlyCollection FindElements(By by); } diff --git a/dotnet/src/webdriver/ISupportsLogs.cs b/dotnet/src/webdriver/ISupportsLogs.cs index 1f78ba08f754d..f79598371016d 100644 --- a/dotnet/src/webdriver/ISupportsLogs.cs +++ b/dotnet/src/webdriver/ISupportsLogs.cs @@ -17,13 +17,12 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Marker interface indicating that an object implementing +/// the interface supports the legacy log retrieval methods. +/// +public interface ISupportsLogs { - /// - /// Marker interface indicating that an object implementing - /// the interface supports the legacy log retrieval methods. - /// - public interface ISupportsLogs - { - } } diff --git a/dotnet/src/webdriver/ISupportsPrint.cs b/dotnet/src/webdriver/ISupportsPrint.cs index 9d40d81f815bc..fa438e4a55786 100644 --- a/dotnet/src/webdriver/ISupportsPrint.cs +++ b/dotnet/src/webdriver/ISupportsPrint.cs @@ -19,19 +19,18 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Interface allowing a driver implementation to generate a print representation of the page. +/// +public interface ISupportsPrint { /// - /// Interface allowing a driver implementation to generate a print representation of the page. + /// Gets a object representing a PDF-formatted print representation of the page. /// - public interface ISupportsPrint - { - /// - /// Gets a object representing a PDF-formatted print representation of the page. - /// - /// A object describing the options of the printed document. - /// The object containing the PDF-formatted print representation of the page. - /// If is . - PrintDocument Print(PrintOptions options); - } + /// A object describing the options of the printed document. + /// The object containing the PDF-formatted print representation of the page. + /// If is . + PrintDocument Print(PrintOptions options); } diff --git a/dotnet/src/webdriver/ITakesScreenshot.cs b/dotnet/src/webdriver/ITakesScreenshot.cs index f2b7de679de5d..02189c6070a41 100644 --- a/dotnet/src/webdriver/ITakesScreenshot.cs +++ b/dotnet/src/webdriver/ITakesScreenshot.cs @@ -17,17 +17,16 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface used to take screen shot images of the screen. +/// +public interface ITakesScreenshot { /// - /// Defines the interface used to take screen shot images of the screen. + /// Gets a object representing the image of the page on the screen. /// - public interface ITakesScreenshot - { - /// - /// Gets a object representing the image of the page on the screen. - /// - /// A object containing the image. - Screenshot GetScreenshot(); - } + /// A object containing the image. + Screenshot GetScreenshot(); } diff --git a/dotnet/src/webdriver/ITargetLocator.cs b/dotnet/src/webdriver/ITargetLocator.cs index f241d48c0d1de..cb787cabc2f2a 100644 --- a/dotnet/src/webdriver/ITargetLocator.cs +++ b/dotnet/src/webdriver/ITargetLocator.cs @@ -20,85 +20,84 @@ using OpenQA.Selenium.Internal; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can locate a given frame or window. +/// +public interface ITargetLocator { /// - /// Defines the interface through which the user can locate a given frame or window. + /// Select a frame by its (zero-based) index. /// - public interface ITargetLocator - { - /// - /// Select a frame by its (zero-based) index. - /// - /// The zero-based index of the frame to select. - /// An instance focused on the specified frame. - /// If the frame cannot be found. - IWebDriver Frame(int frameIndex); + /// The zero-based index of the frame to select. + /// An instance focused on the specified frame. + /// If the frame cannot be found. + IWebDriver Frame(int frameIndex); - /// - /// Select a frame by its name or ID. - /// - /// The name of the frame to select. - /// An instance focused on the specified frame. - /// If the frame cannot be found. - /// If is . - IWebDriver Frame(string frameName); + /// + /// Select a frame by its name or ID. + /// + /// The name of the frame to select. + /// An instance focused on the specified frame. + /// If the frame cannot be found. + /// If is . + IWebDriver Frame(string frameName); - /// - /// Select a frame using its previously located - /// - /// The frame element to switch to. - /// An instance focused on the specified frame. - /// If the element is neither a FRAME nor an IFRAME element. - /// If the element is no longer valid. - /// If is . - /// If cannot be converted to an . - IWebDriver Frame(IWebElement frameElement); + /// + /// Select a frame using its previously located + /// + /// The frame element to switch to. + /// An instance focused on the specified frame. + /// If the element is neither a FRAME nor an IFRAME element. + /// If the element is no longer valid. + /// If is . + /// If cannot be converted to an . + IWebDriver Frame(IWebElement frameElement); - /// - /// Select the parent frame of the currently selected frame. - /// - /// An instance focused on the specified frame. - IWebDriver ParentFrame(); + /// + /// Select the parent frame of the currently selected frame. + /// + /// An instance focused on the specified frame. + IWebDriver ParentFrame(); - /// - /// Switches the focus of future commands for this driver to the window with the given name. - /// - /// The name of the window to select. - /// An instance focused on the given window. - /// If the window cannot be found. - /// If is . - IWebDriver Window(string windowName); + /// + /// Switches the focus of future commands for this driver to the window with the given name. + /// + /// The name of the window to select. + /// An instance focused on the given window. + /// If the window cannot be found. + /// If is . + IWebDriver Window(string windowName); - /// - /// Creates a new browser window and switches the focus for future commands - /// of this driver to the new window. - /// - /// The type of new browser window to be created. - /// The created window is not guaranteed to be of the requested type; if - /// the driver does not support the requested type, a new browser window - /// will be created of whatever type the driver does support. - /// An instance focused on the new browser. - IWebDriver NewWindow(WindowType typeHint); + /// + /// Creates a new browser window and switches the focus for future commands + /// of this driver to the new window. + /// + /// The type of new browser window to be created. + /// The created window is not guaranteed to be of the requested type; if + /// the driver does not support the requested type, a new browser window + /// will be created of whatever type the driver does support. + /// An instance focused on the new browser. + IWebDriver NewWindow(WindowType typeHint); - /// - /// Selects either the first frame on the page or the main document when a page contains iFrames. - /// - /// An instance focused on the default frame. - IWebDriver DefaultContent(); + /// + /// Selects either the first frame on the page or the main document when a page contains iFrames. + /// + /// An instance focused on the default frame. + IWebDriver DefaultContent(); - /// - /// Switches to the element that currently has the focus, or the body element - /// if no element with focus can be detected. - /// - /// An instance representing the element - /// with the focus, or the body element if no element with focus can be detected. - IWebElement ActiveElement(); + /// + /// Switches to the element that currently has the focus, or the body element + /// if no element with focus can be detected. + /// + /// An instance representing the element + /// with the focus, or the body element if no element with focus can be detected. + IWebElement ActiveElement(); - /// - /// Switches to the currently active modal dialog for this particular driver instance. - /// - /// A handle to the dialog. - IAlert Alert(); - } + /// + /// Switches to the currently active modal dialog for this particular driver instance. + /// + /// A handle to the dialog. + IAlert Alert(); } diff --git a/dotnet/src/webdriver/ITimeouts.cs b/dotnet/src/webdriver/ITimeouts.cs index 2e39ace5227f4..55d9560d08dcc 100644 --- a/dotnet/src/webdriver/ITimeouts.cs +++ b/dotnet/src/webdriver/ITimeouts.cs @@ -19,45 +19,44 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can define timeouts. +/// +public interface ITimeouts { /// - /// Defines the interface through which the user can define timeouts. + /// Gets or sets the implicit wait timeout, which is the amount of time the + /// driver should wait when searching for an element if it is not immediately + /// present. /// - public interface ITimeouts - { - /// - /// Gets or sets the implicit wait timeout, which is the amount of time the - /// driver should wait when searching for an element if it is not immediately - /// present. - /// - /// - /// When searching for a single element, the driver should poll the page - /// until the element has been found, or this timeout expires before throwing - /// a . When searching for multiple elements, - /// the driver should poll the page until at least one element has been found - /// or this timeout has expired. - /// - /// Increasing the implicit wait timeout should be used judiciously as it - /// will have an adverse effect on test run time, especially when used with - /// slower location strategies like XPath. - /// - /// - TimeSpan ImplicitWait { get; set; } + /// + /// When searching for a single element, the driver should poll the page + /// until the element has been found, or this timeout expires before throwing + /// a . When searching for multiple elements, + /// the driver should poll the page until at least one element has been found + /// or this timeout has expired. + /// + /// Increasing the implicit wait timeout should be used judiciously as it + /// will have an adverse effect on test run time, especially when used with + /// slower location strategies like XPath. + /// + /// + TimeSpan ImplicitWait { get; set; } - /// - /// Gets or sets the asynchronous script timeout, which is the amount - /// of time the driver should wait when executing JavaScript asynchronously. - /// This timeout only affects the - /// method. - /// - TimeSpan AsynchronousJavaScript { get; set; } + /// + /// Gets or sets the asynchronous script timeout, which is the amount + /// of time the driver should wait when executing JavaScript asynchronously. + /// This timeout only affects the + /// method. + /// + TimeSpan AsynchronousJavaScript { get; set; } - /// - /// Gets or sets the page load timeout, which is the amount of time the driver - /// should wait for a page to load when setting the - /// property. - /// - TimeSpan PageLoad { get; set; } - } + /// + /// Gets or sets the page load timeout, which is the amount of time the driver + /// should wait for a page to load when setting the + /// property. + /// + TimeSpan PageLoad { get; set; } } diff --git a/dotnet/src/webdriver/IWebDriver.cs b/dotnet/src/webdriver/IWebDriver.cs index 9fba695808c4f..d01f06a883770 100644 --- a/dotnet/src/webdriver/IWebDriver.cs +++ b/dotnet/src/webdriver/IWebDriver.cs @@ -20,107 +20,106 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user controls the browser. +/// +/// +/// The interface is the main interface to use for testing, which +/// represents an idealized web browser. The methods in this class fall into three categories: +/// +/// Control of the browser itself +/// Selection of IWebElements +/// Debugging aids +/// +/// +/// Key properties and methods are , which is used to +/// load a new web page by setting the property, and the various methods similar +/// to , which is used to find IWebElements. +/// +/// +/// You use the interface by instantiate drivers that implement of this interface. +/// You should write your tests against this interface so that you may "swap in" a +/// more fully featured browser when there is a requirement for one. +/// +/// +public interface IWebDriver : ISearchContext, IDisposable { /// - /// Defines the interface through which the user controls the browser. + /// Gets or sets the URL the browser is currently displaying. /// /// - /// The interface is the main interface to use for testing, which - /// represents an idealized web browser. The methods in this class fall into three categories: - /// - /// Control of the browser itself - /// Selection of IWebElements - /// Debugging aids - /// - /// - /// Key properties and methods are , which is used to - /// load a new web page by setting the property, and the various methods similar - /// to , which is used to find IWebElements. - /// - /// - /// You use the interface by instantiate drivers that implement of this interface. - /// You should write your tests against this interface so that you may "swap in" a - /// more fully featured browser when there is a requirement for one. - /// + /// Setting the property will load a new web page in the current browser window. + /// This is done using an HTTP GET operation, and the method will block until the + /// load is complete. This will follow redirects issued either by the server or + /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" + /// for any duration of time, it is best to wait until this timeout is over, since + /// should the underlying page change while your test is executing the results of + /// future calls against this interface will be against the freshly loaded page. /// - public interface IWebDriver : ISearchContext, IDisposable - { - /// - /// Gets or sets the URL the browser is currently displaying. - /// - /// - /// Setting the property will load a new web page in the current browser window. - /// This is done using an HTTP GET operation, and the method will block until the - /// load is complete. This will follow redirects issued either by the server or - /// as a meta-redirect from within the returned HTML. Should a meta-redirect "rest" - /// for any duration of time, it is best to wait until this timeout is over, since - /// should the underlying page change while your test is executing the results of - /// future calls against this interface will be against the freshly loaded page. - /// - /// - /// - string Url { get; set; } + /// + /// + string Url { get; set; } - /// - /// Gets the title of the current browser window. - /// - string Title { get; } + /// + /// Gets the title of the current browser window. + /// + string Title { get; } - /// - /// Gets the source of the page last loaded by the browser. - /// - /// - /// If the page has been modified after loading (for example, by JavaScript) - /// there is no guarantee that the returned text is that of the modified page. - /// Please consult the documentation of the particular driver being used to - /// determine whether the returned text reflects the current state of the page - /// or the text last sent by the web server. The page source returned is a - /// representation of the underlying DOM: do not expect it to be formatted - /// or escaped in the same way as the response sent from the web server. - /// - string PageSource { get; } + /// + /// Gets the source of the page last loaded by the browser. + /// + /// + /// If the page has been modified after loading (for example, by JavaScript) + /// there is no guarantee that the returned text is that of the modified page. + /// Please consult the documentation of the particular driver being used to + /// determine whether the returned text reflects the current state of the page + /// or the text last sent by the web server. The page source returned is a + /// representation of the underlying DOM: do not expect it to be formatted + /// or escaped in the same way as the response sent from the web server. + /// + string PageSource { get; } - /// - /// Gets the current window handle, which is an opaque handle to this - /// window that uniquely identifies it within this driver instance. - /// - string CurrentWindowHandle { get; } + /// + /// Gets the current window handle, which is an opaque handle to this + /// window that uniquely identifies it within this driver instance. + /// + string CurrentWindowHandle { get; } - /// - /// Gets the window handles of open browser windows. - /// - ReadOnlyCollection WindowHandles { get; } + /// + /// Gets the window handles of open browser windows. + /// + ReadOnlyCollection WindowHandles { get; } - /// - /// Close the current window, quitting the browser if it is the last window currently open. - /// - void Close(); + /// + /// Close the current window, quitting the browser if it is the last window currently open. + /// + void Close(); - /// - /// Quits this driver, closing every associated window. - /// - void Quit(); + /// + /// Quits this driver, closing every associated window. + /// + void Quit(); - /// - /// Instructs the driver to change its settings. - /// - /// An object allowing the user to change - /// the settings of the driver. - IOptions Manage(); + /// + /// Instructs the driver to change its settings. + /// + /// An object allowing the user to change + /// the settings of the driver. + IOptions Manage(); - /// - /// Instructs the driver to navigate the browser to another location. - /// - /// An object allowing the user to access - /// the browser's history and to navigate to a given URL. - INavigation Navigate(); + /// + /// Instructs the driver to navigate the browser to another location. + /// + /// An object allowing the user to access + /// the browser's history and to navigate to a given URL. + INavigation Navigate(); - /// - /// Instructs the driver to send future commands to a different frame or window. - /// - /// An object which can be used to select - /// a frame or window. - ITargetLocator SwitchTo(); - } + /// + /// Instructs the driver to send future commands to a different frame or window. + /// + /// An object which can be used to select + /// a frame or window. + ITargetLocator SwitchTo(); } diff --git a/dotnet/src/webdriver/IWebElement.cs b/dotnet/src/webdriver/IWebElement.cs index 7182d1c563940..b2a329f9dd7f8 100644 --- a/dotnet/src/webdriver/IWebElement.cs +++ b/dotnet/src/webdriver/IWebElement.cs @@ -20,200 +20,199 @@ using System; using System.Drawing; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user controls elements on the page. +/// +/// The interface represents an HTML element. +/// Generally, all interesting operations to do with interacting with a page will +/// be performed through this interface. +/// +public interface IWebElement : ISearchContext { /// - /// Defines the interface through which the user controls elements on the page. + /// Gets the tag name of this element. /// - /// The interface represents an HTML element. - /// Generally, all interesting operations to do with interacting with a page will - /// be performed through this interface. + /// + /// The property returns the tag name of the + /// element, not the value of the name attribute. For example, it will return + /// "input" for an element specified by the HTML markup <input name="foo" />. /// - public interface IWebElement : ISearchContext - { - /// - /// Gets the tag name of this element. - /// - /// - /// The property returns the tag name of the - /// element, not the value of the name attribute. For example, it will return - /// "input" for an element specified by the HTML markup <input name="foo" />. - /// - /// Thrown when the target element is no longer valid in the document DOM. - string TagName { get; } - - /// - /// Get the visible (i.e. not hidden by CSS) text of this element, including sub-elements. - /// - /// Thrown when the target element is no longer valid in the document DOM. - string Text { get; } - - /// - /// Gets a value indicating whether or not this element is enabled. - /// - /// The property will generally - /// return for everything except explicitly disabled input elements. - /// Thrown when the target element is no longer valid in the document DOM. - bool Enabled { get; } - - /// - /// Gets a value indicating whether or not this element is selected. - /// - /// This operation only applies to input elements such as checkboxes, - /// options in a select element and radio buttons. - /// Thrown when the target element is no longer valid in the document DOM. - bool Selected { get; } - - /// - /// Gets a object containing the coordinates of the upper-left corner - /// of this element relative to the upper-left corner of the page. - /// - /// Thrown when the target element is no longer valid in the document DOM. - Point Location { get; } - - /// - /// Gets a object containing the height and width of this element. - /// - /// Thrown when the target element is no longer valid in the document DOM. - Size Size { get; } - - /// - /// Gets a value indicating whether or not this element is displayed. - /// - /// The property avoids the problem - /// of having to parse an element's "style" attribute to determine - /// visibility of an element. - /// Thrown when the target element is no longer valid in the document DOM. - bool Displayed { get; } - - /// - /// Clears the content of this element. - /// - /// If this element is a text entry element, the - /// method will clear the value. It has no effect on other elements. Text entry elements - /// are defined as elements with INPUT or TEXTAREA tags. - /// Thrown when the target element is no longer valid in the document DOM. - void Clear(); - - /// - /// Simulates typing text into the element. - /// - /// The text to type into the element. - /// The text to be typed may include special characters like arrow keys, - /// backspaces, function keys, and so on. Valid special keys are defined in - /// . - /// - /// Thrown when the target element is not enabled. - /// Thrown when the target element is no longer valid in the document DOM. - void SendKeys(string text); - - /// - /// Submits this element to the web server. - /// - /// If this current element is a form, or an element within a form, - /// then this will be submitted to the web server. If this causes the current - /// page to change, then this method will block until the new page is loaded. - /// Thrown when the target element is no longer valid in the document DOM. - void Submit(); - - /// - /// Clicks this element. - /// - /// - /// - /// Click this element. If the click causes a new page to load, the - /// method will attempt to block until the page has loaded. After calling the - /// method, you should discard all references to this - /// element unless you know that the element and the page will still be present. - /// Otherwise, any further operations performed on this element will have an undefined. - /// behavior. - /// - /// - /// If this element is not clickable, then this operation is ignored. This allows you to - /// simulate a users to accidentally missing the target when clicking. - /// - /// - /// Thrown when the target element is no longer valid in the document DOM. - void Click(); - - /// - /// Gets the value of the specified attribute for this element. - /// - /// The name of the attribute. - /// The attribute's current value. Returns a if the - /// value is not set. - /// The method will return the current value - /// of the attribute, even if the value has been modified after the page has been - /// loaded. Note that the value of the following attributes will be returned even if - /// there is no explicit attribute on the element: - /// - /// - /// Attribute name - /// Value returned if not explicitly specified - /// Valid element types - /// - /// - /// checked - /// checked - /// Check Box - /// - /// - /// selected - /// selected - /// Options in Select elements - /// - /// - /// disabled - /// disabled - /// Input and other UI elements - /// - /// - /// - /// Thrown when the target element is no longer valid in the document DOM. - string? GetAttribute(string attributeName); - - /// - /// Gets the value of a declared HTML attribute of this element. - /// - /// The name of the HTML attribute to get the value of. - /// The HTML attribute's current value. Returns a if the - /// value is not set or the declared attribute does not exist. - /// Thrown when the target element is no longer valid in the document DOM. - /// - /// As opposed to the method, this method - /// only returns attributes declared in the element's HTML markup. To access the value - /// of an IDL property of the element, either use the - /// method or the method. - /// - string? GetDomAttribute(string attributeName); - - /// - /// Gets the value of a JavaScript property of this element. - /// - /// The name of the JavaScript property to get the value of. - /// The JavaScript property's current value. Returns a if the - /// value is not set or the property does not exist. - /// Thrown when the target element is no longer valid in the document DOM. - string? GetDomProperty(string propertyName); - - /// - /// Gets the value of a CSS property of this element. - /// - /// The name of the CSS property to get the value of. - /// The value of the specified CSS property. - /// The value returned by the - /// method is likely to be unpredictable in a cross-browser environment. - /// Color values should be returned as hex strings. For example, a - /// "background-color" property set as "green" in the HTML source, will - /// return "#008000" for its value. - /// Thrown when the target element is no longer valid in the document DOM. - string GetCssValue(string propertyName); - - /// - /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. - /// - /// Thrown when this element does not have a shadow root. - /// A shadow root representation. - ISearchContext GetShadowRoot(); - } + /// Thrown when the target element is no longer valid in the document DOM. + string TagName { get; } + + /// + /// Get the visible (i.e. not hidden by CSS) text of this element, including sub-elements. + /// + /// Thrown when the target element is no longer valid in the document DOM. + string Text { get; } + + /// + /// Gets a value indicating whether or not this element is enabled. + /// + /// The property will generally + /// return for everything except explicitly disabled input elements. + /// Thrown when the target element is no longer valid in the document DOM. + bool Enabled { get; } + + /// + /// Gets a value indicating whether or not this element is selected. + /// + /// This operation only applies to input elements such as checkboxes, + /// options in a select element and radio buttons. + /// Thrown when the target element is no longer valid in the document DOM. + bool Selected { get; } + + /// + /// Gets a object containing the coordinates of the upper-left corner + /// of this element relative to the upper-left corner of the page. + /// + /// Thrown when the target element is no longer valid in the document DOM. + Point Location { get; } + + /// + /// Gets a object containing the height and width of this element. + /// + /// Thrown when the target element is no longer valid in the document DOM. + Size Size { get; } + + /// + /// Gets a value indicating whether or not this element is displayed. + /// + /// The property avoids the problem + /// of having to parse an element's "style" attribute to determine + /// visibility of an element. + /// Thrown when the target element is no longer valid in the document DOM. + bool Displayed { get; } + + /// + /// Clears the content of this element. + /// + /// If this element is a text entry element, the + /// method will clear the value. It has no effect on other elements. Text entry elements + /// are defined as elements with INPUT or TEXTAREA tags. + /// Thrown when the target element is no longer valid in the document DOM. + void Clear(); + + /// + /// Simulates typing text into the element. + /// + /// The text to type into the element. + /// The text to be typed may include special characters like arrow keys, + /// backspaces, function keys, and so on. Valid special keys are defined in + /// . + /// + /// Thrown when the target element is not enabled. + /// Thrown when the target element is no longer valid in the document DOM. + void SendKeys(string text); + + /// + /// Submits this element to the web server. + /// + /// If this current element is a form, or an element within a form, + /// then this will be submitted to the web server. If this causes the current + /// page to change, then this method will block until the new page is loaded. + /// Thrown when the target element is no longer valid in the document DOM. + void Submit(); + + /// + /// Clicks this element. + /// + /// + /// + /// Click this element. If the click causes a new page to load, the + /// method will attempt to block until the page has loaded. After calling the + /// method, you should discard all references to this + /// element unless you know that the element and the page will still be present. + /// Otherwise, any further operations performed on this element will have an undefined. + /// behavior. + /// + /// + /// If this element is not clickable, then this operation is ignored. This allows you to + /// simulate a users to accidentally missing the target when clicking. + /// + /// + /// Thrown when the target element is no longer valid in the document DOM. + void Click(); + + /// + /// Gets the value of the specified attribute for this element. + /// + /// The name of the attribute. + /// The attribute's current value. Returns a if the + /// value is not set. + /// The method will return the current value + /// of the attribute, even if the value has been modified after the page has been + /// loaded. Note that the value of the following attributes will be returned even if + /// there is no explicit attribute on the element: + /// + /// + /// Attribute name + /// Value returned if not explicitly specified + /// Valid element types + /// + /// + /// checked + /// checked + /// Check Box + /// + /// + /// selected + /// selected + /// Options in Select elements + /// + /// + /// disabled + /// disabled + /// Input and other UI elements + /// + /// + /// + /// Thrown when the target element is no longer valid in the document DOM. + string? GetAttribute(string attributeName); + + /// + /// Gets the value of a declared HTML attribute of this element. + /// + /// The name of the HTML attribute to get the value of. + /// The HTML attribute's current value. Returns a if the + /// value is not set or the declared attribute does not exist. + /// Thrown when the target element is no longer valid in the document DOM. + /// + /// As opposed to the method, this method + /// only returns attributes declared in the element's HTML markup. To access the value + /// of an IDL property of the element, either use the + /// method or the method. + /// + string? GetDomAttribute(string attributeName); + + /// + /// Gets the value of a JavaScript property of this element. + /// + /// The name of the JavaScript property to get the value of. + /// The JavaScript property's current value. Returns a if the + /// value is not set or the property does not exist. + /// Thrown when the target element is no longer valid in the document DOM. + string? GetDomProperty(string propertyName); + + /// + /// Gets the value of a CSS property of this element. + /// + /// The name of the CSS property to get the value of. + /// The value of the specified CSS property. + /// The value returned by the + /// method is likely to be unpredictable in a cross-browser environment. + /// Color values should be returned as hex strings. For example, a + /// "background-color" property set as "green" in the HTML source, will + /// return "#008000" for its value. + /// Thrown when the target element is no longer valid in the document DOM. + string GetCssValue(string propertyName); + + /// + /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. + /// + /// Thrown when this element does not have a shadow root. + /// A shadow root representation. + ISearchContext GetShadowRoot(); } diff --git a/dotnet/src/webdriver/IWindow.cs b/dotnet/src/webdriver/IWindow.cs index c09ddd7341b22..13ba672484a17 100644 --- a/dotnet/src/webdriver/IWindow.cs +++ b/dotnet/src/webdriver/IWindow.cs @@ -19,38 +19,37 @@ using System.Drawing; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides methods for getting and setting the size and position of the browser window. +/// +public interface IWindow { /// - /// Provides methods for getting and setting the size and position of the browser window. + /// Gets or sets the position of the browser window relative to the upper-left corner of the screen. /// - public interface IWindow - { - /// - /// Gets or sets the position of the browser window relative to the upper-left corner of the screen. - /// - /// When setting this property, it should act as the JavaScript window.moveTo() method. - Point Position { get; set; } + /// When setting this property, it should act as the JavaScript window.moveTo() method. + Point Position { get; set; } - /// - /// Gets or sets the size of the outer browser window, including title bars and window borders. - /// - /// When setting this property, it should act as the JavaScript window.resizeTo() method. - Size Size { get; set; } + /// + /// Gets or sets the size of the outer browser window, including title bars and window borders. + /// + /// When setting this property, it should act as the JavaScript window.resizeTo() method. + Size Size { get; set; } - /// - /// Maximizes the current window if it is not already maximized. - /// - void Maximize(); + /// + /// Maximizes the current window if it is not already maximized. + /// + void Maximize(); - /// - /// Minimizes the current window if it is not already minimized. - /// - void Minimize(); + /// + /// Minimizes the current window if it is not already minimized. + /// + void Minimize(); - /// - /// Sets the current window to full screen if it is not already in that state. - /// - void FullScreen(); - } + /// + /// Sets the current window to full screen if it is not already in that state. + /// + void FullScreen(); } diff --git a/dotnet/src/webdriver/IWrapsDriver.cs b/dotnet/src/webdriver/IWrapsDriver.cs index 27af4c7e56faa..f9955ca3f2c82 100644 --- a/dotnet/src/webdriver/IWrapsDriver.cs +++ b/dotnet/src/webdriver/IWrapsDriver.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can access the driver used to find an element. +/// +public interface IWrapsDriver { /// - /// Defines the interface through which the user can access the driver used to find an element. + /// Gets the used to find this element. /// - public interface IWrapsDriver - { - /// - /// Gets the used to find this element. - /// - IWebDriver WrappedDriver { get; } - } + IWebDriver WrappedDriver { get; } } diff --git a/dotnet/src/webdriver/IWrapsElement.cs b/dotnet/src/webdriver/IWrapsElement.cs index 18e91139fd5ec..fcce8d5295908 100644 --- a/dotnet/src/webdriver/IWrapsElement.cs +++ b/dotnet/src/webdriver/IWrapsElement.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can discover if there is an underlying element to be used. +/// +public interface IWrapsElement { /// - /// Defines the interface through which the user can discover if there is an underlying element to be used. + /// Gets the wrapped by this object. /// - public interface IWrapsElement - { - /// - /// Gets the wrapped by this object. - /// - IWebElement WrappedElement { get; } - } + IWebElement WrappedElement { get; } } diff --git a/dotnet/src/webdriver/IWritableCapabilities.cs b/dotnet/src/webdriver/IWritableCapabilities.cs index e2ab8ddeaea61..f9a8614f55763 100644 --- a/dotnet/src/webdriver/IWritableCapabilities.cs +++ b/dotnet/src/webdriver/IWritableCapabilities.cs @@ -17,24 +17,23 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Capabilities of the browser that you are going to use +/// +public interface IWritableCapabilities : ICapabilities { /// - /// Capabilities of the browser that you are going to use + /// Sets a capability of the browser. /// - public interface IWritableCapabilities : ICapabilities - { - /// - /// Sets a capability of the browser. - /// - /// The capability to set. - /// The value for the capability. - void SetCapability(string capability, object capabilityValue); + /// The capability to set. + /// The value for the capability. + void SetCapability(string capability, object capabilityValue); - /// - /// Gets this capabilities object as a read-only object. - /// - /// The capabilities object as a read-only object. - ICapabilities AsReadOnly(); - } + /// + /// Gets this capabilities object as a read-only object. + /// + /// The capabilities object as a read-only object. + ICapabilities AsReadOnly(); } diff --git a/dotnet/src/webdriver/InitializationScript.cs b/dotnet/src/webdriver/InitializationScript.cs index 69b128c92f02a..189cf02b88888 100644 --- a/dotnet/src/webdriver/InitializationScript.cs +++ b/dotnet/src/webdriver/InitializationScript.cs @@ -20,64 +20,63 @@ using System; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents a JavaScript script that is loaded and run on every document load. +/// +public class InitializationScript { - /// - /// Represents a JavaScript script that is loaded and run on every document load. - /// - public class InitializationScript + internal InitializationScript(string scriptId, string scriptName, string scriptSource) { - internal InitializationScript(string scriptId, string scriptName, string scriptSource) - { - this.ScriptId = scriptId ?? throw new ArgumentNullException(nameof(scriptId)); - this.ScriptName = scriptName ?? throw new ArgumentNullException(nameof(scriptName)); - this.ScriptSource = scriptSource ?? throw new ArgumentNullException(nameof(scriptSource)); - } + this.ScriptId = scriptId ?? throw new ArgumentNullException(nameof(scriptId)); + this.ScriptName = scriptName ?? throw new ArgumentNullException(nameof(scriptName)); + this.ScriptSource = scriptSource ?? throw new ArgumentNullException(nameof(scriptSource)); + } - /// - /// Gets the internal ID of the initialization script. - /// - public string ScriptId { get; } + /// + /// Gets the internal ID of the initialization script. + /// + public string ScriptId { get; } - /// - /// Gets the friendly name of the initialization script. - /// - public string ScriptName { get; } + /// + /// Gets the friendly name of the initialization script. + /// + public string ScriptName { get; } - /// - /// Gets the JavaScript source of the initialization script. - /// - public string ScriptSource { get; } + /// + /// Gets the JavaScript source of the initialization script. + /// + public string ScriptSource { get; } - /// - /// Indicates whether the current is equal to another of the same type. - /// - /// An to compare with this . - /// if the current is equal to the other parameter; otherwise, . - public override bool Equals(object? obj) - { - return obj is InitializationScript other && this.ScriptId == other.ScriptId && this.ScriptName == other.ScriptName && this.ScriptSource == other.ScriptSource; - } + /// + /// Indicates whether the current is equal to another of the same type. + /// + /// An to compare with this . + /// if the current is equal to the other parameter; otherwise, . + public override bool Equals(object? obj) + { + return obj is InitializationScript other && this.ScriptId == other.ScriptId && this.ScriptName == other.ScriptName && this.ScriptSource == other.ScriptSource; + } - /// - /// Serves as a hash function for a particular . - /// - /// A hash code for the current . - public override int GetHashCode() - { - int result = this.ScriptId.GetHashCode(); - result = (31 * result) + this.ScriptName.GetHashCode(); - result = (31 * result) + this.ScriptSource.GetHashCode(); - return result; - } + /// + /// Serves as a hash function for a particular . + /// + /// A hash code for the current . + public override int GetHashCode() + { + int result = this.ScriptId.GetHashCode(); + result = (31 * result) + this.ScriptName.GetHashCode(); + result = (31 * result) + this.ScriptSource.GetHashCode(); + return result; + } - /// - /// Returns a string that represents the current object. - /// - /// A string that represents the current object. - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Initialization Script '{0}'\nInternal ID: {1}\nSource:{2}", this.ScriptName, this.ScriptId, this.ScriptSource); - } + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Initialization Script '{0}'\nInternal ID: {1}\nSource:{2}", this.ScriptName, this.ScriptId, this.ScriptSource); } } diff --git a/dotnet/src/webdriver/InsecureCertificateException.cs b/dotnet/src/webdriver/InsecureCertificateException.cs index 2d5e368a7e98f..62ff860e31271 100644 --- a/dotnet/src/webdriver/InsecureCertificateException.cs +++ b/dotnet/src/webdriver/InsecureCertificateException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an insecure certificate is used. +/// +[Serializable] +public class InsecureCertificateException : WebDriverException { /// - /// The exception that is thrown when an insecure certificate is used. + /// Initializes a new instance of the class. /// - [Serializable] - public class InsecureCertificateException : WebDriverException + public InsecureCertificateException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public InsecureCertificateException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public InsecureCertificateException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public InsecureCertificateException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public InsecureCertificateException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public InsecureCertificateException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/Interactions/ActionBuilder.cs b/dotnet/src/webdriver/Interactions/ActionBuilder.cs index 573cfa4986ec1..a10eda36654ee 100644 --- a/dotnet/src/webdriver/Interactions/ActionBuilder.cs +++ b/dotnet/src/webdriver/Interactions/ActionBuilder.cs @@ -21,118 +21,117 @@ using System.Collections.Generic; using System.Text; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Provides methods that allow the creation of action sequences to enable +/// advanced user interactions. +/// +public class ActionBuilder { + private readonly Dictionary sequences = new Dictionary(); + /// - /// Provides methods that allow the creation of action sequences to enable - /// advanced user interactions. + /// Adds an action to the built set of actions. Adding an action will + /// add a "tick" to the set of all actions to be executed. /// - public class ActionBuilder + /// The action to add to the set of actions + /// A self reference. + public ActionBuilder AddAction(Interaction actionToAdd) { - private readonly Dictionary sequences = new Dictionary(); - - /// - /// Adds an action to the built set of actions. Adding an action will - /// add a "tick" to the set of all actions to be executed. - /// - /// The action to add to the set of actions - /// A self reference. - public ActionBuilder AddAction(Interaction actionToAdd) - { - this.AddActions(actionToAdd); - return this; - } + this.AddActions(actionToAdd); + return this; + } - /// - /// Adds an action to the built set of actions. Adding an action will - /// add a "tick" to the set of all actions to be executed. Only one action - /// for each input device may be added for a single tick. - /// - /// The set actions to add to the existing set of actions. - /// A self reference. - public ActionBuilder AddActions(params Interaction[] actionsToAdd) - { - this.ProcessTick(actionsToAdd); - return this; - } + /// + /// Adds an action to the built set of actions. Adding an action will + /// add a "tick" to the set of all actions to be executed. Only one action + /// for each input device may be added for a single tick. + /// + /// The set actions to add to the existing set of actions. + /// A self reference. + public ActionBuilder AddActions(params Interaction[] actionsToAdd) + { + this.ProcessTick(actionsToAdd); + return this; + } - /// - /// Converts the set of actions in this to a . - /// - /// A suitable for transmission across the wire. - /// The collection returned is read-only. - public IList ToActionSequenceList() - { - return new List(this.sequences.Values).AsReadOnly(); - } + /// + /// Converts the set of actions in this to a . + /// + /// A suitable for transmission across the wire. + /// The collection returned is read-only. + public IList ToActionSequenceList() + { + return new List(this.sequences.Values).AsReadOnly(); + } + + /// + /// Resets the list of sequences. + /// + public void ClearSequences() + { + this.sequences.Clear(); + } - /// - /// Resets the list of sequences. - /// - public void ClearSequences() + /// + /// Returns a string that represents the current . + /// + /// A string that represents the current . + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + foreach (ActionSequence sequence in this.sequences.Values) { - this.sequences.Clear(); + builder.AppendLine(sequence.ToString()); } - /// - /// Returns a string that represents the current . - /// - /// A string that represents the current . - public override string ToString() + return builder.ToString(); + } + + private void ProcessTick(params Interaction[] interactionsToAdd) + { + List usedDevices = new List(); + foreach (Interaction interaction in interactionsToAdd) { - StringBuilder builder = new StringBuilder(); - foreach (ActionSequence sequence in this.sequences.Values) + if (usedDevices.Contains(interaction.SourceDevice)) { - builder.AppendLine(sequence.ToString()); + throw new ArgumentException("You can only add one action per device for a single tick."); } - - return builder.ToString(); } - private void ProcessTick(params Interaction[] interactionsToAdd) + List unusedDevices = new List(this.sequences.Keys); + foreach (Interaction interaction in interactionsToAdd) { - List usedDevices = new List(); - foreach (Interaction interaction in interactionsToAdd) - { - if (usedDevices.Contains(interaction.SourceDevice)) - { - throw new ArgumentException("You can only add one action per device for a single tick."); - } - } - - List unusedDevices = new List(this.sequences.Keys); - foreach (Interaction interaction in interactionsToAdd) - { - ActionSequence sequence = this.GetOrAddSequence(interaction.SourceDevice); - sequence.AddAction(interaction); + ActionSequence sequence = this.GetOrAddSequence(interaction.SourceDevice); + sequence.AddAction(interaction); - unusedDevices.Remove(interaction.SourceDevice); - } + unusedDevices.Remove(interaction.SourceDevice); + } - foreach (InputDevice unusedDevice in unusedDevices) - { - ActionSequence sequence = this.sequences[unusedDevice]; - sequence.AddAction(new PauseInteraction(unusedDevice, TimeSpan.Zero)); - } + foreach (InputDevice unusedDevice in unusedDevices) + { + ActionSequence sequence = this.sequences[unusedDevice]; + sequence.AddAction(new PauseInteraction(unusedDevice, TimeSpan.Zero)); } + } - private ActionSequence GetOrAddSequence(InputDevice device) + private ActionSequence GetOrAddSequence(InputDevice device) + { + if (this.sequences.TryGetValue(device, out ActionSequence? existingSequence)) { - if (this.sequences.TryGetValue(device, out ActionSequence? existingSequence)) - { - return existingSequence; - } + return existingSequence; + } - int longestSequenceLength = 0; - foreach (KeyValuePair pair in this.sequences) - { - longestSequenceLength = Math.Max(longestSequenceLength, pair.Value.Count); - } + int longestSequenceLength = 0; + foreach (KeyValuePair pair in this.sequences) + { + longestSequenceLength = Math.Max(longestSequenceLength, pair.Value.Count); + } - ActionSequence sequence = new ActionSequence(device, longestSequenceLength); - this.sequences[device] = sequence; + ActionSequence sequence = new ActionSequence(device, longestSequenceLength); + this.sequences[device] = sequence; - return sequence; - } + return sequence; } } diff --git a/dotnet/src/webdriver/Interactions/ActionSequence.cs b/dotnet/src/webdriver/Interactions/ActionSequence.cs index e7c959bc2ad06..564c1329d9e38 100644 --- a/dotnet/src/webdriver/Interactions/ActionSequence.cs +++ b/dotnet/src/webdriver/Interactions/ActionSequence.cs @@ -22,110 +22,109 @@ using System.Globalization; using System.Text; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents a sequence of actions to be performed in the target browser. +/// +public class ActionSequence { + private readonly List interactions = new List(); + /// - /// Represents a sequence of actions to be performed in the target browser. + /// Initializes a new instance of the class. /// - public class ActionSequence + /// The input device that executes this sequence of actions. + public ActionSequence(InputDevice device) + : this(device, 0) { - private readonly List interactions = new List(); - - /// - /// Initializes a new instance of the class. - /// - /// The input device that executes this sequence of actions. - public ActionSequence(InputDevice device) - : this(device, 0) + } + + /// + /// Initializes a new instance of the class. + /// + /// The input device that executes this sequence of actions. + /// the initial size of the sequence. + public ActionSequence(InputDevice device, int initialSize) + { + this.InputDevice = device ?? throw new ArgumentNullException(nameof(device), "Input device cannot be null."); + + for (int i = 0; i < initialSize; i++) { + this.AddAction(new PauseInteraction(this.InputDevice, TimeSpan.Zero)); } + } - /// - /// Initializes a new instance of the class. - /// - /// The input device that executes this sequence of actions. - /// the initial size of the sequence. - public ActionSequence(InputDevice device, int initialSize) - { - this.InputDevice = device ?? throw new ArgumentNullException(nameof(device), "Input device cannot be null."); + /// + /// Gets the count of actions in the sequence. + /// + public int Count => this.interactions.Count; - for (int i = 0; i < initialSize; i++) - { - this.AddAction(new PauseInteraction(this.InputDevice, TimeSpan.Zero)); - } - } + /// + /// Gets the input device for this Action sequence. + /// + [Obsolete("This property has been renamed to InputDevice and will be removed in a future version")] + [CLSCompliant(false)] + public InputDevice inputDevice => InputDevice; + + /// + /// Gets the input device for this Action sequence. + /// + public InputDevice InputDevice { get; } - /// - /// Gets the count of actions in the sequence. - /// - public int Count => this.interactions.Count; - - /// - /// Gets the input device for this Action sequence. - /// - [Obsolete("This property has been renamed to InputDevice and will be removed in a future version")] - [CLSCompliant(false)] - public InputDevice inputDevice => InputDevice; - - /// - /// Gets the input device for this Action sequence. - /// - public InputDevice InputDevice { get; } - - /// - /// Adds an action to the sequence. - /// - /// The action to add to the sequence. - /// A self-reference to this sequence of actions. - public ActionSequence AddAction(Interaction interactionToAdd) + /// + /// Adds an action to the sequence. + /// + /// The action to add to the sequence. + /// A self-reference to this sequence of actions. + public ActionSequence AddAction(Interaction interactionToAdd) + { + if (interactionToAdd == null) { - if (interactionToAdd == null) - { - throw new ArgumentNullException(nameof(interactionToAdd), "Interaction to add to sequence must not be null"); - } - - if (!interactionToAdd.IsValidFor(this.InputDevice.DeviceKind)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Interaction {0} is invalid for device type {1}.", interactionToAdd.GetType(), this.InputDevice.DeviceKind), nameof(interactionToAdd)); - } - - this.interactions.Add(interactionToAdd); - return this; + throw new ArgumentNullException(nameof(interactionToAdd), "Interaction to add to sequence must not be null"); } - /// - /// Converts this action sequence into an object suitable for serializing across the wire. - /// - /// A containing the actions in this sequence. - public Dictionary ToDictionary() + if (!interactionToAdd.IsValidFor(this.InputDevice.DeviceKind)) { - Dictionary toReturn = this.InputDevice.ToDictionary(); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Interaction {0} is invalid for device type {1}.", interactionToAdd.GetType(), this.InputDevice.DeviceKind), nameof(interactionToAdd)); + } - List encodedActions = new List(); - foreach (Interaction action in this.interactions) - { - encodedActions.Add(action.ToDictionary()); - } + this.interactions.Add(interactionToAdd); + return this; + } - toReturn["actions"] = encodedActions; + /// + /// Converts this action sequence into an object suitable for serializing across the wire. + /// + /// A containing the actions in this sequence. + public Dictionary ToDictionary() + { + Dictionary toReturn = this.InputDevice.ToDictionary(); - return toReturn; + List encodedActions = new List(); + foreach (Interaction action in this.interactions) + { + encodedActions.Add(action.ToDictionary()); } - /// - /// Returns a string that represents the current . - /// - /// A string that represents the current . - public override string ToString() + toReturn["actions"] = encodedActions; + + return toReturn; + } + + /// + /// Returns a string that represents the current . + /// + /// A string that represents the current . + public override string ToString() + { + StringBuilder builder = new StringBuilder("Action sequence - ").Append(this.InputDevice.ToString()); + foreach (Interaction action in this.interactions) { - StringBuilder builder = new StringBuilder("Action sequence - ").Append(this.InputDevice.ToString()); - foreach (Interaction action in this.interactions) - { - builder.AppendLine(); - builder.AppendFormat(" {0}", action.ToString()); - } - - return builder.ToString(); + builder.AppendLine(); + builder.AppendFormat(" {0}", action.ToString()); } + + return builder.ToString(); } } diff --git a/dotnet/src/webdriver/Interactions/Actions.cs b/dotnet/src/webdriver/Interactions/Actions.cs index 51bca4d962653..1018a8be10c73 100644 --- a/dotnet/src/webdriver/Interactions/Actions.cs +++ b/dotnet/src/webdriver/Interactions/Actions.cs @@ -21,656 +21,655 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Provides a mechanism for building advanced interactions with the browser. +/// +public class Actions : IAction { + private readonly TimeSpan duration; + private ActionBuilder actionBuilder = new ActionBuilder(); + private PointerInputDevice? activePointer; + private KeyInputDevice? activeKeyboard; + private WheelInputDevice? activeWheel; + /// - /// Provides a mechanism for building advanced interactions with the browser. + /// Initializes a new instance of the class. /// - public class Actions : IAction + /// The object on which the actions built will be performed. + /// If does not implement . + public Actions(IWebDriver driver) + : this(driver, TimeSpan.FromMilliseconds(250)) { - private readonly TimeSpan duration; - private ActionBuilder actionBuilder = new ActionBuilder(); - private PointerInputDevice? activePointer; - private KeyInputDevice? activeKeyboard; - private WheelInputDevice? activeWheel; - - /// - /// Initializes a new instance of the class. - /// - /// The object on which the actions built will be performed. - /// If does not implement . - public Actions(IWebDriver driver) - : this(driver, TimeSpan.FromMilliseconds(250)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The object on which the actions built will be performed. - /// How long durable action is expected to take. - /// If does not implement . - public Actions(IWebDriver driver, TimeSpan duration) - { - IActionExecutor actionExecutor = GetDriverAs(driver) - ?? throw new ArgumentException("The IWebDriver object must implement or wrap a driver that implements IActionExecutor.", nameof(driver)); + } - this.ActionExecutor = actionExecutor; - this.duration = duration; - } + /// + /// Initializes a new instance of the class. + /// + /// The object on which the actions built will be performed. + /// How long durable action is expected to take. + /// If does not implement . + public Actions(IWebDriver driver, TimeSpan duration) + { + IActionExecutor actionExecutor = GetDriverAs(driver) + ?? throw new ArgumentException("The IWebDriver object must implement or wrap a driver that implements IActionExecutor.", nameof(driver)); - /// - /// Returns the for the driver. - /// - protected IActionExecutor ActionExecutor { get; } - - /// - /// Sets the active pointer device for this Actions class. - /// - /// The kind of pointer device to set as active. - /// The name of the pointer device to set as active. - /// A self-reference to this Actions class. - /// If a device with this name exists but is not a pointer. - [MemberNotNull(nameof(activePointer))] - public Actions SetActivePointer(PointerKind kind, string name) - { - InputDevice? device = FindDeviceById(name); + this.ActionExecutor = actionExecutor; + this.duration = duration; + } - this.activePointer = device switch - { - null => new PointerInputDevice(kind, name), - PointerInputDevice pointerDevice => pointerDevice, - _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a pointer. Actual input type: {device.DeviceKind}"), - }; + /// + /// Returns the for the driver. + /// + protected IActionExecutor ActionExecutor { get; } - return this; - } + /// + /// Sets the active pointer device for this Actions class. + /// + /// The kind of pointer device to set as active. + /// The name of the pointer device to set as active. + /// A self-reference to this Actions class. + /// If a device with this name exists but is not a pointer. + [MemberNotNull(nameof(activePointer))] + public Actions SetActivePointer(PointerKind kind, string name) + { + InputDevice? device = FindDeviceById(name); - /// - /// Sets the active keyboard device for this Actions class. - /// - /// The name of the keyboard device to set as active. - /// A self-reference to this Actions class. - /// If a device with this name exists but is not a keyboard. - [MemberNotNull(nameof(activeKeyboard))] - public Actions SetActiveKeyboard(string name) + this.activePointer = device switch { - InputDevice? device = FindDeviceById(name); + null => new PointerInputDevice(kind, name), + PointerInputDevice pointerDevice => pointerDevice, + _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a pointer. Actual input type: {device.DeviceKind}"), + }; - this.activeKeyboard = device switch - { - null => new KeyInputDevice(name), - KeyInputDevice keyDevice => keyDevice, - _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a keyboard. Actual input type: {device.DeviceKind}"), - }; + return this; + } - return this; - } + /// + /// Sets the active keyboard device for this Actions class. + /// + /// The name of the keyboard device to set as active. + /// A self-reference to this Actions class. + /// If a device with this name exists but is not a keyboard. + [MemberNotNull(nameof(activeKeyboard))] + public Actions SetActiveKeyboard(string name) + { + InputDevice? device = FindDeviceById(name); - /// - /// Sets the active wheel device for this Actions class. - /// - /// The name of the wheel device to set as active. - /// A self-reference to this Actions class. - /// If a device with this name exists but is not a wheel. - [MemberNotNull(nameof(activeWheel))] - public Actions SetActiveWheel(string name) + this.activeKeyboard = device switch { - InputDevice? device = FindDeviceById(name); + null => new KeyInputDevice(name), + KeyInputDevice keyDevice => keyDevice, + _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a keyboard. Actual input type: {device.DeviceKind}"), + }; - this.activeWheel = device switch - { - null => new WheelInputDevice(name), - WheelInputDevice wheelDevice => wheelDevice, - _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a wheel. Actual input type: {device.DeviceKind}"), - }; + return this; + } - return this; - } + /// + /// Sets the active wheel device for this Actions class. + /// + /// The name of the wheel device to set as active. + /// A self-reference to this Actions class. + /// If a device with this name exists but is not a wheel. + [MemberNotNull(nameof(activeWheel))] + public Actions SetActiveWheel(string name) + { + InputDevice? device = FindDeviceById(name); - private InputDevice? FindDeviceById(string? name) + this.activeWheel = device switch { - foreach (var sequence in this.actionBuilder.ToActionSequenceList()) - { - Dictionary actions = sequence.ToDictionary(); - - string id = (string)actions["id"]; - - if (id == name) - { - return sequence.InputDevice; - } - } + null => new WheelInputDevice(name), + WheelInputDevice wheelDevice => wheelDevice, + _ => throw new InvalidOperationException($"Device under the name \"{name}\" is not a wheel. Actual input type: {device.DeviceKind}"), + }; - return null; - } + return this; + } - /// - /// Gets the active pointer device for this Actions class. - /// - /// The active pointer device for this Actions class. - public PointerInputDevice GetActivePointer() + private InputDevice? FindDeviceById(string? name) + { + foreach (var sequence in this.actionBuilder.ToActionSequenceList()) { - if (this.activePointer == null) - { - SetActivePointer(PointerKind.Mouse, "default mouse"); - } + Dictionary actions = sequence.ToDictionary(); - return this.activePointer; - } + string id = (string)actions["id"]; - /// - /// Gets the active keyboard device for this Actions class. - /// - /// The active keyboard device for this Actions class. - public KeyInputDevice GetActiveKeyboard() - { - if (this.activeKeyboard == null) + if (id == name) { - SetActiveKeyboard("default keyboard"); + return sequence.InputDevice; } - - return this.activeKeyboard; } - /// - /// Gets the active wheel device for this Actions class. - /// - /// The active wheel device for this Actions class. - public WheelInputDevice GetActiveWheel() - { - if (this.activeWheel == null) - { - SetActiveWheel("default wheel"); - } - - return this.activeWheel; - } + return null; + } - /// - /// Sends a modifier key down message to the browser. - /// - /// The key to be sent. - /// A self-reference to this . - /// If the key sent is not is not one - /// of , , , - /// , ,, - /// ,. - public Actions KeyDown(string theKey) + /// + /// Gets the active pointer device for this Actions class. + /// + /// The active pointer device for this Actions class. + public PointerInputDevice GetActivePointer() + { + if (this.activePointer == null) { - return this.KeyDown(null, theKey); + SetActivePointer(PointerKind.Mouse, "default mouse"); } - /// - /// Sends a modifier key down message to the specified element in the browser. - /// - /// The element to which to send the key command. - /// The key to be sent. - /// A self-reference to this . - /// If the key sent is not is not one - /// of , , , - /// , ,, - /// ,. - public Actions KeyDown(IWebElement? element, string theKey) - { - if (string.IsNullOrEmpty(theKey)) - { - throw new ArgumentException("The key value must not be null or empty", nameof(theKey)); - } - - ILocatable? target = GetLocatableFromElement(element); - if (element != null) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - } - - this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyDown(theKey[0])); - this.actionBuilder.AddAction(new PauseInteraction(this.GetActiveKeyboard(), TimeSpan.FromMilliseconds(100))); - return this; - } + return this.activePointer; + } - /// - /// Sends a modifier key up message to the browser. - /// - /// The key to be sent. - /// A self-reference to this . - /// If the key sent is not is not one - /// of , , , - /// , ,, - /// ,. - public Actions KeyUp(string theKey) + /// + /// Gets the active keyboard device for this Actions class. + /// + /// The active keyboard device for this Actions class. + public KeyInputDevice GetActiveKeyboard() + { + if (this.activeKeyboard == null) { - return this.KeyUp(null, theKey); + SetActiveKeyboard("default keyboard"); } - /// - /// Sends a modifier up down message to the specified element in the browser. - /// - /// The element to which to send the key command. - /// The key to be sent. - /// A self-reference to this . - /// If the key sent is not is not one - /// of , , , - /// , ,, - /// ,. - public Actions KeyUp(IWebElement? element, string theKey) - { - if (string.IsNullOrEmpty(theKey)) - { - throw new ArgumentException("The key value must not be null or empty", nameof(theKey)); - } - - ILocatable? target = GetLocatableFromElement(element); - if (element != null) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - } - - this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyUp(theKey[0])); - return this; - } + return this.activeKeyboard; + } - /// - /// Sends a sequence of keystrokes to the browser. - /// - /// The keystrokes to send to the browser. - /// A self-reference to this . - /// If is or . - public Actions SendKeys(string keysToSend) + /// + /// Gets the active wheel device for this Actions class. + /// + /// The active wheel device for this Actions class. + public WheelInputDevice GetActiveWheel() + { + if (this.activeWheel == null) { - return this.SendKeys(null, keysToSend); + SetActiveWheel("default wheel"); } - /// - /// Sends a sequence of keystrokes to the specified element in the browser. - /// - /// The element to which to send the keystrokes. - /// The keystrokes to send to the browser. - /// A self-reference to this . - /// If is or . - public Actions SendKeys(IWebElement? element, string keysToSend) - { - if (string.IsNullOrEmpty(keysToSend)) - { - throw new ArgumentException("The key value must not be null or empty", nameof(keysToSend)); - } - - ILocatable? target = GetLocatableFromElement(element); - if (element != null) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - } - - foreach (char key in keysToSend) - { - this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyDown(key)); - this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyUp(key)); - } + return this.activeWheel; + } - return this; - } + /// + /// Sends a modifier key down message to the browser. + /// + /// The key to be sent. + /// A self-reference to this . + /// If the key sent is not is not one + /// of , , , + /// , ,, + /// ,. + public Actions KeyDown(string theKey) + { + return this.KeyDown(null, theKey); + } - /// - /// Clicks and holds the mouse button down on the specified element. - /// - /// The element on which to click and hold. - /// A self-reference to this . - /// If is null. - public Actions ClickAndHold(IWebElement onElement) + /// + /// Sends a modifier key down message to the specified element in the browser. + /// + /// The element to which to send the key command. + /// The key to be sent. + /// A self-reference to this . + /// If the key sent is not is not one + /// of , , , + /// , ,, + /// ,. + public Actions KeyDown(IWebElement? element, string theKey) + { + if (string.IsNullOrEmpty(theKey)) { - this.MoveToElement(onElement).ClickAndHold(); - return this; + throw new ArgumentException("The key value must not be null or empty", nameof(theKey)); } - /// - /// Clicks and holds the mouse button at the last known mouse coordinates. - /// - /// A self-reference to this . - public Actions ClickAndHold() + ILocatable? target = GetLocatableFromElement(element); + if (element != null) { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); - return this; + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); } - /// - /// Releases the mouse button on the specified element. - /// - /// The element on which to release the button. - /// A self-reference to this . - /// If is null. - public Actions Release(IWebElement onElement) - { - this.MoveToElement(onElement).Release(); - return this; - } + this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyDown(theKey[0])); + this.actionBuilder.AddAction(new PauseInteraction(this.GetActiveKeyboard(), TimeSpan.FromMilliseconds(100))); + return this; + } - /// - /// Releases the mouse button at the last known mouse coordinates. - /// - /// A self-reference to this . - public Actions Release() - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - return this; - } + /// + /// Sends a modifier key up message to the browser. + /// + /// The key to be sent. + /// A self-reference to this . + /// If the key sent is not is not one + /// of , , , + /// , ,, + /// ,. + public Actions KeyUp(string theKey) + { + return this.KeyUp(null, theKey); + } - /// - /// Clicks the mouse on the specified element. - /// - /// The element on which to click. - /// A self-reference to this . - /// If is null. - public Actions Click(IWebElement onElement) + /// + /// Sends a modifier up down message to the specified element in the browser. + /// + /// The element to which to send the key command. + /// The key to be sent. + /// A self-reference to this . + /// If the key sent is not is not one + /// of , , , + /// , ,, + /// ,. + public Actions KeyUp(IWebElement? element, string theKey) + { + if (string.IsNullOrEmpty(theKey)) { - this.MoveToElement(onElement).Click(); - return this; + throw new ArgumentException("The key value must not be null or empty", nameof(theKey)); } - /// - /// Clicks the mouse at the last known mouse coordinates. - /// - /// A self-reference to this . - public Actions Click() + ILocatable? target = GetLocatableFromElement(element); + if (element != null) { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - return this; } - /// - /// Double-clicks the mouse on the specified element. - /// - /// The element on which to double-click. - /// A self-reference to this . - /// If is null. - public Actions DoubleClick(IWebElement onElement) + this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyUp(theKey[0])); + return this; + } + + /// + /// Sends a sequence of keystrokes to the browser. + /// + /// The keystrokes to send to the browser. + /// A self-reference to this . + /// If is or . + public Actions SendKeys(string keysToSend) + { + return this.SendKeys(null, keysToSend); + } + + /// + /// Sends a sequence of keystrokes to the specified element in the browser. + /// + /// The element to which to send the keystrokes. + /// The keystrokes to send to the browser. + /// A self-reference to this . + /// If is or . + public Actions SendKeys(IWebElement? element, string keysToSend) + { + if (string.IsNullOrEmpty(keysToSend)) { - this.MoveToElement(onElement).DoubleClick(); - return this; + throw new ArgumentException("The key value must not be null or empty", nameof(keysToSend)); } - /// - /// Double-clicks the mouse at the last known mouse coordinates. - /// - /// A self-reference to this . - public Actions DoubleClick() + ILocatable? target = GetLocatableFromElement(element); + if (element != null) { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(element, 0, 0, duration)); this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); - return this; } - /// - /// Moves the mouse to the specified element. - /// - /// The element to which to move the mouse. - /// A self-reference to this . - /// If is null. - public Actions MoveToElement(IWebElement toElement) + foreach (char key in keysToSend) { - if (toElement == null) - { - throw new ArgumentException("MoveToElement cannot move to a null element with no offset.", nameof(toElement)); - } - - return this.MoveToElement(toElement, 0, 0); + this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyDown(key)); + this.actionBuilder.AddAction(this.GetActiveKeyboard().CreateKeyUp(key)); } - /// - /// Moves the mouse to the specified offset of the top-left corner of the specified element. - /// In Selenium 4.3 the origin for the offset will be the in-view center point of the element. - /// - /// The element to which to move the mouse. - /// The horizontal offset to which to move the mouse. - /// The vertical offset to which to move the mouse. - /// A self-reference to this . - public Actions MoveToElement(IWebElement toElement, int offsetX, int offsetY) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(toElement, offsetX, offsetY, duration)); - return this; - } + return this; + } - /// - /// Moves the mouse to the specified offset of the last known mouse coordinates. - /// - /// The horizontal offset to which to move the mouse. - /// The vertical offset to which to move the mouse. - /// A self-reference to this . - public Actions MoveByOffset(int offsetX, int offsetY) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(CoordinateOrigin.Pointer, offsetX, offsetY, duration)); - return this; - } + /// + /// Clicks and holds the mouse button down on the specified element. + /// + /// The element on which to click and hold. + /// A self-reference to this . + /// If is null. + public Actions ClickAndHold(IWebElement onElement) + { + this.MoveToElement(onElement).ClickAndHold(); + return this; + } - /// - /// Moves the mouse from the upper left corner of the current viewport by the provided offset. - /// - /// The horizontal offset to which to move the mouse. - /// The vertical offset to which to move the mouse. - /// A self-reference to this . - public Actions MoveToLocation(int offsetX, int offsetY) - { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(CoordinateOrigin.Viewport, offsetX, offsetY, duration)); - return this; - } + /// + /// Clicks and holds the mouse button at the last known mouse coordinates. + /// + /// A self-reference to this . + public Actions ClickAndHold() + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); + return this; + } - /// - /// Right-clicks the mouse on the specified element. - /// - /// The element on which to right-click. - /// A self-reference to this . - /// If is null. - public Actions ContextClick(IWebElement onElement) - { - this.MoveToElement(onElement).ContextClick(); - return this; - } + /// + /// Releases the mouse button on the specified element. + /// + /// The element on which to release the button. + /// A self-reference to this . + /// If is null. + public Actions Release(IWebElement onElement) + { + this.MoveToElement(onElement).Release(); + return this; + } + + /// + /// Releases the mouse button at the last known mouse coordinates. + /// + /// A self-reference to this . + public Actions Release() + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); + return this; + } + + /// + /// Clicks the mouse on the specified element. + /// + /// The element on which to click. + /// A self-reference to this . + /// If is null. + public Actions Click(IWebElement onElement) + { + this.MoveToElement(onElement).Click(); + return this; + } + + /// + /// Clicks the mouse at the last known mouse coordinates. + /// + /// A self-reference to this . + public Actions Click() + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); + return this; + } + + /// + /// Double-clicks the mouse on the specified element. + /// + /// The element on which to double-click. + /// A self-reference to this . + /// If is null. + public Actions DoubleClick(IWebElement onElement) + { + this.MoveToElement(onElement).DoubleClick(); + return this; + } + + /// + /// Double-clicks the mouse at the last known mouse coordinates. + /// + /// A self-reference to this . + public Actions DoubleClick() + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Left)); + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Left)); + return this; + } - /// - /// Right-clicks the mouse at the last known mouse coordinates. - /// - /// A self-reference to this . - public Actions ContextClick() + /// + /// Moves the mouse to the specified element. + /// + /// The element to which to move the mouse. + /// A self-reference to this . + /// If is null. + public Actions MoveToElement(IWebElement toElement) + { + if (toElement == null) { - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Right)); - this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Right)); - return this; + throw new ArgumentException("MoveToElement cannot move to a null element with no offset.", nameof(toElement)); } - /// - /// Performs a drag-and-drop operation from one element to another. - /// - /// The element on which the drag operation is started. - /// The element on which the drop is performed. - /// A self-reference to this . - /// If or are null. - public Actions DragAndDrop(IWebElement source, IWebElement target) + return this.MoveToElement(toElement, 0, 0); + } + + /// + /// Moves the mouse to the specified offset of the top-left corner of the specified element. + /// In Selenium 4.3 the origin for the offset will be the in-view center point of the element. + /// + /// The element to which to move the mouse. + /// The horizontal offset to which to move the mouse. + /// The vertical offset to which to move the mouse. + /// A self-reference to this . + public Actions MoveToElement(IWebElement toElement, int offsetX, int offsetY) + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(toElement, offsetX, offsetY, duration)); + return this; + } + + /// + /// Moves the mouse to the specified offset of the last known mouse coordinates. + /// + /// The horizontal offset to which to move the mouse. + /// The vertical offset to which to move the mouse. + /// A self-reference to this . + public Actions MoveByOffset(int offsetX, int offsetY) + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(CoordinateOrigin.Pointer, offsetX, offsetY, duration)); + return this; + } + + /// + /// Moves the mouse from the upper left corner of the current viewport by the provided offset. + /// + /// The horizontal offset to which to move the mouse. + /// The vertical offset to which to move the mouse. + /// A self-reference to this . + public Actions MoveToLocation(int offsetX, int offsetY) + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerMove(CoordinateOrigin.Viewport, offsetX, offsetY, duration)); + return this; + } + + /// + /// Right-clicks the mouse on the specified element. + /// + /// The element on which to right-click. + /// A self-reference to this . + /// If is null. + public Actions ContextClick(IWebElement onElement) + { + this.MoveToElement(onElement).ContextClick(); + return this; + } + + /// + /// Right-clicks the mouse at the last known mouse coordinates. + /// + /// A self-reference to this . + public Actions ContextClick() + { + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerDown(MouseButton.Right)); + this.actionBuilder.AddAction(this.GetActivePointer().CreatePointerUp(MouseButton.Right)); + return this; + } + + /// + /// Performs a drag-and-drop operation from one element to another. + /// + /// The element on which the drag operation is started. + /// The element on which the drop is performed. + /// A self-reference to this . + /// If or are null. + public Actions DragAndDrop(IWebElement source, IWebElement target) + { + this.ClickAndHold(source).MoveToElement(target).Release(target); + return this; + } + + /// + /// Performs a drag-and-drop operation on one element to a specified offset. + /// + /// The element on which the drag operation is started. + /// The horizontal offset to which to move the mouse. + /// The vertical offset to which to move the mouse. + /// A self-reference to this . + /// If is null. + public Actions DragAndDropToOffset(IWebElement source, int offsetX, int offsetY) + { + this.ClickAndHold(source).MoveByOffset(offsetX, offsetY).Release(); + return this; + } + + /// + /// If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport. + /// + /// Which element to scroll into the viewport. + /// A self-reference to this . + /// If is null. + public Actions ScrollToElement(IWebElement element) + { + this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(element, 0, 0, 0, 0, duration)); + + return this; + } + + /// + /// Scrolls by provided amounts with the origin in the top left corner of the viewport. + /// + /// Distance along X axis to scroll using the wheel. A negative value scrolls left. + /// Distance along Y axis to scroll using the wheel. A negative value scrolls up. + /// A self-reference to this . + public Actions ScrollByAmount(int deltaX, int deltaY) + { + this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(deltaX, deltaY, duration)); + + return this; + } + + /// + /// Scrolls by provided amount based on a provided origin. + /// + /// + /// The scroll origin is either the center of an element or the upper left of the viewport plus any offsets. + /// If the origin is an element, and the element is not in the viewport, the bottom of the element will first + /// be scrolled to the bottom of the viewport. + /// + /// Where scroll originates (viewport or element center) plus provided offsets. + /// Distance along X axis to scroll using the wheel. A negative value scrolls left. + /// Distance along Y axis to scroll using the wheel. A negative value scrolls up. + /// A self-reference to this . + /// If the origin with offset is outside the viewport. + /// If is null. + /// If both or either of Viewport and Element are set. + public Actions ScrollFromOrigin(WheelInputDevice.ScrollOrigin scrollOrigin, int deltaX, int deltaY) + { + if (scrollOrigin is null) { - this.ClickAndHold(source).MoveToElement(target).Release(target); - return this; + throw new ArgumentNullException(nameof(scrollOrigin)); } - /// - /// Performs a drag-and-drop operation on one element to a specified offset. - /// - /// The element on which the drag operation is started. - /// The horizontal offset to which to move the mouse. - /// The vertical offset to which to move the mouse. - /// A self-reference to this . - /// If is null. - public Actions DragAndDropToOffset(IWebElement source, int offsetX, int offsetY) + if (scrollOrigin.Viewport && scrollOrigin.Element != null) { - this.ClickAndHold(source).MoveByOffset(offsetX, offsetY).Release(); - return this; + throw new ArgumentException("viewport can not be true if an element is defined.", nameof(scrollOrigin)); } - /// - /// If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport. - /// - /// Which element to scroll into the viewport. - /// A self-reference to this . - /// If is null. - public Actions ScrollToElement(IWebElement element) + if (scrollOrigin.Viewport) { - this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(element, 0, 0, 0, 0, duration)); - - return this; + this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(CoordinateOrigin.Viewport, + scrollOrigin.XOffset, scrollOrigin.YOffset, deltaX, deltaY, duration)); } - - /// - /// Scrolls by provided amounts with the origin in the top left corner of the viewport. - /// - /// Distance along X axis to scroll using the wheel. A negative value scrolls left. - /// Distance along Y axis to scroll using the wheel. A negative value scrolls up. - /// A self-reference to this . - public Actions ScrollByAmount(int deltaX, int deltaY) + else { - this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(deltaX, deltaY, duration)); - - return this; + this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(scrollOrigin.Element!, + scrollOrigin.XOffset, scrollOrigin.YOffset, deltaX, deltaY, duration)); } - /// - /// Scrolls by provided amount based on a provided origin. - /// - /// - /// The scroll origin is either the center of an element or the upper left of the viewport plus any offsets. - /// If the origin is an element, and the element is not in the viewport, the bottom of the element will first - /// be scrolled to the bottom of the viewport. - /// - /// Where scroll originates (viewport or element center) plus provided offsets. - /// Distance along X axis to scroll using the wheel. A negative value scrolls left. - /// Distance along Y axis to scroll using the wheel. A negative value scrolls up. - /// A self-reference to this . - /// If the origin with offset is outside the viewport. - /// If is null. - /// If both or either of Viewport and Element are set. - public Actions ScrollFromOrigin(WheelInputDevice.ScrollOrigin scrollOrigin, int deltaX, int deltaY) - { - if (scrollOrigin is null) - { - throw new ArgumentNullException(nameof(scrollOrigin)); - } + return this; + } - if (scrollOrigin.Viewport && scrollOrigin.Element != null) - { - throw new ArgumentException("viewport can not be true if an element is defined.", nameof(scrollOrigin)); - } + /// + /// Performs a Pause. + /// + /// How long to pause the action chain. + /// A self-reference to this . + /// If is negative. + public Actions Pause(TimeSpan duration) + { + this.actionBuilder.AddAction(new PauseInteraction(this.GetActivePointer(), duration)); + return this; + } - if (scrollOrigin.Viewport) - { - this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(CoordinateOrigin.Viewport, - scrollOrigin.XOffset, scrollOrigin.YOffset, deltaX, deltaY, duration)); - } - else - { - this.actionBuilder.AddAction(this.GetActiveWheel().CreateWheelScroll(scrollOrigin.Element!, - scrollOrigin.XOffset, scrollOrigin.YOffset, deltaX, deltaY, duration)); - } + /// + /// Builds the sequence of actions. + /// + /// A composite which can be used to perform the actions. + public IAction Build() + { + return this; + } - return this; - } + /// + /// Performs the currently built action. + /// + public void Perform() + { + this.ActionExecutor.PerformActions(this.actionBuilder.ToActionSequenceList()); + this.actionBuilder.ClearSequences(); + } - /// - /// Performs a Pause. - /// - /// How long to pause the action chain. - /// A self-reference to this . - /// If is negative. - public Actions Pause(TimeSpan duration) - { - this.actionBuilder.AddAction(new PauseInteraction(this.GetActivePointer(), duration)); - return this; - } + /// + /// Clears the list of actions to be performed. + /// + public void Reset() + { + this.actionBuilder = new ActionBuilder(); + } - /// - /// Builds the sequence of actions. - /// - /// A composite which can be used to perform the actions. - public IAction Build() + /// + /// Gets the instance of the specified . + /// + /// The to get the location of. + /// The of the . + [return: NotNullIfNotNull(nameof(element))] + protected static ILocatable? GetLocatableFromElement(IWebElement? element) + { + if (element == null) { - return this; + return null; } - /// - /// Performs the currently built action. - /// - public void Perform() + ILocatable? target = null; + IWrapsElement? wrapper = element as IWrapsElement; + while (wrapper != null) { - this.ActionExecutor.PerformActions(this.actionBuilder.ToActionSequenceList()); - this.actionBuilder.ClearSequences(); + target = wrapper.WrappedElement as ILocatable; + wrapper = wrapper.WrappedElement as IWrapsElement; } - /// - /// Clears the list of actions to be performed. - /// - public void Reset() + if (target == null) { - this.actionBuilder = new ActionBuilder(); + target = element as ILocatable; } - /// - /// Gets the instance of the specified . - /// - /// The to get the location of. - /// The of the . - [return: NotNullIfNotNull(nameof(element))] - protected static ILocatable? GetLocatableFromElement(IWebElement? element) + if (target == null) { - if (element == null) - { - return null; - } - - ILocatable? target = null; - IWrapsElement? wrapper = element as IWrapsElement; - while (wrapper != null) - { - target = wrapper.WrappedElement as ILocatable; - wrapper = wrapper.WrappedElement as IWrapsElement; - } - - if (target == null) - { - target = element as ILocatable; - } - - if (target == null) - { - throw new ArgumentException("The IWebElement object must implement or wrap an element that implements ILocatable.", nameof(element)); - } - - return target; + throw new ArgumentException("The IWebElement object must implement or wrap an element that implements ILocatable.", nameof(element)); } - private static T? GetDriverAs(IWebDriver? driver) where T : class + return target; + } + + private static T? GetDriverAs(IWebDriver? driver) where T : class + { + T? driverAsType = driver as T; + if (driverAsType == null) { - T? driverAsType = driver as T; - if (driverAsType == null) + IWrapsDriver? wrapper = driver as IWrapsDriver; + while (wrapper != null) { - IWrapsDriver? wrapper = driver as IWrapsDriver; - while (wrapper != null) + driverAsType = wrapper.WrappedDriver as T; + if (driverAsType != null) { - driverAsType = wrapper.WrappedDriver as T; - if (driverAsType != null) - { - driver = wrapper.WrappedDriver; - break; - } - - wrapper = wrapper.WrappedDriver as IWrapsDriver; + driver = wrapper.WrappedDriver; + break; } - } - return driverAsType; + wrapper = wrapper.WrappedDriver as IWrapsDriver; + } } + + return driverAsType; } } diff --git a/dotnet/src/webdriver/Interactions/IAction.cs b/dotnet/src/webdriver/Interactions/IAction.cs index 57dfdcdd2fc72..2cea79ff1a71a 100644 --- a/dotnet/src/webdriver/Interactions/IAction.cs +++ b/dotnet/src/webdriver/Interactions/IAction.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Provides methods by which an interaction with the browser can be performed. +/// +public interface IAction { /// - /// Provides methods by which an interaction with the browser can be performed. + /// Performs this action on the browser. /// - public interface IAction - { - /// - /// Performs this action on the browser. - /// - void Perform(); - } + void Perform(); } diff --git a/dotnet/src/webdriver/Interactions/ICoordinates.cs b/dotnet/src/webdriver/Interactions/ICoordinates.cs index 7f73d2167d3bc..40a25d18bb3df 100644 --- a/dotnet/src/webdriver/Interactions/ICoordinates.cs +++ b/dotnet/src/webdriver/Interactions/ICoordinates.cs @@ -19,31 +19,30 @@ using System.Drawing; -namespace OpenQA.Selenium.Interactions.Internal +namespace OpenQA.Selenium.Interactions.Internal; + +/// +/// Provides location of the element using various frames of reference. +/// +public interface ICoordinates { /// - /// Provides location of the element using various frames of reference. + /// Gets the location of an element in absolute screen coordinates. /// - public interface ICoordinates - { - /// - /// Gets the location of an element in absolute screen coordinates. - /// - Point LocationOnScreen { get; } + Point LocationOnScreen { get; } - /// - /// Gets the location of an element relative to the origin of the view port. - /// - Point LocationInViewport { get; } + /// + /// Gets the location of an element relative to the origin of the view port. + /// + Point LocationInViewport { get; } - /// - /// Gets the location of an element's position within the HTML DOM. - /// - Point LocationInDom { get; } + /// + /// Gets the location of an element's position within the HTML DOM. + /// + Point LocationInDom { get; } - /// - /// Gets a locator providing a user-defined location for this element. - /// - object AuxiliaryLocator { get; } - } + /// + /// Gets a locator providing a user-defined location for this element. + /// + object AuxiliaryLocator { get; } } diff --git a/dotnet/src/webdriver/Interactions/InputDevice.cs b/dotnet/src/webdriver/Interactions/InputDevice.cs index a8d9ab773f3d0..0501168e81e98 100644 --- a/dotnet/src/webdriver/Interactions/InputDevice.cs +++ b/dotnet/src/webdriver/Interactions/InputDevice.cs @@ -21,82 +21,81 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Base class for all input devices for actions. +/// +public abstract class InputDevice { /// - /// Base class for all input devices for actions. + /// Initializes a new instance of the class. /// - public abstract class InputDevice + /// The unique name of the input device represented by this class. + /// If is or . + protected InputDevice(string deviceName) { - /// - /// Initializes a new instance of the class. - /// - /// The unique name of the input device represented by this class. - /// If is or . - protected InputDevice(string deviceName) + if (string.IsNullOrEmpty(deviceName)) { - if (string.IsNullOrEmpty(deviceName)) - { - throw new ArgumentException("Device name must not be null or empty", nameof(deviceName)); - } - - this.DeviceName = deviceName; + throw new ArgumentException("Device name must not be null or empty", nameof(deviceName)); } - /// - /// Gets the unique name of this input device. - /// - public string DeviceName { get; } + this.DeviceName = deviceName; + } - /// - /// Gets the kind of device for this input device. - /// - public abstract InputDeviceKind DeviceKind { get; } + /// + /// Gets the unique name of this input device. + /// + public string DeviceName { get; } - /// - /// Returns a value for this input device that can be transmitted across the wire to a remote end. - /// - /// A representing this action. - public abstract Dictionary ToDictionary(); + /// + /// Gets the kind of device for this input device. + /// + public abstract InputDeviceKind DeviceKind { get; } - /// - /// Creates a pause action for synchronization with other action sequences. - /// - /// The representing the action. - public Interaction CreatePause() - { - return this.CreatePause(TimeSpan.Zero); - } + /// + /// Returns a value for this input device that can be transmitted across the wire to a remote end. + /// + /// A representing this action. + public abstract Dictionary ToDictionary(); - /// - /// Creates a pause action for synchronization with other action sequences. - /// - /// A representing the duration - /// of the pause. Note that pauses to synchronize - /// with other action sequences for other devices. - /// The representing the action. - /// If is negative. - public Interaction CreatePause(TimeSpan duration) - { - return new PauseInteraction(this, duration); - } + /// + /// Creates a pause action for synchronization with other action sequences. + /// + /// The representing the action. + public Interaction CreatePause() + { + return this.CreatePause(TimeSpan.Zero); + } - /// - /// Returns a hash code for the current . - /// - /// A hash code for the current . - public override int GetHashCode() - { - return this.DeviceName.GetHashCode(); - } + /// + /// Creates a pause action for synchronization with other action sequences. + /// + /// A representing the duration + /// of the pause. Note that pauses to synchronize + /// with other action sequences for other devices. + /// The representing the action. + /// If is negative. + public Interaction CreatePause(TimeSpan duration) + { + return new PauseInteraction(this, duration); + } - /// - /// Returns a string that represents the current . - /// - /// A string that represents the current . - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "{0} input device [name: {1}]", this.DeviceKind, this.DeviceName); - } + /// + /// Returns a hash code for the current . + /// + /// A hash code for the current . + public override int GetHashCode() + { + return this.DeviceName.GetHashCode(); + } + + /// + /// Returns a string that represents the current . + /// + /// A string that represents the current . + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0} input device [name: {1}]", this.DeviceKind, this.DeviceName); } } diff --git a/dotnet/src/webdriver/Interactions/InputDeviceKind.cs b/dotnet/src/webdriver/Interactions/InputDeviceKind.cs index 5c707ab72cb9b..e0b93dba653e1 100644 --- a/dotnet/src/webdriver/Interactions/InputDeviceKind.cs +++ b/dotnet/src/webdriver/Interactions/InputDeviceKind.cs @@ -17,31 +17,30 @@ // under the License. // -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Enumerated values for the kinds of devices available. +/// +public enum InputDeviceKind { /// - /// Enumerated values for the kinds of devices available. + /// Represents the null device. /// - public enum InputDeviceKind - { - /// - /// Represents the null device. - /// - None, + None, - /// - /// Represents a key-based device, primarily for entering text. - /// - Key, + /// + /// Represents a key-based device, primarily for entering text. + /// + Key, - /// - /// Represents a pointer-based device, such as a mouse, pen, or stylus. - /// - Pointer, + /// + /// Represents a pointer-based device, such as a mouse, pen, or stylus. + /// + Pointer, - /// - /// Represents a wheel device. - /// - Wheel - } + /// + /// Represents a wheel device. + /// + Wheel } diff --git a/dotnet/src/webdriver/Interactions/Interaction.cs b/dotnet/src/webdriver/Interactions/Interaction.cs index d9c12215b2042..04a5d2f379938 100644 --- a/dotnet/src/webdriver/Interactions/Interaction.cs +++ b/dotnet/src/webdriver/Interactions/Interaction.cs @@ -20,42 +20,41 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents a single interaction for a given input device. +/// +public abstract class Interaction { /// - /// Represents a single interaction for a given input device. + /// Initializes a new instance of the class. /// - public abstract class Interaction + /// The input device which performs this action. + protected Interaction(InputDevice sourceDevice) { - /// - /// Initializes a new instance of the class. - /// - /// The input device which performs this action. - protected Interaction(InputDevice sourceDevice) - { - this.SourceDevice = sourceDevice ?? throw new ArgumentNullException(nameof(sourceDevice), "Source device cannot be null"); - } + this.SourceDevice = sourceDevice ?? throw new ArgumentNullException(nameof(sourceDevice), "Source device cannot be null"); + } - /// - /// Gets the device for which this action is intended. - /// - public InputDevice SourceDevice { get; } + /// + /// Gets the device for which this action is intended. + /// + public InputDevice SourceDevice { get; } - /// - /// Returns a value for this action that can be transmitted across the wire to a remote end. - /// - /// A representing this action. - public abstract Dictionary ToDictionary(); + /// + /// Returns a value for this action that can be transmitted across the wire to a remote end. + /// + /// A representing this action. + public abstract Dictionary ToDictionary(); - /// - /// Gets a value indicating whether this action is valid for the specified type of input device. - /// - /// The type of device to check. - /// if the action is valid for the specified type of input device; - /// otherwise, . - public virtual bool IsValidFor(InputDeviceKind sourceDeviceKind) - { - return this.SourceDevice.DeviceKind == sourceDeviceKind; - } + /// + /// Gets a value indicating whether this action is valid for the specified type of input device. + /// + /// The type of device to check. + /// if the action is valid for the specified type of input device; + /// otherwise, . + public virtual bool IsValidFor(InputDeviceKind sourceDeviceKind) + { + return this.SourceDevice.DeviceKind == sourceDeviceKind; } } diff --git a/dotnet/src/webdriver/Interactions/KeyInputDevice.cs b/dotnet/src/webdriver/Interactions/KeyInputDevice.cs index fbe8b620344e3..b7dba67ec17f3 100644 --- a/dotnet/src/webdriver/Interactions/KeyInputDevice.cs +++ b/dotnet/src/webdriver/Interactions/KeyInputDevice.cs @@ -21,118 +21,117 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents a key input device, such as a keyboard. +/// +public class KeyInputDevice : InputDevice { /// - /// Represents a key input device, such as a keyboard. + /// Initializes a new instance of the class. /// - public class KeyInputDevice : InputDevice + public KeyInputDevice() + : this(Guid.NewGuid().ToString()) { - /// - /// Initializes a new instance of the class. - /// - public KeyInputDevice() - : this(Guid.NewGuid().ToString()) - { - } + } - /// - /// Initializes a new instance of the class, given the device's name. - /// - /// The unique name of this input device. - /// If is or . - public KeyInputDevice(string deviceName) - : base(deviceName) - { - } + /// + /// Initializes a new instance of the class, given the device's name. + /// + /// The unique name of this input device. + /// If is or . + public KeyInputDevice(string deviceName) + : base(deviceName) + { + } - /// - /// Gets the type of device for this input device. - /// - public override InputDeviceKind DeviceKind => InputDeviceKind.Key; + /// + /// Gets the type of device for this input device. + /// + public override InputDeviceKind DeviceKind => InputDeviceKind.Key; - /// - /// Converts this input device into an object suitable for serializing across the wire. - /// - /// A representing this input device. - public override Dictionary ToDictionary() - { - Dictionary toReturn = new Dictionary(); + /// + /// Converts this input device into an object suitable for serializing across the wire. + /// + /// A representing this input device. + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - toReturn["type"] = "key"; - toReturn["id"] = this.DeviceName; + toReturn["type"] = "key"; + toReturn["id"] = this.DeviceName; - return toReturn; - } + return toReturn; + } - /// - /// Creates a key-down action for simulating a press of a key. - /// - /// The unicode character to be sent. - /// The representing the action. - public Interaction CreateKeyDown(char codePoint) + /// + /// Creates a key-down action for simulating a press of a key. + /// + /// The unicode character to be sent. + /// The representing the action. + public Interaction CreateKeyDown(char codePoint) + { + return new KeyDownInteraction(this, codePoint); + } + + /// + /// Creates a key-up action for simulating a release of a key. + /// + /// The unicode character to be sent. + /// The representing the action. + public Interaction CreateKeyUp(char codePoint) + { + return new KeyUpInteraction(this, codePoint); + } + + private class KeyDownInteraction : TypingInteraction + { + public KeyDownInteraction(InputDevice sourceDevice, char codePoint) + : base(sourceDevice, "keyDown", codePoint) { - return new KeyDownInteraction(this, codePoint); } - /// - /// Creates a key-up action for simulating a release of a key. - /// - /// The unicode character to be sent. - /// The representing the action. - public Interaction CreateKeyUp(char codePoint) + public override string ToString() { - return new KeyUpInteraction(this, codePoint); + return string.Format(CultureInfo.InvariantCulture, "Key down [key: {0}]", Keys.GetDescription(this.Value)); } + } - private class KeyDownInteraction : TypingInteraction + private class KeyUpInteraction : TypingInteraction + { + public KeyUpInteraction(InputDevice sourceDevice, char codePoint) + : base(sourceDevice, "keyUp", codePoint) { - public KeyDownInteraction(InputDevice sourceDevice, char codePoint) - : base(sourceDevice, "keyDown", codePoint) - { - } - - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Key down [key: {0}]", Keys.GetDescription(this.Value)); - } } - private class KeyUpInteraction : TypingInteraction + public override string ToString() { - public KeyUpInteraction(InputDevice sourceDevice, char codePoint) - : base(sourceDevice, "keyUp", codePoint) - { - } - - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Key up [key: {0}]", Keys.GetDescription(this.Value)); - } + return string.Format(CultureInfo.InvariantCulture, "Key up [key: {0}]", Keys.GetDescription(this.Value)); } + } - private class TypingInteraction : Interaction - { - private readonly string type; + private class TypingInteraction : Interaction + { + private readonly string type; - public TypingInteraction(InputDevice sourceDevice, string type, char codePoint) - : base(sourceDevice) - { - this.type = type; - this.Value = codePoint.ToString(); - } + public TypingInteraction(InputDevice sourceDevice, string type, char codePoint) + : base(sourceDevice) + { + this.type = type; + this.Value = codePoint.ToString(); + } - protected string Value { get; } + protected string Value { get; } - public override Dictionary ToDictionary() - { - Dictionary toReturn = new Dictionary(); + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - toReturn["type"] = this.type; - toReturn["value"] = this.Value; + toReturn["type"] = this.type; + toReturn["value"] = this.Value; - return toReturn; - } + return toReturn; } } } diff --git a/dotnet/src/webdriver/Interactions/PauseInteraction.cs b/dotnet/src/webdriver/Interactions/PauseInteraction.cs index 92bf4783472f4..e17993c238917 100644 --- a/dotnet/src/webdriver/Interactions/PauseInteraction.cs +++ b/dotnet/src/webdriver/Interactions/PauseInteraction.cs @@ -21,69 +21,68 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents a pause action. +/// +internal class PauseInteraction : Interaction { + private readonly TimeSpan duration = TimeSpan.Zero; + /// - /// Represents a pause action. + /// Initializes a new instance of the class. /// - internal class PauseInteraction : Interaction + /// The input device on which to execute the pause. + public PauseInteraction(InputDevice sourceDevice) + : this(sourceDevice, TimeSpan.Zero) { - private readonly TimeSpan duration = TimeSpan.Zero; + } - /// - /// Initializes a new instance of the class. - /// - /// The input device on which to execute the pause. - public PauseInteraction(InputDevice sourceDevice) - : this(sourceDevice, TimeSpan.Zero) + /// + /// Initializes a new instance of the class. + /// + /// The input device on which to execute the pause. + /// The length of time to pause for. + /// If is negative. + public PauseInteraction(InputDevice sourceDevice, TimeSpan duration) + : base(sourceDevice) + { + if (duration < TimeSpan.Zero) { + throw new ArgumentException("Duration must be greater than or equal to zero", nameof(duration)); } - /// - /// Initializes a new instance of the class. - /// - /// The input device on which to execute the pause. - /// The length of time to pause for. - /// If is negative. - public PauseInteraction(InputDevice sourceDevice, TimeSpan duration) - : base(sourceDevice) - { - if (duration < TimeSpan.Zero) - { - throw new ArgumentException("Duration must be greater than or equal to zero", nameof(duration)); - } - - this.duration = duration; - } + this.duration = duration; + } - /// - /// Returns a value for this action that can be transmitted across the wire to a remote end. - /// - /// A representing this action. - public override Dictionary ToDictionary() - { - Dictionary toReturn = new Dictionary(); + /// + /// Returns a value for this action that can be transmitted across the wire to a remote end. + /// + /// A representing this action. + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - toReturn["type"] = "pause"; - toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); + toReturn["type"] = "pause"; + toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); - return toReturn; - } + return toReturn; + } - /// - /// Gets a value indicating whether this action is valid for the specified type of input device. - /// - /// The type of device to check. - /// if the action is valid for the specified type of input device; - /// otherwise, . - public override bool IsValidFor(InputDeviceKind sourceDeviceKind) - { - return true; - } + /// + /// Gets a value indicating whether this action is valid for the specified type of input device. + /// + /// The type of device to check. + /// if the action is valid for the specified type of input device; + /// otherwise, . + public override bool IsValidFor(InputDeviceKind sourceDeviceKind) + { + return true; + } - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Pause [duration: {0} ms]", this.duration.TotalMilliseconds); - } + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Pause [duration: {0} ms]", this.duration.TotalMilliseconds); } } diff --git a/dotnet/src/webdriver/Interactions/PointerInputDevice.cs b/dotnet/src/webdriver/Interactions/PointerInputDevice.cs index 77c249a855db5..d1c65f4312214 100644 --- a/dotnet/src/webdriver/Interactions/PointerInputDevice.cs +++ b/dotnet/src/webdriver/Interactions/PointerInputDevice.cs @@ -22,593 +22,592 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents the origin of the coordinates for mouse movement. +/// +public enum CoordinateOrigin +{ + /// + /// The coordinate origin is the origin of the view port of the browser. + /// + Viewport, + + /// + /// The origin of the movement is the most recent pointer location. + /// + Pointer, + + /// + /// The origin of the movement is the center of the element specified. + /// + Element +} + +/// +/// Specifies the type of pointer a pointer device represents. +/// +public enum PointerKind +{ + /// + /// The pointer device is a mouse. + /// + Mouse, + + /// + /// The pointer device is a pen or stylus. + /// + Pen, + + /// + /// The pointer device is a touch screen device. + /// + Touch +} + +/// +/// Specifies the button used during a pointer down or up action. +/// +public enum MouseButton +{ + /// + /// This button is used for signifying touch actions. + /// + Touch = 0, + + /// + /// The button used is the primary button. + /// + Left = 0, + + /// + /// The button used is the middle button or mouse wheel. + /// + Middle = 1, + + /// + /// The button used is the secondary button. + /// + Right = 2, + + /// + /// The X1 button used for navigating back. + /// + Back = 3, + + /// + /// The X2 button used for navigating forward. + /// + Forward = 4, +} + +/// +/// Represents a pointer input device, such as a stylus, mouse, or finger on a touch screen. +/// +public class PointerInputDevice : InputDevice { + private readonly PointerKind pointerKind; + /// - /// Represents the origin of the coordinates for mouse movement. + /// Initializes a new instance of the class. /// - public enum CoordinateOrigin + public PointerInputDevice() + : this(PointerKind.Mouse) { - /// - /// The coordinate origin is the origin of the view port of the browser. - /// - Viewport, + } - /// - /// The origin of the movement is the most recent pointer location. - /// - Pointer, + /// + /// Initializes a new instance of the class. + /// + /// The kind of pointer represented by this input device. + public PointerInputDevice(PointerKind pointerKind) + : this(pointerKind, Guid.NewGuid().ToString()) + { + } - /// - /// The origin of the movement is the center of the element specified. - /// - Element + /// + /// Initializes a new instance of the class. + /// + /// The kind of pointer represented by this input device. + /// The unique name for this input device. + /// If is or . + public PointerInputDevice(PointerKind pointerKind, string deviceName) + : base(deviceName) + { + this.pointerKind = pointerKind; } /// - /// Specifies the type of pointer a pointer device represents. + /// Gets the type of device for this input device. + /// + public override InputDeviceKind DeviceKind => InputDeviceKind.Pointer; + + /// + /// Returns a value for this input device that can be transmitted across the wire to a remote end. /// - public enum PointerKind + /// A representing this action. + public override Dictionary ToDictionary() { - /// - /// The pointer device is a mouse. - /// - Mouse, + Dictionary toReturn = new Dictionary(); - /// - /// The pointer device is a pen or stylus. - /// - Pen, + toReturn["type"] = "pointer"; + toReturn["id"] = this.DeviceName; - /// - /// The pointer device is a touch screen device. - /// - Touch + Dictionary parameters = new Dictionary(); + parameters["pointerType"] = this.pointerKind.ToString().ToLowerInvariant(); + toReturn["parameters"] = parameters; + + return toReturn; } /// - /// Specifies the button used during a pointer down or up action. + /// Creates a pointer down action. /// - public enum MouseButton + /// The button of the pointer that should be pressed. + /// The action representing the pointer down gesture. + public Interaction CreatePointerDown(MouseButton button) { - /// - /// This button is used for signifying touch actions. - /// - Touch = 0, + return CreatePointerDown(button, new PointerEventProperties()); + } - /// - /// The button used is the primary button. - /// - Left = 0, + /// + /// Creates a pointer down action. + /// + /// + /// MouseButton value applies to Pen types for primary, secondary and erase functionality (0, 2, and 5 respectively) + /// + /// The button of the pointer that should be pressed. + /// The specifications for the pointer event interaction + /// The action representing the pointer down gesture. + public Interaction CreatePointerDown(MouseButton button, PointerEventProperties properties) + { + return new PointerDownInteraction(this, button, properties); + } - /// - /// The button used is the middle button or mouse wheel. - /// - Middle = 1, + /// + /// Creates a pointer up action. + /// + /// The button of the pointer that should be released. + /// The action representing the pointer up gesture. + public Interaction CreatePointerUp(MouseButton button) + { + return CreatePointerUp(button, new PointerEventProperties()); + } - /// - /// The button used is the secondary button. - /// - Right = 2, + /// + /// Creates a pointer down action. + /// + /// + /// MouseButton value applies to Pen types for primary, secondary and erase functionality (0, 2, and 5 respectively) + /// + /// The button of the pointer that should be pressed. + /// The specifications for the pointer event interaction + /// The action representing the pointer down gesture. + public Interaction CreatePointerUp(MouseButton button, PointerEventProperties properties) + { + return new PointerUpInteraction(this, button, properties); + } - /// - /// The X1 button used for navigating back. - /// - Back = 3, + /// + /// Creates a pointer move action to a specific element. + /// + /// The used as the target for the move. + /// The horizontal offset from the origin of the move. + /// The vertical offset from the origin of the move. + /// The length of time the move gesture takes to complete. + /// The action representing the pointer move gesture. + /// If is . + public Interaction CreatePointerMove(IWebElement target, int xOffset, int yOffset, TimeSpan duration) + { + return CreatePointerMove(target, xOffset, yOffset, duration, new PointerEventProperties()); + } - /// - /// The X2 button used for navigating forward. - /// - Forward = 4, + /// + /// Creates a pointer move action to a specific element. + /// + /// The used as the target for the move. + /// The horizontal offset from the origin of the move. + /// The vertical offset from the origin of the move. + /// The length of time the move gesture takes to complete. + /// The specifications for the pointer event interaction + /// The action representing the pointer move gesture. + /// If is . + public Interaction CreatePointerMove(IWebElement target, int xOffset, int yOffset, TimeSpan duration, PointerEventProperties properties) + { + return new PointerMoveInteraction(this, target, CoordinateOrigin.Element, xOffset, yOffset, duration, properties); } /// - /// Represents a pointer input device, such as a stylus, mouse, or finger on a touch screen. + /// Creates a pointer move action to an absolute coordinate. /// - public class PointerInputDevice : InputDevice + /// The origin of coordinates for the move. Values can be relative to + /// the view port origin, or the most recent pointer position. + /// The horizontal offset from the origin of the move. + /// The vertical offset from the origin of the move. + /// The length of time the move gesture takes to complete. + /// The action representing the pointer move gesture. + /// Thrown when passing CoordinateOrigin.Element into origin. + /// Users should us the other CreatePointerMove overload to move to a specific element. + public Interaction CreatePointerMove(CoordinateOrigin origin, int xOffset, int yOffset, TimeSpan duration) { - private readonly PointerKind pointerKind; + return CreatePointerMove(origin, xOffset, yOffset, duration, new PointerEventProperties()); + } - /// - /// Initializes a new instance of the class. - /// - public PointerInputDevice() - : this(PointerKind.Mouse) + /// + /// Creates a pointer move action to an absolute coordinate. + /// + /// The origin of coordinates for the move. Values can be relative to + /// the view port origin, or the most recent pointer position. + /// The horizontal offset from the origin of the move. + /// The vertical offset from the origin of the move. + /// The length of time the move gesture takes to complete. + /// The object containing additional proprties for this pointer move operation. + /// The action representing the pointer move gesture. + /// Thrown when passing CoordinateOrigin.Element into origin. + /// Users should use the other CreatePointerMove overload to move to a specific element. + public Interaction CreatePointerMove(CoordinateOrigin origin, int xOffset, int yOffset, TimeSpan duration, PointerEventProperties properties) + { + if (origin == CoordinateOrigin.Element) { + throw new ArgumentException("Using a value of CoordinateOrigin.Element without an element is not supported.", nameof(origin)); } - /// - /// Initializes a new instance of the class. - /// - /// The kind of pointer represented by this input device. - public PointerInputDevice(PointerKind pointerKind) - : this(pointerKind, Guid.NewGuid().ToString()) - { - } + return new PointerMoveInteraction(this, null, origin, xOffset, yOffset, duration, properties); + } - /// - /// Initializes a new instance of the class. - /// - /// The kind of pointer represented by this input device. - /// The unique name for this input device. - /// If is or . - public PointerInputDevice(PointerKind pointerKind, string deviceName) - : base(deviceName) - { - this.pointerKind = pointerKind; - } + /// + /// Creates a pointer cancel action. + /// + /// The action representing the pointer cancel gesture. + public Interaction CreatePointerCancel() + { + return new PointerCancelInteraction(this); + } + /// + /// A class representing the properties of a pointer event. + /// + public class PointerEventProperties + { /// - /// Gets the type of device for this input device. + /// Gets or sets the width (magnitude on x-axis) in pixels of the contact geometry of the pointer. /// - public override InputDeviceKind DeviceKind => InputDeviceKind.Pointer; + public double? Width { get; set; } /// - /// Returns a value for this input device that can be transmitted across the wire to a remote end. + /// Gets or sets the height (magnitude on y-axis) in pixels of the contact geometry of the pointer. /// - /// A representing this action. - public override Dictionary ToDictionary() - { - Dictionary toReturn = new Dictionary(); - - toReturn["type"] = "pointer"; - toReturn["id"] = this.DeviceName; - - Dictionary parameters = new Dictionary(); - parameters["pointerType"] = this.pointerKind.ToString().ToLowerInvariant(); - toReturn["parameters"] = parameters; - - return toReturn; - } + public double? Height { get; set; } /// - /// Creates a pointer down action. + /// Gets or sets the normalized pressure of the pointer input. /// - /// The button of the pointer that should be pressed. - /// The action representing the pointer down gesture. - public Interaction CreatePointerDown(MouseButton button) - { - return CreatePointerDown(button, new PointerEventProperties()); - } + /// + /// 0 and 1 represent the minimum and maximum pressure the hardware is capable of detecting, respectively. + /// + public double? Pressure { get; set; } /// - /// Creates a pointer down action. + /// Gets or sets the normalized tangential pressure (also known as barrel pressure) of the pointer input. /// /// - /// MouseButton value applies to Pen types for primary, secondary and erase functionality (0, 2, and 5 respectively) + /// Valid values are between -1 and 1 with 0 being the neutral position of the control. + /// Some hardware may only support positive values between 0 and 1. /// - /// The button of the pointer that should be pressed. - /// The specifications for the pointer event interaction - /// The action representing the pointer down gesture. - public Interaction CreatePointerDown(MouseButton button, PointerEventProperties properties) - { - return new PointerDownInteraction(this, button, properties); - } + public double? TangentialPressure { get; set; } /// - /// Creates a pointer up action. + /// Gets or sets the plane angle in degrees between the Y-Z plane and the plane containing + /// both the transducer (e.g. pen stylus) axis and the Y axis.. /// - /// The button of the pointer that should be released. - /// The action representing the pointer up gesture. - public Interaction CreatePointerUp(MouseButton button) - { - return CreatePointerUp(button, new PointerEventProperties()); - } + /// + /// Valid values are between -90 and 90. A positive value is to the right. + /// + public int? TiltX { get; set; } /// - /// Creates a pointer down action. + /// Gets or sets the plane angle in degrees between the X-Z plane and the plane containing + /// both the transducer (e.g. pen stylus) axis and the X axis.. /// /// - /// MouseButton value applies to Pen types for primary, secondary and erase functionality (0, 2, and 5 respectively) + /// Valid values are between -90 and 90. A positive value is toward the user. /// - /// The button of the pointer that should be pressed. - /// The specifications for the pointer event interaction - /// The action representing the pointer down gesture. - public Interaction CreatePointerUp(MouseButton button, PointerEventProperties properties) - { - return new PointerUpInteraction(this, button, properties); - } + public int? TiltY { get; set; } /// - /// Creates a pointer move action to a specific element. + /// Gets or sets the clockwise rotation in degrees of a transducer (e.g. stylus) around its own major axis /// - /// The used as the target for the move. - /// The horizontal offset from the origin of the move. - /// The vertical offset from the origin of the move. - /// The length of time the move gesture takes to complete. - /// The action representing the pointer move gesture. - /// If is . - public Interaction CreatePointerMove(IWebElement target, int xOffset, int yOffset, TimeSpan duration) - { - return CreatePointerMove(target, xOffset, yOffset, duration, new PointerEventProperties()); - } + /// + /// Valid values are between 0 and 359. + /// + public int? Twist { get; set; } /// - /// Creates a pointer move action to a specific element. + /// Gets or sets the altitude in radians of the transducer (e.g. pen/stylus) /// - /// The used as the target for the move. - /// The horizontal offset from the origin of the move. - /// The vertical offset from the origin of the move. - /// The length of time the move gesture takes to complete. - /// The specifications for the pointer event interaction - /// The action representing the pointer move gesture. - /// If is . - public Interaction CreatePointerMove(IWebElement target, int xOffset, int yOffset, TimeSpan duration, PointerEventProperties properties) - { - return new PointerMoveInteraction(this, target, CoordinateOrigin.Element, xOffset, yOffset, duration, properties); - } + /// + /// Valid values are between 0 and π/2, where 0 is parallel to the surface (X-Y plane), + /// and π/2 is perpendicular to the surface. + /// + public double? AltitudeAngle { get; set; } /// - /// Creates a pointer move action to an absolute coordinate. + /// Gets or sets the azimuth angle (in radians) of the transducer (e.g. pen/stylus) /// - /// The origin of coordinates for the move. Values can be relative to - /// the view port origin, or the most recent pointer position. - /// The horizontal offset from the origin of the move. - /// The vertical offset from the origin of the move. - /// The length of time the move gesture takes to complete. - /// The action representing the pointer move gesture. - /// Thrown when passing CoordinateOrigin.Element into origin. - /// Users should us the other CreatePointerMove overload to move to a specific element. - public Interaction CreatePointerMove(CoordinateOrigin origin, int xOffset, int yOffset, TimeSpan duration) - { - return CreatePointerMove(origin, xOffset, yOffset, duration, new PointerEventProperties()); - } + /// + /// Valid values are between 0 and 2π, + /// where 0 represents a transducer whose cap is pointing in the direction of increasing X values, + /// and the values progressively increase when going clockwise. + /// + public double? AzimuthAngle { get; set; } /// - /// Creates a pointer move action to an absolute coordinate. + /// Serializes the properties of this input device as a dictionary. /// - /// The origin of coordinates for the move. Values can be relative to - /// the view port origin, or the most recent pointer position. - /// The horizontal offset from the origin of the move. - /// The vertical offset from the origin of the move. - /// The length of time the move gesture takes to complete. - /// The object containing additional proprties for this pointer move operation. - /// The action representing the pointer move gesture. - /// Thrown when passing CoordinateOrigin.Element into origin. - /// Users should use the other CreatePointerMove overload to move to a specific element. - public Interaction CreatePointerMove(CoordinateOrigin origin, int xOffset, int yOffset, TimeSpan duration, PointerEventProperties properties) + /// The dictionary containing the properties of this device. + public Dictionary ToDictionary() { - if (origin == CoordinateOrigin.Element) + Dictionary toReturn = new Dictionary(); + + if (this.Width.HasValue) { - throw new ArgumentException("Using a value of CoordinateOrigin.Element without an element is not supported.", nameof(origin)); + toReturn["width"] = this.Width.Value; } - return new PointerMoveInteraction(this, null, origin, xOffset, yOffset, duration, properties); - } - - /// - /// Creates a pointer cancel action. - /// - /// The action representing the pointer cancel gesture. - public Interaction CreatePointerCancel() - { - return new PointerCancelInteraction(this); - } + if (this.Height.HasValue) + { + toReturn["height"] = this.Height.Value; + } - /// - /// A class representing the properties of a pointer event. - /// - public class PointerEventProperties - { - /// - /// Gets or sets the width (magnitude on x-axis) in pixels of the contact geometry of the pointer. - /// - public double? Width { get; set; } - - /// - /// Gets or sets the height (magnitude on y-axis) in pixels of the contact geometry of the pointer. - /// - public double? Height { get; set; } - - /// - /// Gets or sets the normalized pressure of the pointer input. - /// - /// - /// 0 and 1 represent the minimum and maximum pressure the hardware is capable of detecting, respectively. - /// - public double? Pressure { get; set; } - - /// - /// Gets or sets the normalized tangential pressure (also known as barrel pressure) of the pointer input. - /// - /// - /// Valid values are between -1 and 1 with 0 being the neutral position of the control. - /// Some hardware may only support positive values between 0 and 1. - /// - public double? TangentialPressure { get; set; } - - /// - /// Gets or sets the plane angle in degrees between the Y-Z plane and the plane containing - /// both the transducer (e.g. pen stylus) axis and the Y axis.. - /// - /// - /// Valid values are between -90 and 90. A positive value is to the right. - /// - public int? TiltX { get; set; } - - /// - /// Gets or sets the plane angle in degrees between the X-Z plane and the plane containing - /// both the transducer (e.g. pen stylus) axis and the X axis.. - /// - /// - /// Valid values are between -90 and 90. A positive value is toward the user. - /// - public int? TiltY { get; set; } - - /// - /// Gets or sets the clockwise rotation in degrees of a transducer (e.g. stylus) around its own major axis - /// - /// - /// Valid values are between 0 and 359. - /// - public int? Twist { get; set; } - - /// - /// Gets or sets the altitude in radians of the transducer (e.g. pen/stylus) - /// - /// - /// Valid values are between 0 and π/2, where 0 is parallel to the surface (X-Y plane), - /// and π/2 is perpendicular to the surface. - /// - public double? AltitudeAngle { get; set; } - - /// - /// Gets or sets the azimuth angle (in radians) of the transducer (e.g. pen/stylus) - /// - /// - /// Valid values are between 0 and 2π, - /// where 0 represents a transducer whose cap is pointing in the direction of increasing X values, - /// and the values progressively increase when going clockwise. - /// - public double? AzimuthAngle { get; set; } - - /// - /// Serializes the properties of this input device as a dictionary. - /// - /// The dictionary containing the properties of this device. - public Dictionary ToDictionary() + if (this.Pressure.HasValue) { - Dictionary toReturn = new Dictionary(); + toReturn["pressure"] = this.Pressure.Value; + } - if (this.Width.HasValue) - { - toReturn["width"] = this.Width.Value; - } + if (this.TangentialPressure.HasValue) + { + toReturn["tangentialPressure"] = this.TangentialPressure.Value; + } - if (this.Height.HasValue) - { - toReturn["height"] = this.Height.Value; - } + if (this.TiltX.HasValue) + { + toReturn["tiltX"] = this.TiltX.Value; + } - if (this.Pressure.HasValue) - { - toReturn["pressure"] = this.Pressure.Value; - } + if (this.TiltY.HasValue) + { + toReturn["tiltY"] = this.TiltY.Value; + } - if (this.TangentialPressure.HasValue) - { - toReturn["tangentialPressure"] = this.TangentialPressure.Value; - } + if (this.Twist.HasValue) + { + toReturn["twist"] = this.Twist.Value; + } - if (this.TiltX.HasValue) - { - toReturn["tiltX"] = this.TiltX.Value; - } + if (this.AltitudeAngle.HasValue) + { + toReturn["altitudeAngle"] = this.AltitudeAngle.Value; + } - if (this.TiltY.HasValue) - { - toReturn["tiltY"] = this.TiltY.Value; - } + if (this.AzimuthAngle.HasValue) + { + toReturn["azimuthAngle"] = this.AzimuthAngle.Value; + } - if (this.Twist.HasValue) - { - toReturn["twist"] = this.Twist.Value; - } + return toReturn; + } + } - if (this.AltitudeAngle.HasValue) - { - toReturn["altitudeAngle"] = this.AltitudeAngle.Value; - } + private class PointerDownInteraction : Interaction + { + private readonly MouseButton button; + private readonly PointerEventProperties eventProperties; - if (this.AzimuthAngle.HasValue) - { - toReturn["azimuthAngle"] = this.AzimuthAngle.Value; - } + public PointerDownInteraction(InputDevice sourceDevice, MouseButton button, PointerEventProperties properties) + : base(sourceDevice) + { + this.button = button; + this.eventProperties = properties; + } - return toReturn; + public override Dictionary ToDictionary() + { + Dictionary toReturn; + if (eventProperties is null) + { + toReturn = new Dictionary(); } + else + { + toReturn = eventProperties.ToDictionary(); + } + toReturn["type"] = "pointerDown"; + toReturn["button"] = Convert.ToInt32(this.button, CultureInfo.InvariantCulture); + + return toReturn; } - private class PointerDownInteraction : Interaction + public override string ToString() { - private readonly MouseButton button; - private readonly PointerEventProperties eventProperties; + return "Pointer down"; + } + } - public PointerDownInteraction(InputDevice sourceDevice, MouseButton button, PointerEventProperties properties) - : base(sourceDevice) - { - this.button = button; - this.eventProperties = properties; - } + private class PointerUpInteraction : Interaction + { + private readonly MouseButton button; + private readonly PointerEventProperties eventProperties; - public override Dictionary ToDictionary() - { - Dictionary toReturn; - if (eventProperties is null) - { - toReturn = new Dictionary(); - } - else - { - toReturn = eventProperties.ToDictionary(); - } - toReturn["type"] = "pointerDown"; - toReturn["button"] = Convert.ToInt32(this.button, CultureInfo.InvariantCulture); + public PointerUpInteraction(InputDevice sourceDevice, MouseButton button, PointerEventProperties properties) + : base(sourceDevice) + { + this.button = button; + this.eventProperties = properties; + } - return toReturn; + public override Dictionary ToDictionary() + { + Dictionary toReturn; + if (eventProperties is null) + { + toReturn = new Dictionary(); } - - public override string ToString() + else { - return "Pointer down"; + toReturn = eventProperties.ToDictionary(); } + + toReturn["type"] = "pointerUp"; + toReturn["button"] = Convert.ToInt32(this.button, CultureInfo.InvariantCulture); + + return toReturn; } - private class PointerUpInteraction : Interaction + public override string ToString() { - private readonly MouseButton button; - private readonly PointerEventProperties eventProperties; + return "Pointer up"; + } + } + + private class PointerCancelInteraction : Interaction + { + public PointerCancelInteraction(InputDevice sourceDevice) + : base(sourceDevice) + { + } - public PointerUpInteraction(InputDevice sourceDevice, MouseButton button, PointerEventProperties properties) - : base(sourceDevice) + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); + toReturn["type"] = "pointerCancel"; + return toReturn; + } + + public override string ToString() + { + return "Pointer cancel"; + } + } + + private class PointerMoveInteraction : Interaction + { + private readonly IWebElement? target; + private readonly int x = 0; + private readonly int y = 0; + private TimeSpan duration = TimeSpan.MinValue; + private readonly CoordinateOrigin origin = CoordinateOrigin.Pointer; + private readonly PointerEventProperties eventProperties; + + public PointerMoveInteraction(InputDevice sourceDevice, IWebElement? target, CoordinateOrigin origin, int x, int y, TimeSpan duration, PointerEventProperties properties) + : base(sourceDevice) + { + if (target != null) { - this.button = button; - this.eventProperties = properties; + this.target = target; + this.origin = CoordinateOrigin.Element; } - - public override Dictionary ToDictionary() + else { - Dictionary toReturn; - if (eventProperties is null) + if (this.origin != CoordinateOrigin.Element) { - toReturn = new Dictionary(); + this.origin = origin; } - else - { - toReturn = eventProperties.ToDictionary(); - } - - toReturn["type"] = "pointerUp"; - toReturn["button"] = Convert.ToInt32(this.button, CultureInfo.InvariantCulture); - - return toReturn; } - public override string ToString() + if (duration != TimeSpan.MinValue) { - return "Pointer up"; + this.duration = duration; } + + this.x = x; + this.y = y; + this.eventProperties = properties; } - private class PointerCancelInteraction : Interaction + public override Dictionary ToDictionary() { - public PointerCancelInteraction(InputDevice sourceDevice) - : base(sourceDevice) + Dictionary toReturn; + if (eventProperties == null) { + toReturn = new Dictionary(); } - - public override Dictionary ToDictionary() + else { - Dictionary toReturn = new Dictionary(); - toReturn["type"] = "pointerCancel"; - return toReturn; + toReturn = eventProperties.ToDictionary(); } - public override string ToString() + toReturn["type"] = "pointerMove"; + if (this.duration != TimeSpan.MinValue) { - return "Pointer cancel"; + toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); } - } - private class PointerMoveInteraction : Interaction - { - private readonly IWebElement? target; - private readonly int x = 0; - private readonly int y = 0; - private TimeSpan duration = TimeSpan.MinValue; - private readonly CoordinateOrigin origin = CoordinateOrigin.Pointer; - private readonly PointerEventProperties eventProperties; - - public PointerMoveInteraction(InputDevice sourceDevice, IWebElement? target, CoordinateOrigin origin, int x, int y, TimeSpan duration, PointerEventProperties properties) - : base(sourceDevice) + if (this.target != null) { - if (target != null) - { - this.target = target; - this.origin = CoordinateOrigin.Element; - } - else - { - if (this.origin != CoordinateOrigin.Element) - { - this.origin = origin; - } - } - - if (duration != TimeSpan.MinValue) - { - this.duration = duration; - } - - this.x = x; - this.y = y; - this.eventProperties = properties; + toReturn["origin"] = this.ConvertElement(); } - - public override Dictionary ToDictionary() + else { - Dictionary toReturn; - if (eventProperties == null) - { - toReturn = new Dictionary(); - } - else - { - toReturn = eventProperties.ToDictionary(); - } - - toReturn["type"] = "pointerMove"; - if (this.duration != TimeSpan.MinValue) - { - toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); - } + toReturn["origin"] = this.origin.ToString().ToLowerInvariant(); + } - if (this.target != null) - { - toReturn["origin"] = this.ConvertElement(); - } - else - { - toReturn["origin"] = this.origin.ToString().ToLowerInvariant(); - } + toReturn["x"] = this.x; + toReturn["y"] = this.y; - toReturn["x"] = this.x; - toReturn["y"] = this.y; + return toReturn; + } - return toReturn; + public override string ToString() + { + string originDescription = this.origin.ToString(); + if (this.origin == CoordinateOrigin.Element) + { + originDescription = this.target?.ToString() ?? ""; } - public override string ToString() + return string.Format(CultureInfo.InvariantCulture, "Pointer move [origin: {0}, x offset: {1}, y offset: {2}, duration: {3}ms]", originDescription, this.x, this.y, this.duration.TotalMilliseconds); + } + + private Dictionary ConvertElement() + { + IWebDriverObjectReference? elementReference = this.target as IWebDriverObjectReference; + if (elementReference == null) { - string originDescription = this.origin.ToString(); - if (this.origin == CoordinateOrigin.Element) + IWrapsElement? elementWrapper = this.target as IWrapsElement; + if (elementWrapper != null) { - originDescription = this.target?.ToString() ?? ""; + elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; } - - return string.Format(CultureInfo.InvariantCulture, "Pointer move [origin: {0}, x offset: {1}, y offset: {2}, duration: {3}ms]", originDescription, this.x, this.y, this.duration.TotalMilliseconds); } - private Dictionary ConvertElement() + if (elementReference == null) { - IWebDriverObjectReference? elementReference = this.target as IWebDriverObjectReference; - if (elementReference == null) - { - IWrapsElement? elementWrapper = this.target as IWrapsElement; - if (elementWrapper != null) - { - elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; - } - } - - if (elementReference == null) - { - throw new ArgumentException("Target element cannot be converted to IWebElementReference"); - } - - Dictionary elementDictionary = elementReference.ToDictionary(); - return elementDictionary; + throw new ArgumentException("Target element cannot be converted to IWebElementReference"); } + + Dictionary elementDictionary = elementReference.ToDictionary(); + return elementDictionary; } } } diff --git a/dotnet/src/webdriver/Interactions/WheelInputDevice.cs b/dotnet/src/webdriver/Interactions/WheelInputDevice.cs index 006e7f82017ec..7a30103774fae 100644 --- a/dotnet/src/webdriver/Interactions/WheelInputDevice.cs +++ b/dotnet/src/webdriver/Interactions/WheelInputDevice.cs @@ -21,210 +21,209 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.Interactions +namespace OpenQA.Selenium.Interactions; + +/// +/// Represents a wheel input device, such as a mouse wheel. +/// +public class WheelInputDevice : InputDevice { /// - /// Represents a wheel input device, such as a mouse wheel. + /// Initializes a new instance of the class. /// - public class WheelInputDevice : InputDevice + public WheelInputDevice() + : this(Guid.NewGuid().ToString()) { - /// - /// Initializes a new instance of the class. - /// - public WheelInputDevice() - : this(Guid.NewGuid().ToString()) - { - } + } - /// - /// Initializes a new instance of the class, given the device's name. - /// - /// The unique name of this input device. - /// If is or . - public WheelInputDevice(string deviceName) - : base(deviceName) - { - } + /// + /// Initializes a new instance of the class, given the device's name. + /// + /// The unique name of this input device. + /// If is or . + public WheelInputDevice(string deviceName) + : base(deviceName) + { + } - /// - /// Gets the type of device for this input device. - /// - public override InputDeviceKind DeviceKind => InputDeviceKind.Wheel; + /// + /// Gets the type of device for this input device. + /// + public override InputDeviceKind DeviceKind => InputDeviceKind.Wheel; - /// - /// Returns a value for this input device that can be transmitted across the wire to a remote end. - /// - /// A representing this action. - public override Dictionary ToDictionary() - { - Dictionary toReturn = new Dictionary(); + /// + /// Returns a value for this input device that can be transmitted across the wire to a remote end. + /// + /// A representing this action. + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - toReturn["type"] = "wheel"; - toReturn["id"] = this.DeviceName; + toReturn["type"] = "wheel"; + toReturn["id"] = this.DeviceName; - return toReturn; - } + return toReturn; + } - /// - /// Creates a wheel scroll action. - /// - /// The distance along the X axis to scroll using the wheel. - /// The distance along the Y axis to scroll using the wheel. - /// The duration of the scroll action. - /// The representing the wheel scroll. - public Interaction CreateWheelScroll(int deltaX, int deltaY, TimeSpan duration) + /// + /// Creates a wheel scroll action. + /// + /// The distance along the X axis to scroll using the wheel. + /// The distance along the Y axis to scroll using the wheel. + /// The duration of the scroll action. + /// The representing the wheel scroll. + public Interaction CreateWheelScroll(int deltaX, int deltaY, TimeSpan duration) + { + return new WheelScrollInteraction(this, null, CoordinateOrigin.Viewport, 0, 0, deltaX, deltaY, duration); + } + + /// + /// Creates a wheel scroll action beginning with an element. + /// + /// The in which to begin the scroll. + /// The horizontal offset from the center of the target element from which to start the scroll. + /// The vertical offset from the center of the target element from which to start the scroll. + /// The distance along the X axis to scroll using the wheel. + /// The distance along the Y axis to scroll using the wheel. + /// The duration of the scroll action. + /// The representing the wheel scroll. + /// If is . + public Interaction CreateWheelScroll(IWebElement target, int xOffset, int yOffset, int deltaX, int deltaY, TimeSpan duration) + { + if (target is null) { - return new WheelScrollInteraction(this, null, CoordinateOrigin.Viewport, 0, 0, deltaX, deltaY, duration); + throw new ArgumentNullException(nameof(target)); } + return new WheelScrollInteraction(this, target, CoordinateOrigin.Element, xOffset, yOffset, deltaX, deltaY, duration); + } + + /// + /// Creates a wheel scroll action. + /// + /// The coordinate origin, either the view port or the current pointer position, from which to begin the scroll. + /// The horizontal offset from the center of the origin from which to start the scroll. + /// The vertical offset from the center of the origin from which to start the scroll. + /// The distance along the X axis to scroll using the wheel. + /// The distance along the Y axis to scroll using the wheel. + /// The duration of the scroll action. + /// The representing the wheel scroll. + public Interaction CreateWheelScroll(CoordinateOrigin origin, int xOffset, int yOffset, int deltaX, int deltaY, TimeSpan duration) + { + return new WheelScrollInteraction(this, null, origin, xOffset, yOffset, deltaX, deltaY, duration); + } + + /// + /// Object representing the scroll origin of a scroll operation. + /// + public class ScrollOrigin + { /// - /// Creates a wheel scroll action beginning with an element. + /// Gets or sets the element for the scroll origin. /// - /// The in which to begin the scroll. - /// The horizontal offset from the center of the target element from which to start the scroll. - /// The vertical offset from the center of the target element from which to start the scroll. - /// The distance along the X axis to scroll using the wheel. - /// The distance along the Y axis to scroll using the wheel. - /// The duration of the scroll action. - /// The representing the wheel scroll. - /// If is . - public Interaction CreateWheelScroll(IWebElement target, int xOffset, int yOffset, int deltaX, int deltaY, TimeSpan duration) - { - if (target is null) - { - throw new ArgumentNullException(nameof(target)); - } + public IWebElement? Element { get; set; } - return new WheelScrollInteraction(this, target, CoordinateOrigin.Element, xOffset, yOffset, deltaX, deltaY, duration); - } + /// + /// Gets or sets a value indicating whether the viewport should be used as the origin. + /// + public bool Viewport { get; set; } /// - /// Creates a wheel scroll action. + /// Gets or sets the horizontal offset of the scroll origin. /// - /// The coordinate origin, either the view port or the current pointer position, from which to begin the scroll. - /// The horizontal offset from the center of the origin from which to start the scroll. - /// The vertical offset from the center of the origin from which to start the scroll. - /// The distance along the X axis to scroll using the wheel. - /// The distance along the Y axis to scroll using the wheel. - /// The duration of the scroll action. - /// The representing the wheel scroll. - public Interaction CreateWheelScroll(CoordinateOrigin origin, int xOffset, int yOffset, int deltaX, int deltaY, TimeSpan duration) - { - return new WheelScrollInteraction(this, null, origin, xOffset, yOffset, deltaX, deltaY, duration); - } + public int XOffset { get; set; } = 0; /// - /// Object representing the scroll origin of a scroll operation. + /// Gets or sets the vertical offset of the scroll origin. /// - public class ScrollOrigin - { - /// - /// Gets or sets the element for the scroll origin. - /// - public IWebElement? Element { get; set; } - - /// - /// Gets or sets a value indicating whether the viewport should be used as the origin. - /// - public bool Viewport { get; set; } - - /// - /// Gets or sets the horizontal offset of the scroll origin. - /// - public int XOffset { get; set; } = 0; - - /// - /// Gets or sets the vertical offset of the scroll origin. - /// - public int YOffset { get; set; } = 0; + public int YOffset { get; set; } = 0; - } + } - private class WheelScrollInteraction : Interaction + private class WheelScrollInteraction : Interaction + { + private readonly IWebElement? target; + private readonly int x = 0; + private readonly int y = 0; + private readonly int deltaX = 0; + private readonly int deltaY = 0; + private readonly TimeSpan duration = TimeSpan.MinValue; + private readonly CoordinateOrigin origin = CoordinateOrigin.Viewport; + + public WheelScrollInteraction(InputDevice sourceDevice, IWebElement? target, CoordinateOrigin origin, int x, int y, int deltaX, int deltaY, TimeSpan duration) + : base(sourceDevice) { - private readonly IWebElement? target; - private readonly int x = 0; - private readonly int y = 0; - private readonly int deltaX = 0; - private readonly int deltaY = 0; - private readonly TimeSpan duration = TimeSpan.MinValue; - private readonly CoordinateOrigin origin = CoordinateOrigin.Viewport; - - public WheelScrollInteraction(InputDevice sourceDevice, IWebElement? target, CoordinateOrigin origin, int x, int y, int deltaX, int deltaY, TimeSpan duration) - : base(sourceDevice) + if (target != null) { - if (target != null) - { - this.target = target; - this.origin = CoordinateOrigin.Element; - } - else - { - if (this.origin != CoordinateOrigin.Element) - { - this.origin = origin; - } - } - - if (duration != TimeSpan.MinValue) - { - this.duration = duration; - } - - this.x = x; - this.y = y; - this.deltaX = deltaX; - this.deltaY = deltaY; + this.target = target; + this.origin = CoordinateOrigin.Element; } - - public override Dictionary ToDictionary() + else { - Dictionary toReturn = new Dictionary(); - - toReturn["type"] = "scroll"; - if (this.duration != TimeSpan.MinValue) + if (this.origin != CoordinateOrigin.Element) { - toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); + this.origin = origin; } + } - if (this.target != null) - { - toReturn["origin"] = this.ConvertElement(); - } - else - { - toReturn["origin"] = this.origin.ToString().ToLowerInvariant(); - } + if (duration != TimeSpan.MinValue) + { + this.duration = duration; + } - toReturn["x"] = this.x; - toReturn["y"] = this.y; + this.x = x; + this.y = y; + this.deltaX = deltaX; + this.deltaY = deltaY; + } - toReturn["deltaX"] = this.deltaX; - toReturn["deltaY"] = this.deltaY; + public override Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - return toReturn; + toReturn["type"] = "scroll"; + if (this.duration != TimeSpan.MinValue) + { + toReturn["duration"] = Convert.ToInt64(this.duration.TotalMilliseconds); } - private Dictionary ConvertElement() + if (this.target != null) { - IWebDriverObjectReference? elementReference = this.target as IWebDriverObjectReference; - if (elementReference == null) - { - IWrapsElement? elementWrapper = this.target as IWrapsElement; - if (elementWrapper != null) - { - elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; - } - } + toReturn["origin"] = this.ConvertElement(); + } + else + { + toReturn["origin"] = this.origin.ToString().ToLowerInvariant(); + } + + toReturn["x"] = this.x; + toReturn["y"] = this.y; + + toReturn["deltaX"] = this.deltaX; + toReturn["deltaY"] = this.deltaY; + + return toReturn; + } - if (elementReference == null) + private Dictionary ConvertElement() + { + IWebDriverObjectReference? elementReference = this.target as IWebDriverObjectReference; + if (elementReference == null) + { + IWrapsElement? elementWrapper = this.target as IWrapsElement; + if (elementWrapper != null) { - throw new ArgumentException("Target element cannot be converted to IWebElementReference"); + elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; } + } - Dictionary elementDictionary = elementReference.ToDictionary(); - return elementDictionary; + if (elementReference == null) + { + throw new ArgumentException("Target element cannot be converted to IWebElementReference"); } + + Dictionary elementDictionary = elementReference.ToDictionary(); + return elementDictionary; } } } diff --git a/dotnet/src/webdriver/Internal/AndroidOptions.cs b/dotnet/src/webdriver/Internal/AndroidOptions.cs index 8dba3b6ec0b04..1d185427d3f16 100644 --- a/dotnet/src/webdriver/Internal/AndroidOptions.cs +++ b/dotnet/src/webdriver/Internal/AndroidOptions.cs @@ -19,41 +19,40 @@ using System; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Provides a base class for options for browsers to be automated on Android. +/// +public class AndroidOptions { /// - /// Provides a base class for options for browsers to be automated on Android. + /// Initializes a new instance of the class. /// - public class AndroidOptions + /// + /// If is or . + protected AndroidOptions(string androidPackage) { - /// - /// Initializes a new instance of the class. - /// - /// - /// If is or . - protected AndroidOptions(string androidPackage) + if (string.IsNullOrEmpty(androidPackage)) { - if (string.IsNullOrEmpty(androidPackage)) - { - throw new ArgumentException("The Android package cannot be null or the empty string", nameof(androidPackage)); - } - - this.AndroidPackage = androidPackage; + throw new ArgumentException("The Android package cannot be null or the empty string", nameof(androidPackage)); } - /// - /// The package name of the application to automate. - /// - public string AndroidPackage { get; } + this.AndroidPackage = androidPackage; + } - /// - /// The serial number of the device on which to launch the application. - /// - public string? AndroidDeviceSerial { get; set; } + /// + /// The package name of the application to automate. + /// + public string AndroidPackage { get; } - /// - /// Gets or sets the name of the Activity hosting the app. - /// - public string? AndroidActivity { get; set; } - } + /// + /// The serial number of the device on which to launch the application. + /// + public string? AndroidDeviceSerial { get; set; } + + /// + /// Gets or sets the name of the Activity hosting the app. + /// + public string? AndroidActivity { get; set; } } diff --git a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs index 2052fd56baa25..6d294e05827ed 100644 --- a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs +++ b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs @@ -19,140 +19,139 @@ using System; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/* + * Based on: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs + * + * Now it is a part of .NET 9+ as System.Buffers.Text.Base64Url + * https://github.com/SeleniumHQ/selenium/issues/14813 + */ + +/// +/// Encodes and Decodes strings as Base64Url encoding. +/// +public static class Base64UrlEncoder { - /* - * Based on: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs - * - * Now it is a part of .NET 9+ as System.Buffers.Text.Base64Url - * https://github.com/SeleniumHQ/selenium/issues/14813 - */ + private const char base64PadCharacter = '='; + private const string doubleBase64PadCharacter = "=="; + private const char base64Character62 = '+'; + private const char base64Character63 = '/'; + private const char base64UrlCharacter62 = '-'; + private const char base64UrlCharacter63 = '_'; /// - /// Encodes and Decodes strings as Base64Url encoding. + /// Encoding table /// - public static class Base64UrlEncoder + internal static readonly char[] s_base64Table = { - private const char base64PadCharacter = '='; - private const string doubleBase64PadCharacter = "=="; - private const char base64Character62 = '+'; - private const char base64Character63 = '/'; - private const char base64UrlCharacter62 = '-'; - private const char base64UrlCharacter63 = '_'; - - /// - /// Encoding table - /// - internal static readonly char[] s_base64Table = + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + base64UrlCharacter62, + base64UrlCharacter63 + }; + + /// + /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation which is encoded with base-64-url digits. + /// + /// An array of 8-bit unsigned integers. + /// The string representation in base 64 url encoding of length elements of inArray, starting at position offset. + /// 'inArray' is null. + public static string Encode(byte[] inArray) + { + _ = inArray ?? throw new ArgumentNullException(nameof(inArray)); + + if (inArray.Length == 0) + return string.Empty; + + var length = inArray.Length; + + int lengthmod3 = length % 3; + int limit = length - lengthmod3; + char[] output = new char[(length + 2) / 3 * 4]; + char[] table = s_base64Table; + int i, j = 0; + + // takes 3 bytes from inArray and insert 4 bytes into output + for (i = 0; i < limit; i += 3) { - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', - '0','1','2','3','4','5','6','7','8','9', - base64UrlCharacter62, - base64UrlCharacter63 - }; - - /// - /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation which is encoded with base-64-url digits. - /// - /// An array of 8-bit unsigned integers. - /// The string representation in base 64 url encoding of length elements of inArray, starting at position offset. - /// 'inArray' is null. - public static string Encode(byte[] inArray) + byte d0 = inArray[i]; + byte d1 = inArray[i + 1]; + byte d2 = inArray[i + 2]; + + output[j + 0] = table[d0 >> 2]; + output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)]; + output[j + 2] = table[((d1 & 0x0f) << 2) | (d2 >> 6)]; + output[j + 3] = table[d2 & 0x3f]; + j += 4; + } + + //Where we left off before + i = limit; + + switch (lengthmod3) { - _ = inArray ?? throw new ArgumentNullException(nameof(inArray)); - - if (inArray.Length == 0) - return string.Empty; - - var length = inArray.Length; - - int lengthmod3 = length % 3; - int limit = length - lengthmod3; - char[] output = new char[(length + 2) / 3 * 4]; - char[] table = s_base64Table; - int i, j = 0; - - // takes 3 bytes from inArray and insert 4 bytes into output - for (i = 0; i < limit; i += 3) - { - byte d0 = inArray[i]; - byte d1 = inArray[i + 1]; - byte d2 = inArray[i + 2]; - - output[j + 0] = table[d0 >> 2]; - output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)]; - output[j + 2] = table[((d1 & 0x0f) << 2) | (d2 >> 6)]; - output[j + 3] = table[d2 & 0x3f]; - j += 4; - } - - //Where we left off before - i = limit; - - switch (lengthmod3) - { - case 2: - { - byte d0 = inArray[i]; - byte d1 = inArray[i + 1]; - - output[j + 0] = table[d0 >> 2]; - output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)]; - output[j + 2] = table[(d1 & 0x0f) << 2]; - j += 3; - } - break; - - case 1: - { - byte d0 = inArray[i]; - - output[j + 0] = table[d0 >> 2]; - output[j + 1] = table[(d0 & 0x03) << 4]; - j += 2; - } - break; - - //default or case 0: no further operations are needed. - } - - return new string(output, 0, j); + case 2: + { + byte d0 = inArray[i]; + byte d1 = inArray[i + 1]; + + output[j + 0] = table[d0 >> 2]; + output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)]; + output[j + 2] = table[(d1 & 0x0f) << 2]; + j += 3; + } + break; + + case 1: + { + byte d0 = inArray[i]; + + output[j + 0] = table[d0 >> 2]; + output[j + 1] = table[(d0 & 0x03) << 4]; + j += 2; + } + break; + + //default or case 0: no further operations are needed. } - /// - /// Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. - /// base64Url encoded string. - /// UTF8 bytes. - public static byte[] DecodeBytes(string str) + return new string(output, 0, j); + } + + /// + /// Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. + /// base64Url encoded string. + /// UTF8 bytes. + public static byte[] DecodeBytes(string str) + { + _ = str ?? throw new ArgumentNullException(nameof(str)); + + // 62nd char of encoding + str = str.Replace(base64UrlCharacter62, base64Character62); + + // 63rd char of encoding + str = str.Replace(base64UrlCharacter63, base64Character63); + + // check for padding + switch (str.Length % 4) { - _ = str ?? throw new ArgumentNullException(nameof(str)); - - // 62nd char of encoding - str = str.Replace(base64UrlCharacter62, base64Character62); - - // 63rd char of encoding - str = str.Replace(base64UrlCharacter63, base64Character63); - - // check for padding - switch (str.Length % 4) - { - case 0: - // No pad chars in this case - break; - case 2: - // Two pad chars - str += doubleBase64PadCharacter; - break; - case 3: - // One pad char - str += base64PadCharacter; - break; - default: - throw new FormatException(str); - } - - return Convert.FromBase64String(str); + case 0: + // No pad chars in this case + break; + case 2: + // Two pad chars + str += doubleBase64PadCharacter; + break; + case 3: + // One pad char + str += base64PadCharacter; + break; + default: + throw new FormatException(str); } + + return Convert.FromBase64String(str); } } diff --git a/dotnet/src/webdriver/Internal/FileUtilities.cs b/dotnet/src/webdriver/Internal/FileUtilities.cs index 5d0aeadd6ed37..1603cdbde8ea5 100644 --- a/dotnet/src/webdriver/Internal/FileUtilities.cs +++ b/dotnet/src/webdriver/Internal/FileUtilities.cs @@ -23,200 +23,199 @@ using System.IO; using System.Reflection; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Encapsulates methods for working with files. +/// +internal static class FileUtilities { + private static readonly ILogger logger = Log.GetLogger(typeof(FileUtilities)); + /// - /// Encapsulates methods for working with files. + /// Recursively copies a directory. /// - internal static class FileUtilities + /// The source directory to copy. + /// The destination directory. + /// if the copy is completed; otherwise . + public static bool CopyDirectory(string sourceDirectory, string destinationDirectory) { - private static readonly ILogger logger = Log.GetLogger(typeof(FileUtilities)); - - /// - /// Recursively copies a directory. - /// - /// The source directory to copy. - /// The destination directory. - /// if the copy is completed; otherwise . - public static bool CopyDirectory(string sourceDirectory, string destinationDirectory) - { - bool copyComplete; - DirectoryInfo sourceDirectoryInfo = new DirectoryInfo(sourceDirectory); - DirectoryInfo destinationDirectoryInfo = new DirectoryInfo(destinationDirectory); + bool copyComplete; + DirectoryInfo sourceDirectoryInfo = new DirectoryInfo(sourceDirectory); + DirectoryInfo destinationDirectoryInfo = new DirectoryInfo(destinationDirectory); - if (sourceDirectoryInfo.Exists) + if (sourceDirectoryInfo.Exists) + { + if (!destinationDirectoryInfo.Exists) { - if (!destinationDirectoryInfo.Exists) - { - destinationDirectoryInfo.Create(); - } + destinationDirectoryInfo.Create(); + } - foreach (FileInfo fileEntry in sourceDirectoryInfo.GetFiles()) - { - fileEntry.CopyTo(Path.Combine(destinationDirectoryInfo.FullName, fileEntry.Name)); - } + foreach (FileInfo fileEntry in sourceDirectoryInfo.GetFiles()) + { + fileEntry.CopyTo(Path.Combine(destinationDirectoryInfo.FullName, fileEntry.Name)); + } - foreach (DirectoryInfo directoryEntry in sourceDirectoryInfo.GetDirectories()) + foreach (DirectoryInfo directoryEntry in sourceDirectoryInfo.GetDirectories()) + { + if (!CopyDirectory(directoryEntry.FullName, Path.Combine(destinationDirectoryInfo.FullName, directoryEntry.Name))) { - if (!CopyDirectory(directoryEntry.FullName, Path.Combine(destinationDirectoryInfo.FullName, directoryEntry.Name))) - { - copyComplete = false; - } + copyComplete = false; } } - - copyComplete = true; - return copyComplete; } - /// - /// Recursively deletes a directory, retrying on error until a timeout. - /// - /// The directory to delete. - /// This method does not throw an exception if the delete fails. - public static void DeleteDirectory(string? directoryToDelete) + copyComplete = true; + return copyComplete; + } + + /// + /// Recursively deletes a directory, retrying on error until a timeout. + /// + /// The directory to delete. + /// This method does not throw an exception if the delete fails. + public static void DeleteDirectory(string? directoryToDelete) + { + int numberOfRetries = 0; + while (Directory.Exists(directoryToDelete) && numberOfRetries < 10) { - int numberOfRetries = 0; - while (Directory.Exists(directoryToDelete) && numberOfRetries < 10) + try { - try - { - Directory.Delete(directoryToDelete, true); - } - catch (IOException) - { - // If we hit an exception (like file still in use), wait a half second - // and try again. If we still hit an exception, go ahead and let it through. - System.Threading.Thread.Sleep(500); - } - catch (UnauthorizedAccessException) - { - // If we hit an exception (like file still in use), wait a half second - // and try again. If we still hit an exception, go ahead and let it through. - System.Threading.Thread.Sleep(500); - } - finally - { - numberOfRetries++; - } + Directory.Delete(directoryToDelete, true); } - - if (Directory.Exists(directoryToDelete)) + catch (IOException) { - if (logger.IsEnabled(LogEventLevel.Trace)) - { - logger.Trace($"Unable to delete directory '{directoryToDelete}'"); - } + // If we hit an exception (like file still in use), wait a half second + // and try again. If we still hit an exception, go ahead and let it through. + System.Threading.Thread.Sleep(500); + } + catch (UnauthorizedAccessException) + { + // If we hit an exception (like file still in use), wait a half second + // and try again. If we still hit an exception, go ahead and let it through. + System.Threading.Thread.Sleep(500); + } + finally + { + numberOfRetries++; } } - /// - /// Searches for a file with the specified name. - /// - /// The name of the file to find. - /// The full path to the directory where the file can be found, - /// or an empty string if the file does not exist in the locations searched. - /// - /// This method looks first in the directory of the currently executing - /// assembly. If the specified file is not there, the method then looks in - /// each directory on the PATH environment variable, in order. - /// - public static string FindFile(string fileName) + if (Directory.Exists(directoryToDelete)) { - // Look first in the same directory as the executing assembly - string currentDirectory = GetCurrentDirectory(); - if (File.Exists(Path.Combine(currentDirectory, fileName))) + if (logger.IsEnabled(LogEventLevel.Trace)) { - return currentDirectory; + logger.Trace($"Unable to delete directory '{directoryToDelete}'"); } + } + } - // If it's not in the same directory as the executing assembly, - // try looking in the system path. - string? systemPath = Environment.GetEnvironmentVariable("PATH"); - if (!string.IsNullOrEmpty(systemPath)) + /// + /// Searches for a file with the specified name. + /// + /// The name of the file to find. + /// The full path to the directory where the file can be found, + /// or an empty string if the file does not exist in the locations searched. + /// + /// This method looks first in the directory of the currently executing + /// assembly. If the specified file is not there, the method then looks in + /// each directory on the PATH environment variable, in order. + /// + public static string FindFile(string fileName) + { + // Look first in the same directory as the executing assembly + string currentDirectory = GetCurrentDirectory(); + if (File.Exists(Path.Combine(currentDirectory, fileName))) + { + return currentDirectory; + } + + // If it's not in the same directory as the executing assembly, + // try looking in the system path. + string? systemPath = Environment.GetEnvironmentVariable("PATH"); + if (!string.IsNullOrEmpty(systemPath)) + { + string expandedPath = Environment.ExpandEnvironmentVariables(systemPath); + string[] directories = expandedPath.Split(Path.PathSeparator); + foreach (string directory in directories) { - string expandedPath = Environment.ExpandEnvironmentVariables(systemPath); - string[] directories = expandedPath.Split(Path.PathSeparator); - foreach (string directory in directories) + // N.B., if the directory in the path contains an invalid character, + // we will skip that directory, meaning no error will be thrown. This + // may be confusing to the user, so we might want to revisit this. + if (directory.IndexOfAny(Path.GetInvalidPathChars()) < 0) { - // N.B., if the directory in the path contains an invalid character, - // we will skip that directory, meaning no error will be thrown. This - // may be confusing to the user, so we might want to revisit this. - if (directory.IndexOfAny(Path.GetInvalidPathChars()) < 0) + if (File.Exists(Path.Combine(directory, fileName))) { - if (File.Exists(Path.Combine(directory, fileName))) - { - currentDirectory = directory; - return currentDirectory; - } + currentDirectory = directory; + return currentDirectory; } } } - - // Note that if it wasn't found on the system path, currentDirectory is still - // set to the same directory as the executing assembly. - return string.Empty; } - /// - /// Gets the directory of the currently executing assembly. - /// - /// The directory of the currently executing assembly. - public static string GetCurrentDirectory() - { - Assembly executingAssembly = typeof(FileUtilities).Assembly; - string? location = null; + // Note that if it wasn't found on the system path, currentDirectory is still + // set to the same directory as the executing assembly. + return string.Empty; + } - // Make sure not to call Path.GetDirectoryName if assembly location is null or empty - string assemblyLocation = executingAssembly.Location; - if (!string.IsNullOrEmpty(assemblyLocation)) - { - location = Path.GetDirectoryName(assemblyLocation); - } + /// + /// Gets the directory of the currently executing assembly. + /// + /// The directory of the currently executing assembly. + public static string GetCurrentDirectory() + { + Assembly executingAssembly = typeof(FileUtilities).Assembly; + string? location = null; - if (string.IsNullOrEmpty(location)) - { - // If there is no location information from the executing - // assembly, we will bail by using the current directory. - // Note this is inaccurate, because the working directory - // may not actually be the directory of the current assembly, - // especially if the WebDriver assembly was embedded as a - // resource. - location = Directory.GetCurrentDirectory(); - } + // Make sure not to call Path.GetDirectoryName if assembly location is null or empty + string assemblyLocation = executingAssembly.Location; + if (!string.IsNullOrEmpty(assemblyLocation)) + { + location = Path.GetDirectoryName(assemblyLocation); + } - string currentDirectory = location!; + if (string.IsNullOrEmpty(location)) + { + // If there is no location information from the executing + // assembly, we will bail by using the current directory. + // Note this is inaccurate, because the working directory + // may not actually be the directory of the current assembly, + // especially if the WebDriver assembly was embedded as a + // resource. + location = Directory.GetCurrentDirectory(); + } + + string currentDirectory = location!; #if !NET8_0_OR_GREATER - // If we're shadow copying, get the directory from the codebase instead - if (AppDomain.CurrentDomain.ShadowCopyFiles) - { - var codeBase = executingAssembly.CodeBase; + // If we're shadow copying, get the directory from the codebase instead + if (AppDomain.CurrentDomain.ShadowCopyFiles) + { + var codeBase = executingAssembly.CodeBase; - if (codeBase is not null) - { - currentDirectory = Path.GetDirectoryName(codeBase); - } + if (codeBase is not null) + { + currentDirectory = Path.GetDirectoryName(codeBase); } + } #endif - return currentDirectory; - } + return currentDirectory; + } - /// - /// Generates the full path to a random directory name in the temporary directory, following a naming pattern.. - /// - /// The pattern to use in creating the directory name, following standard - /// .NET string replacement tokens. - /// The full path to the random directory name in the temporary directory. - public static string GenerateRandomTempDirectoryName( + /// + /// Generates the full path to a random directory name in the temporary directory, following a naming pattern.. + /// + /// The pattern to use in creating the directory name, following standard + /// .NET string replacement tokens. + /// The full path to the random directory name in the temporary directory. + public static string GenerateRandomTempDirectoryName( #if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.CompositeFormat)] + [System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.CompositeFormat)] #endif - string directoryPattern) - { - string directoryName = string.Format(CultureInfo.InvariantCulture, directoryPattern, Guid.NewGuid().ToString("N")); - return Path.Combine(Path.GetTempPath(), directoryName); - } + string directoryPattern) + { + string directoryName = string.Format(CultureInfo.InvariantCulture, directoryPattern, Guid.NewGuid().ToString("N")); + return Path.Combine(Path.GetTempPath(), directoryName); } } diff --git a/dotnet/src/webdriver/Internal/IFindsElement.cs b/dotnet/src/webdriver/Internal/IFindsElement.cs index a43055f9c91bd..87fe809825266 100644 --- a/dotnet/src/webdriver/Internal/IFindsElement.cs +++ b/dotnet/src/webdriver/Internal/IFindsElement.cs @@ -19,27 +19,26 @@ using System.Collections.ObjectModel; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Defines the interface through which the user finds elements by a strategy and value. +/// +public interface IFindsElement { /// - /// Defines the interface through which the user finds elements by a strategy and value. + /// Finds the first element matching the specified value using the specified mechanism. /// - public interface IFindsElement - { - /// - /// Finds the first element matching the specified value using the specified mechanism. - /// - /// The mechanism to use when matching. - /// The value to match. - /// The first matching the criteria. - IWebElement FindElement(string mechanism, string value); + /// The mechanism to use when matching. + /// The value to match. + /// The first matching the criteria. + IWebElement FindElement(string mechanism, string value); - /// - /// Finds all elements matching the specified value using the specified mechanism. - /// - /// The mechanism to use when matching. - /// The value to match. - /// IWebElements matching the criteria. - ReadOnlyCollection FindElements(string mechanism, string value); - } + /// + /// Finds all elements matching the specified value using the specified mechanism. + /// + /// The mechanism to use when matching. + /// The value to match. + /// IWebElements matching the criteria. + ReadOnlyCollection FindElements(string mechanism, string value); } diff --git a/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs b/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs index 8c281818c4e0d..d001a13c9d8f0 100644 --- a/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs +++ b/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs @@ -19,16 +19,15 @@ using System.Collections.Generic; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Defines the interface through which the user can access the driver used to find an element. +/// +internal interface IHasCapabilitiesDictionary { /// - /// Defines the interface through which the user can access the driver used to find an element. + /// Gets the underlying IDictionary for a given set of capabilities. /// - internal interface IHasCapabilitiesDictionary - { - /// - /// Gets the underlying IDictionary for a given set of capabilities. - /// - IDictionary CapabilitiesDictionary { get; } - } + IDictionary CapabilitiesDictionary { get; } } diff --git a/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs b/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs index bc67fdd7c6a6b..2f46a8a96f20c 100644 --- a/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs +++ b/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs @@ -19,22 +19,21 @@ using System.Collections.Generic; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Defines the interface through which the framework can serialize an element to the wire protocol. +/// +internal interface IWebDriverObjectReference { /// - /// Defines the interface through which the framework can serialize an element to the wire protocol. + /// Gets the internal ID of the WebDriver object. /// - internal interface IWebDriverObjectReference - { - /// - /// Gets the internal ID of the WebDriver object. - /// - string ObjectReferenceId { get; } + string ObjectReferenceId { get; } - /// - /// Converts an object into an object that represents an element for the wire protocol. - /// - /// A that represents an element in the wire protocol. - Dictionary ToDictionary(); - } + /// + /// Converts an object into an object that represents an element for the wire protocol. + /// + /// A that represents an element in the wire protocol. + Dictionary ToDictionary(); } diff --git a/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs index b9f8ddb8789f8..d40653de2fbfe 100644 --- a/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs @@ -19,11 +19,10 @@ using System; -namespace OpenQA.Selenium.Internal.Logging -{ - /// - /// Represents a log handler that writes log events to the given text writer. - /// - [Obsolete("Use TextWriterHandler instead, will be removed in v4.32")] - public class ConsoleLogHandler() : TextWriterHandler(Console.Error); -} +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a log handler that writes log events to the given text writer. +/// +[Obsolete("Use TextWriterHandler instead, will be removed in v4.32")] +public class ConsoleLogHandler() : TextWriterHandler(Console.Error); diff --git a/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs b/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs index 125d9bcfe1002..7a1a3192c95ed 100644 --- a/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs @@ -20,107 +20,106 @@ using System; using System.IO; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a log handler that writes log events to a file. +/// +public class FileLogHandler : ILogHandler, IDisposable { - /// - /// Represents a log handler that writes log events to a file. - /// - public class FileLogHandler : ILogHandler, IDisposable - { - // performance trick to avoid expensive Enum.ToString() with fixed length - private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" }; - - private FileStream _fileStream; - private StreamWriter _streamWriter; - - private readonly object _lockObj = new object(); - private bool _isDisposed; - - /// - /// Initializes a new instance of the class with the specified file path. - /// - /// The path of the log file. - /// If is or . - public FileLogHandler(string filePath) - : this(filePath, overwrite: true) - { + // performance trick to avoid expensive Enum.ToString() with fixed length + private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" }; - } + private FileStream _fileStream; + private StreamWriter _streamWriter; - /// - /// Initializes a new instance of the class with the specified file path. - /// - /// The path of the log file. - /// Specifies whether the file should be overwritten if it exists on the disk. - /// If is or . - public FileLogHandler(string filePath, bool overwrite) - { - if (string.IsNullOrEmpty(filePath)) throw new ArgumentException("File log path cannot be null or empty.", nameof(filePath)); + private readonly object _lockObj = new object(); + private bool _isDisposed; - var directory = Path.GetDirectoryName(filePath); - if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } + /// + /// Initializes a new instance of the class with the specified file path. + /// + /// The path of the log file. + /// If is or . + public FileLogHandler(string filePath) + : this(filePath, overwrite: true) + { - var fileMode = overwrite ? FileMode.Create : FileMode.Append; + } - _fileStream = File.Open(filePath, fileMode, FileAccess.Write, FileShare.Read); - _streamWriter = new StreamWriter(_fileStream, System.Text.Encoding.UTF8) - { - AutoFlush = true - }; - } + /// + /// Initializes a new instance of the class with the specified file path. + /// + /// The path of the log file. + /// Specifies whether the file should be overwritten if it exists on the disk. + /// If is or . + public FileLogHandler(string filePath, bool overwrite) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentException("File log path cannot be null or empty.", nameof(filePath)); - /// - /// Handles a log event by writing it to the log file. - /// - /// The log event to handle. - public void Handle(LogEvent logEvent) + var directory = Path.GetDirectoryName(filePath); + if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory)) { - lock (_lockObj) - { - _streamWriter.WriteLine($"{logEvent.Timestamp:yyyy-MM-dd HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}"); - } + Directory.CreateDirectory(directory); } - /// - /// Disposes the file log handler and releases any resources used. - /// - public void Dispose() + var fileMode = overwrite ? FileMode.Create : FileMode.Append; + + _fileStream = File.Open(filePath, fileMode, FileAccess.Write, FileShare.Read); + _streamWriter = new StreamWriter(_fileStream, System.Text.Encoding.UTF8) { - Dispose(true); - GC.SuppressFinalize(this); - } + AutoFlush = true + }; + } - /// - /// Finalizes the file log handler instance. - /// - ~FileLogHandler() + /// + /// Handles a log event by writing it to the log file. + /// + /// The log event to handle. + public void Handle(LogEvent logEvent) + { + lock (_lockObj) { - Dispose(false); + _streamWriter.WriteLine($"{logEvent.Timestamp:yyyy-MM-dd HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}"); } + } + + /// + /// Disposes the file log handler and releases any resources used. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Finalizes the file log handler instance. + /// + ~FileLogHandler() + { + Dispose(false); + } - /// - /// Disposes the file log handler and releases any resources used. - /// - /// A flag indicating whether to dispose managed resources. - protected virtual void Dispose(bool disposing) + /// + /// Disposes the file log handler and releases any resources used. + /// + /// A flag indicating whether to dispose managed resources. + protected virtual void Dispose(bool disposing) + { + lock (_lockObj) { - lock (_lockObj) + if (!_isDisposed) { - if (!_isDisposed) + if (disposing) { - if (disposing) - { - _streamWriter?.Dispose(); - _streamWriter = null!; - _fileStream?.Dispose(); - _fileStream = null!; - } - - _isDisposed = true; + _streamWriter?.Dispose(); + _streamWriter = null!; + _fileStream?.Dispose(); + _fileStream = null!; } + + _isDisposed = true; } } } diff --git a/dotnet/src/webdriver/Internal/Logging/ILogContext.cs b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs index d1cc52cae4509..6c116617db85c 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogContext.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs @@ -19,74 +19,73 @@ using System; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. +/// +public interface ILogContext : IDisposable { /// - /// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. + /// Creates a new logging context. /// - public interface ILogContext : IDisposable - { - /// - /// Creates a new logging context. - /// - /// A new instance of . - ILogContext CreateContext(); + /// A new instance of . + ILogContext CreateContext(); - /// - /// Creates a new logging context with the specified minimum log level. - /// - /// The minimum log level for the new context. - /// A new instance of with the specified minimum log level. - ILogContext CreateContext(LogEventLevel minimumLevel); + /// + /// Creates a new logging context with the specified minimum log level. + /// + /// The minimum log level for the new context. + /// A new instance of with the specified minimum log level. + ILogContext CreateContext(LogEventLevel minimumLevel); - /// - /// Gets a logger for the specified type. - /// - /// The type for which to retrieve the logger. - /// An instance of for the specified type. - internal ILogger GetLogger(); + /// + /// Gets a logger for the specified type. + /// + /// The type for which to retrieve the logger. + /// An instance of for the specified type. + internal ILogger GetLogger(); - /// - /// Gets a logger for the specified type. - /// - /// The type for which to retrieve the logger. - /// An instance of for the specified type. - internal ILogger GetLogger(Type type); + /// + /// Gets a logger for the specified type. + /// + /// The type for which to retrieve the logger. + /// An instance of for the specified type. + internal ILogger GetLogger(Type type); - /// - /// Checks whether logs emitting is enabled for a logger and a log event level. - /// - /// The specified logger instance to be checked. - /// The specified log event level to be checked. - /// true if log messages emmiting is enabled for the specified logger and log event level, otherwise false. - internal bool IsEnabled(ILogger logger, LogEventLevel level); + /// + /// Checks whether logs emitting is enabled for a logger and a log event level. + /// + /// The specified logger instance to be checked. + /// The specified log event level to be checked. + /// true if log messages emmiting is enabled for the specified logger and log event level, otherwise false. + internal bool IsEnabled(ILogger logger, LogEventLevel level); - /// - /// Emits a log message using the specified logger, log level, and message. - /// - /// The logger to emit the log message. - /// The log level of the message. - /// The log message. - internal void EmitMessage(ILogger logger, LogEventLevel level, string message); + /// + /// Emits a log message using the specified logger, log level, and message. + /// + /// The logger to emit the log message. + /// The log level of the message. + /// The log message. + internal void EmitMessage(ILogger logger, LogEventLevel level, string message); - /// - /// Sets the minimum log level for the current context. - /// - /// The minimum log level. - /// The current instance of with the minimum log level set. - ILogContext SetLevel(LogEventLevel level); + /// + /// Sets the minimum log level for the current context. + /// + /// The minimum log level. + /// The current instance of with the minimum log level set. + ILogContext SetLevel(LogEventLevel level); - /// - /// Sets the minimum log level for the specified type in the current context. - /// - /// The type for which to set the minimum log level. - /// The minimum log level. - /// The current instance of with the minimum log level set for the specified type. - ILogContext SetLevel(Type issuer, LogEventLevel level); + /// + /// Sets the minimum log level for the specified type in the current context. + /// + /// The type for which to set the minimum log level. + /// The minimum log level. + /// The current instance of with the minimum log level set for the specified type. + ILogContext SetLevel(Type issuer, LogEventLevel level); - /// - /// Gets a list of log handlers. - /// - ILogHandlerList Handlers { get; } - } + /// + /// Gets a list of log handlers. + /// + ILogHandlerList Handlers { get; } } diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs index 59c8133d007b3..cf27f6dafe2b2 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs @@ -17,17 +17,16 @@ // under the License. // -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a log handler that handles log events. +/// +public interface ILogHandler { /// - /// Represents a log handler that handles log events. + /// Handles a log event. /// - public interface ILogHandler - { - /// - /// Handles a log event. - /// - /// The log event to handle. - void Handle(LogEvent logEvent); - } + /// The log event to handle. + void Handle(LogEvent logEvent); } diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs index f27b10a53b416..3b068b29e7869 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs @@ -19,31 +19,30 @@ using System.Collections.Generic; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a list of log handlers. +/// +public interface ILogHandlerList : IEnumerable { /// - /// Represents a list of log handlers. + /// Adds a log handler to the list. /// - public interface ILogHandlerList : IEnumerable - { - /// - /// Adds a log handler to the list. - /// - /// The log handler to add. - /// The log context. - ILogContext Add(ILogHandler handler); + /// The log handler to add. + /// The log context. + ILogContext Add(ILogHandler handler); - /// - /// Removes a log handler from the list. - /// - /// The log handler to remove. - /// The log context. - ILogContext Remove(ILogHandler handler); + /// + /// Removes a log handler from the list. + /// + /// The log handler to remove. + /// The log context. + ILogContext Remove(ILogHandler handler); - /// - /// Clears all log handlers from the list. - /// - /// The log context. - ILogContext Clear(); - } + /// + /// Clears all log handlers from the list. + /// + /// The log context. + ILogContext Clear(); } diff --git a/dotnet/src/webdriver/Internal/Logging/ILogger.cs b/dotnet/src/webdriver/Internal/Logging/ILogger.cs index a92a43e0d8445..148836a6d2629 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogger.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogger.cs @@ -19,58 +19,57 @@ using System; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Defines the interface through which log messages are emitted. +/// +internal interface ILogger { /// - /// Defines the interface through which log messages are emitted. + /// Writes a trace-level log message. /// - internal interface ILogger - { - /// - /// Writes a trace-level log message. - /// - /// The log message. - void Trace(string message); + /// The log message. + void Trace(string message); - /// - /// Writes a debug-level log message. - /// - /// The log message. - void Debug(string message); + /// + /// Writes a debug-level log message. + /// + /// The log message. + void Debug(string message); - /// - /// Writes an info-level log message. - /// - /// The log message. - void Info(string message); + /// + /// Writes an info-level log message. + /// + /// The log message. + void Info(string message); - /// - /// Writes a warning-level log message. - /// - /// The log message. - void Warn(string message); + /// + /// Writes a warning-level log message. + /// + /// The log message. + void Warn(string message); - /// - /// Writes an error-level log message. - /// - /// The log message. - void Error(string message); + /// + /// Writes an error-level log message. + /// + /// The log message. + void Error(string message); - /// - /// Gets or sets the log event level. - /// - LogEventLevel Level { get; set; } + /// + /// Gets or sets the log event level. + /// + LogEventLevel Level { get; set; } - /// - /// Gets the type of the logger issuer. - /// - Type Issuer { get; } + /// + /// Gets the type of the logger issuer. + /// + Type Issuer { get; } - /// - /// Checks whether logs emitting is enabled for this logger and a log event level. - /// - /// The specified log event level to be checked. - /// true if log messages emmiting is enabled for the specified log event level, otherwise false. - bool IsEnabled(LogEventLevel level); - } + /// + /// Checks whether logs emitting is enabled for this logger and a log event level. + /// + /// The specified log event level to be checked. + /// true if log messages emmiting is enabled for the specified log event level, otherwise false. + bool IsEnabled(LogEventLevel level); } diff --git a/dotnet/src/webdriver/Internal/Logging/Log.cs b/dotnet/src/webdriver/Internal/Logging/Log.cs index c69e22fe04316..fd46763711731 100644 --- a/dotnet/src/webdriver/Internal/Logging/Log.cs +++ b/dotnet/src/webdriver/Internal/Logging/Log.cs @@ -20,103 +20,102 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Provides context aware logging functionality for the Selenium WebDriver. +/// +/// +/// +/// Use the following code to enable logging to console: +/// +/// Log.SetMinimumLevel(LogEventLevel.Debug)).WithHandler(new ConsoleLogHandler()); +/// +/// +/// Or enable it per limited execution scope: +/// +/// using (var ctx = Log.CreateContext(LogEventLevel.Trace)) +/// { +/// // do something +/// } +/// +/// +public static class Log { + private static readonly LogContextManager _logContextManager = new LogContextManager(); + /// - /// Provides context aware logging functionality for the Selenium WebDriver. + /// Creates a new log context with the current context properties and the specified minimum log event level. /// - /// - /// - /// Use the following code to enable logging to console: - /// - /// Log.SetMinimumLevel(LogEventLevel.Debug)).WithHandler(new ConsoleLogHandler()); - /// - /// - /// Or enable it per limited execution scope: - /// - /// using (var ctx = Log.CreateContext(LogEventLevel.Trace)) - /// { - /// // do something - /// } - /// - /// - public static class Log + /// The created log context. + public static ILogContext CreateContext() { - private static readonly LogContextManager _logContextManager = new LogContextManager(); - - /// - /// Creates a new log context with the current context properties and the specified minimum log event level. - /// - /// The created log context. - public static ILogContext CreateContext() - { - return _logContextManager.CurrentContext.CreateContext(); - } - - /// - /// Creates a new log context with with the current context properties and the specified minimum log event level. - /// - /// The minimum log event level. - /// The created log context. - public static ILogContext CreateContext(LogEventLevel minimumLevel) - { - return _logContextManager.CurrentContext.CreateContext(minimumLevel); - } + return _logContextManager.CurrentContext.CreateContext(); + } - /// - /// Gets or sets the current log context. - /// - [AllowNull] - internal static ILogContext CurrentContext - { - get => _logContextManager.CurrentContext; - set => _logContextManager.CurrentContext = value; - } + /// + /// Creates a new log context with with the current context properties and the specified minimum log event level. + /// + /// The minimum log event level. + /// The created log context. + public static ILogContext CreateContext(LogEventLevel minimumLevel) + { + return _logContextManager.CurrentContext.CreateContext(minimumLevel); + } - /// - /// Gets a logger for the specified type. - /// - /// The type to get the logger for. - /// The logger. - internal static ILogger GetLogger() - { - return _logContextManager.CurrentContext.GetLogger(); - } + /// + /// Gets or sets the current log context. + /// + [AllowNull] + internal static ILogContext CurrentContext + { + get => _logContextManager.CurrentContext; + set => _logContextManager.CurrentContext = value; + } - /// - /// Gets a logger for the specified type. - /// - /// The type to get the logger for. - /// The logger. - internal static ILogger GetLogger(Type type) - { - return _logContextManager.CurrentContext.GetLogger(type); - } + /// + /// Gets a logger for the specified type. + /// + /// The type to get the logger for. + /// The logger. + internal static ILogger GetLogger() + { + return _logContextManager.CurrentContext.GetLogger(); + } - /// - /// Sets the minimum log event level for the current log context. - /// - /// The minimum log event level. - /// The current log context. - public static ILogContext SetLevel(LogEventLevel level) - { - return _logContextManager.CurrentContext.SetLevel(level); - } + /// + /// Gets a logger for the specified type. + /// + /// The type to get the logger for. + /// The logger. + internal static ILogger GetLogger(Type type) + { + return _logContextManager.CurrentContext.GetLogger(type); + } - /// - /// Sets the minimum log event level for the specified issuer in the current log context. - /// - /// The issuer type. - /// The minimum log event level. - /// The current log context. - public static ILogContext SetLevel(Type issuer, LogEventLevel level) - { - return _logContextManager.CurrentContext.SetLevel(issuer, level); - } + /// + /// Sets the minimum log event level for the current log context. + /// + /// The minimum log event level. + /// The current log context. + public static ILogContext SetLevel(LogEventLevel level) + { + return _logContextManager.CurrentContext.SetLevel(level); + } - /// - /// Gets a list of log handlers for the current log context. - /// - public static ILogHandlerList Handlers => _logContextManager.CurrentContext.Handlers; + /// + /// Sets the minimum log event level for the specified issuer in the current log context. + /// + /// The issuer type. + /// The minimum log event level. + /// The current log context. + public static ILogContext SetLevel(Type issuer, LogEventLevel level) + { + return _logContextManager.CurrentContext.SetLevel(issuer, level); } + + /// + /// Gets a list of log handlers for the current log context. + /// + public static ILogHandlerList Handlers => _logContextManager.CurrentContext.Handlers; } diff --git a/dotnet/src/webdriver/Internal/Logging/LogContext.cs b/dotnet/src/webdriver/Internal/Logging/LogContext.cs index 612c901f94172..5bb8ed5359e98 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogContext.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogContext.cs @@ -23,150 +23,149 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. +/// +/// +internal sealed class LogContext : ILogContext { - /// - /// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. - /// - /// - internal sealed class LogContext : ILogContext - { - private ConcurrentDictionary? _loggers; + private ConcurrentDictionary? _loggers; - private LogEventLevel _level; + private LogEventLevel _level; - private readonly ILogContext? _parentLogContext; + private readonly ILogContext? _parentLogContext; - private readonly Lazy _lazyLogHandlerList; + private readonly Lazy _lazyLogHandlerList; - public LogContext(LogEventLevel level, ILogContext? parentLogContext, ConcurrentDictionary? loggers, IEnumerable? handlers) - { - _level = level; + public LogContext(LogEventLevel level, ILogContext? parentLogContext, ConcurrentDictionary? loggers, IEnumerable? handlers) + { + _level = level; - _parentLogContext = parentLogContext; + _parentLogContext = parentLogContext; - _loggers = CloneLoggers(loggers, level); + _loggers = CloneLoggers(loggers, level); - if (handlers is not null) - { - _lazyLogHandlerList = new Lazy(() => new LogHandlerList(this, handlers)); - } - else - { - _lazyLogHandlerList = new Lazy(() => new LogHandlerList(this)); - } + if (handlers is not null) + { + _lazyLogHandlerList = new Lazy(() => new LogHandlerList(this, handlers)); } - - public ILogContext CreateContext() + else { - return CreateContext(_level); + _lazyLogHandlerList = new Lazy(() => new LogHandlerList(this)); } + } - public ILogContext CreateContext(LogEventLevel minimumLevel) - { - ConcurrentDictionary? loggers = CloneLoggers(_loggers, minimumLevel); + public ILogContext CreateContext() + { + return CreateContext(_level); + } - var context = new LogContext(minimumLevel, this, loggers, Handlers); + public ILogContext CreateContext(LogEventLevel minimumLevel) + { + ConcurrentDictionary? loggers = CloneLoggers(_loggers, minimumLevel); - Log.CurrentContext = context; + var context = new LogContext(minimumLevel, this, loggers, Handlers); - return context; - } + Log.CurrentContext = context; - public ILogger GetLogger() + return context; + } + + public ILogger GetLogger() + { + return GetLogger(typeof(T)); + } + + public ILogger GetLogger(Type type) + { + if (type == null) { - return GetLogger(typeof(T)); + throw new ArgumentNullException(nameof(type)); } - public ILogger GetLogger(Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } + _loggers ??= new ConcurrentDictionary(); - _loggers ??= new ConcurrentDictionary(); + return _loggers.GetOrAdd(type, type => new Logger(type, _level)); + } - return _loggers.GetOrAdd(type, type => new Logger(type, _level)); - } + public bool IsEnabled(ILogger logger, LogEventLevel level) + { + return Handlers != null && level >= _level && (_loggers?.TryGetValue(logger.Issuer, out var loggerEntry) != true || level >= loggerEntry?.Level); + } - public bool IsEnabled(ILogger logger, LogEventLevel level) + public void EmitMessage(ILogger logger, LogEventLevel level, string message) + { + if (IsEnabled(logger, level)) { - return Handlers != null && level >= _level && (_loggers?.TryGetValue(logger.Issuer, out var loggerEntry) != true || level >= loggerEntry?.Level); - } + var logEvent = new LogEvent(logger.Issuer, DateTimeOffset.Now, level, message); - public void EmitMessage(ILogger logger, LogEventLevel level, string message) - { - if (IsEnabled(logger, level)) + foreach (var handler in Handlers) { - var logEvent = new LogEvent(logger.Issuer, DateTimeOffset.Now, level, message); - - foreach (var handler in Handlers) - { - handler.Handle(logEvent); - } + handler.Handle(logEvent); } } + } - public ILogContext SetLevel(LogEventLevel level) - { - _level = level; + public ILogContext SetLevel(LogEventLevel level) + { + _level = level; - if (_loggers != null) + if (_loggers != null) + { + foreach (var logger in _loggers.Values) { - foreach (var logger in _loggers.Values) - { - logger.Level = _level; - } + logger.Level = _level; } - - return this; } - public ILogContext SetLevel(Type issuer, LogEventLevel level) - { - GetLogger(issuer).Level = level; + return this; + } - return this; - } + public ILogContext SetLevel(Type issuer, LogEventLevel level) + { + GetLogger(issuer).Level = level; - public ILogHandlerList Handlers => _lazyLogHandlerList.Value; + return this; + } + + public ILogHandlerList Handlers => _lazyLogHandlerList.Value; - public void Dispose() + public void Dispose() + { + // Dispose log handlers associated with this context + // if they are hot handled by parent context + if (Handlers != null && _parentLogContext != null && _parentLogContext.Handlers != null) { - // Dispose log handlers associated with this context - // if they are hot handled by parent context - if (Handlers != null && _parentLogContext != null && _parentLogContext.Handlers != null) + foreach (var logHandler in Handlers) { - foreach (var logHandler in Handlers) + if (!_parentLogContext.Handlers.Contains(logHandler)) { - if (!_parentLogContext.Handlers.Contains(logHandler)) - { - (logHandler as IDisposable)?.Dispose(); - } + (logHandler as IDisposable)?.Dispose(); } } - - Log.CurrentContext = _parentLogContext; } - [return: NotNullIfNotNull(nameof(loggers))] - private static ConcurrentDictionary? CloneLoggers(ConcurrentDictionary? loggers, LogEventLevel minimumLevel) - { - if (loggers is null) - { - return null; - } + Log.CurrentContext = _parentLogContext; + } - var cloned = new Dictionary(loggers.Count); + [return: NotNullIfNotNull(nameof(loggers))] + private static ConcurrentDictionary? CloneLoggers(ConcurrentDictionary? loggers, LogEventLevel minimumLevel) + { + if (loggers is null) + { + return null; + } - foreach (KeyValuePair logger in loggers) - { - var clonedLogger = new Logger(logger.Value.Issuer, minimumLevel); - cloned.Add(logger.Key, clonedLogger); - } + var cloned = new Dictionary(loggers.Count); - return new ConcurrentDictionary(cloned); + foreach (KeyValuePair logger in loggers) + { + var clonedLogger = new Logger(logger.Value.Issuer, minimumLevel); + cloned.Add(logger.Key, clonedLogger); } + + return new ConcurrentDictionary(cloned); } } diff --git a/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs index 247f55eb01d03..2a86b7ecdbb68 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs @@ -21,26 +21,25 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +internal class LogContextManager { - internal class LogContextManager - { - private readonly AsyncLocal _currentAmbientLogContext = new(); + private readonly AsyncLocal _currentAmbientLogContext = new(); - public LogContextManager() - { - var defaulLogHandler = new TextWriterHandler(Console.Error); + public LogContextManager() + { + var defaulLogHandler = new TextWriterHandler(Console.Error); - GlobalContext = new LogContext(LogEventLevel.Info, null, null, [defaulLogHandler]); - } + GlobalContext = new LogContext(LogEventLevel.Info, null, null, [defaulLogHandler]); + } - public ILogContext GlobalContext { get; } + public ILogContext GlobalContext { get; } - [AllowNull] - public ILogContext CurrentContext - { - get => _currentAmbientLogContext.Value ?? GlobalContext; - set => _currentAmbientLogContext.Value = value; - } + [AllowNull] + public ILogContext CurrentContext + { + get => _currentAmbientLogContext.Value ?? GlobalContext; + set => _currentAmbientLogContext.Value = value; } } diff --git a/dotnet/src/webdriver/Internal/Logging/LogEvent.cs b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs index 7a63c9dd0ba0b..9c5568bf61f6f 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogEvent.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs @@ -19,47 +19,46 @@ using System; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a log event in the Selenium WebDriver internal logging system. +/// +public sealed class LogEvent { /// - /// Represents a log event in the Selenium WebDriver internal logging system. + /// Initializes a new instance of the class. /// - public sealed class LogEvent + /// The type that issued the log event. + /// The timestamp of the log event. + /// The level of the log event. + /// The message of the log event. + /// If is . + public LogEvent(Type issuedBy, DateTimeOffset timestamp, LogEventLevel level, string message) { - /// - /// Initializes a new instance of the class. - /// - /// The type that issued the log event. - /// The timestamp of the log event. - /// The level of the log event. - /// The message of the log event. - /// If is . - public LogEvent(Type issuedBy, DateTimeOffset timestamp, LogEventLevel level, string message) - { - IssuedBy = issuedBy ?? throw new ArgumentNullException(nameof(issuedBy)); - Timestamp = timestamp; - Level = level; - Message = message; - } + IssuedBy = issuedBy ?? throw new ArgumentNullException(nameof(issuedBy)); + Timestamp = timestamp; + Level = level; + Message = message; + } - /// - /// Gets the type that issued the log event. - /// - public Type IssuedBy { get; } + /// + /// Gets the type that issued the log event. + /// + public Type IssuedBy { get; } - /// - /// Gets the timestamp of the log event. - /// - public DateTimeOffset Timestamp { get; } + /// + /// Gets the timestamp of the log event. + /// + public DateTimeOffset Timestamp { get; } - /// - /// Gets the level of the log event. - /// - public LogEventLevel Level { get; } + /// + /// Gets the level of the log event. + /// + public LogEventLevel Level { get; } - /// - /// Gets the message of the log event. - /// - public string Message { get; } - } + /// + /// Gets the message of the log event. + /// + public string Message { get; } } diff --git a/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs index af8b728f0d326..61a1477df98d3 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs @@ -17,41 +17,40 @@ // under the License. // -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Defines the levels of logging events. +/// +public enum LogEventLevel { /// - /// Defines the levels of logging events. + /// The most detailed log events. /// - public enum LogEventLevel - { - /// - /// The most detailed log events. - /// - Trace = 0, + Trace = 0, - /// - /// Log events that are useful for debugging purposes. - /// - Debug = 1, + /// + /// Log events that are useful for debugging purposes. + /// + Debug = 1, - /// - /// Log events that provide general information about the application's operation. - /// - Info = 2, + /// + /// Log events that provide general information about the application's operation. + /// + Info = 2, - /// - /// Log events that indicate a potential problem or a non-critical error. - /// - Warn = 3, + /// + /// Log events that indicate a potential problem or a non-critical error. + /// + Warn = 3, - /// - /// Log events that indicate a serious error or a failure that requires immediate attention. - /// - Error = 4, + /// + /// Log events that indicate a serious error or a failure that requires immediate attention. + /// + Error = 4, - /// - /// No log events. - /// - None = 5 - } + /// + /// No log events. + /// + None = 5 } diff --git a/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs index fae66983acda3..7030981fe17e2 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs @@ -19,46 +19,45 @@ using System.Collections.Generic; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a list of log handlers. +/// +/// +internal sealed class LogHandlerList : List, ILogHandlerList { - /// - /// Represents a list of log handlers. - /// - /// - internal sealed class LogHandlerList : List, ILogHandlerList - { - private readonly ILogContext _logContext; + private readonly ILogContext _logContext; - public LogHandlerList(ILogContext logContext) - { - _logContext = logContext; - } + public LogHandlerList(ILogContext logContext) + { + _logContext = logContext; + } - public LogHandlerList(ILogContext logContext, IEnumerable handlers) - : base(handlers) - { - _logContext = logContext; - } + public LogHandlerList(ILogContext logContext, IEnumerable handlers) + : base(handlers) + { + _logContext = logContext; + } - public new ILogContext Add(ILogHandler handler) - { - base.Add(handler); + public new ILogContext Add(ILogHandler handler) + { + base.Add(handler); - return _logContext; - } + return _logContext; + } - public new ILogContext Remove(ILogHandler handler) - { - base.Remove(handler); + public new ILogContext Remove(ILogHandler handler) + { + base.Remove(handler); - return _logContext; - } + return _logContext; + } - public new ILogContext Clear() - { - base.Clear(); + public new ILogContext Clear() + { + base.Clear(); - return _logContext; - } + return _logContext; } } diff --git a/dotnet/src/webdriver/Internal/Logging/Logger.cs b/dotnet/src/webdriver/Internal/Logging/Logger.cs index f3e62cfa0ab8b..3910763fc330b 100644 --- a/dotnet/src/webdriver/Internal/Logging/Logger.cs +++ b/dotnet/src/webdriver/Internal/Logging/Logger.cs @@ -19,57 +19,56 @@ using System; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// The implementation of the interface through which log messages are emitted. +/// +/// +internal sealed class Logger : ILogger { - /// - /// The implementation of the interface through which log messages are emitted. - /// - /// - internal sealed class Logger : ILogger + public Logger(Type issuer, LogEventLevel level) { - public Logger(Type issuer, LogEventLevel level) - { - Issuer = issuer; - Level = level; - } + Issuer = issuer; + Level = level; + } - public LogEventLevel Level { get; set; } + public LogEventLevel Level { get; set; } - public Type Issuer { get; internal set; } + public Type Issuer { get; internal set; } - public void Trace(string message) - { - LogMessage(LogEventLevel.Trace, message); - } + public void Trace(string message) + { + LogMessage(LogEventLevel.Trace, message); + } - public void Debug(string message) - { - LogMessage(LogEventLevel.Debug, message); - } + public void Debug(string message) + { + LogMessage(LogEventLevel.Debug, message); + } - public void Info(string message) - { - LogMessage(LogEventLevel.Info, message); - } + public void Info(string message) + { + LogMessage(LogEventLevel.Info, message); + } - public void Warn(string message) - { - LogMessage(LogEventLevel.Warn, message); - } + public void Warn(string message) + { + LogMessage(LogEventLevel.Warn, message); + } - public void Error(string message) - { - LogMessage(LogEventLevel.Error, message); - } + public void Error(string message) + { + LogMessage(LogEventLevel.Error, message); + } - public bool IsEnabled(LogEventLevel level) - { - return Log.CurrentContext.IsEnabled(this, level); - } + public bool IsEnabled(LogEventLevel level) + { + return Log.CurrentContext.IsEnabled(this, level); + } - private void LogMessage(LogEventLevel level, string message) - { - Log.CurrentContext.EmitMessage(this, level, message); - } + private void LogMessage(LogEventLevel level, string message) + { + Log.CurrentContext.EmitMessage(this, level, message); } } diff --git a/dotnet/src/webdriver/Internal/Logging/TextWriterHandler.cs b/dotnet/src/webdriver/Internal/Logging/TextWriterHandler.cs index 611bbacff0933..aff4047522155 100644 --- a/dotnet/src/webdriver/Internal/Logging/TextWriterHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/TextWriterHandler.cs @@ -19,23 +19,22 @@ using System.IO; -namespace OpenQA.Selenium.Internal.Logging +namespace OpenQA.Selenium.Internal.Logging; + +/// +/// Represents a log handler that writes log events to the given text writer. +/// +public class TextWriterHandler(TextWriter writer) : ILogHandler { + // performance trick to avoid expensive Enum.ToString() with fixed length + private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" }; + /// - /// Represents a log handler that writes log events to the given text writer. + /// Handles a log event by writing it to the text writer. /// - public class TextWriterHandler(TextWriter writer) : ILogHandler + /// The log event to handle. + public void Handle(LogEvent logEvent) { - // performance trick to avoid expensive Enum.ToString() with fixed length - private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" }; - - /// - /// Handles a log event by writing it to the text writer. - /// - /// The log event to handle. - public void Handle(LogEvent logEvent) - { - writer.WriteLine($"{logEvent.Timestamp:HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}"); - } + writer.WriteLine($"{logEvent.Timestamp:HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}"); } } diff --git a/dotnet/src/webdriver/Internal/NullableAttributes.cs b/dotnet/src/webdriver/Internal/NullableAttributes.cs index bbb123a0a86e2..a5ecaf4da4e49 100644 --- a/dotnet/src/webdriver/Internal/NullableAttributes.cs +++ b/dotnet/src/webdriver/Internal/NullableAttributes.cs @@ -23,146 +23,145 @@ // Following polyfill guidance explained here https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#targetframeworks // Original code in https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs -namespace System.Diagnostics.CodeAnalysis +namespace System.Diagnostics.CodeAnalysis; + +/// Specifies that null is allowed as an input even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class AllowNullAttribute : Attribute +{ } + +/// Specifies that null is disallowed as an input even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class DisallowNullAttribute : Attribute +{ } + +/// Specifies that an output may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class MaybeNullAttribute : Attribute +{ } + +/// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class NotNullAttribute : Attribute +{ } + +/// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class MaybeNullWhenAttribute : Attribute { - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } +/// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } +/// Specifies that the output will be non-null if the named parameter is non-null. +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +internal sealed class NotNullIfNotNullAttribute : Attribute +{ + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } +} - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { } +/// Applied to a method that will never return under any circumstance. +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class DoesNotReturnAttribute : Attribute +{ } - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } +/// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class DoesNotReturnIfAttribute : Attribute +{ + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } +} + +/// Specifies that the method or property will ensure that the listed field and property members have not-null values. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullAttribute : Attribute +{ + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } +} - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute +/// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated field or property member will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } + ReturnValue = returnValue; + Members = new[] { member }; } - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated field and property members will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated field or property member will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated field and property members will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } + ReturnValue = returnValue; + Members = members; } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } } #endif diff --git a/dotnet/src/webdriver/Internal/PortUtilities.cs b/dotnet/src/webdriver/Internal/PortUtilities.cs index 594b7cdf9f297..ba80314622a5f 100644 --- a/dotnet/src/webdriver/Internal/PortUtilities.cs +++ b/dotnet/src/webdriver/Internal/PortUtilities.cs @@ -20,37 +20,36 @@ using System.Net; using System.Net.Sockets; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Encapsulates methods for working with ports. +/// +public static class PortUtilities { /// - /// Encapsulates methods for working with ports. + /// Finds a random, free port to be listened on. /// - public static class PortUtilities + /// A random, free port to be listened on. + public static int FindFreePort() { - /// - /// Finds a random, free port to be listened on. - /// - /// A random, free port to be listened on. - public static int FindFreePort() + // Locate a free port on the local machine by binding a socket to + // an IPEndPoint using IPAddress.Any and port 0. The socket will + // select a free port. + int listeningPort = 0; + Socket portSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + try { - // Locate a free port on the local machine by binding a socket to - // an IPEndPoint using IPAddress.Any and port 0. The socket will - // select a free port. - int listeningPort = 0; - Socket portSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - try - { - IPEndPoint socketEndPoint = new IPEndPoint(IPAddress.Any, 0); - portSocket.Bind(socketEndPoint); - socketEndPoint = (IPEndPoint)portSocket.LocalEndPoint!; - listeningPort = socketEndPoint.Port; - } - finally - { - portSocket.Close(); - } - - return listeningPort; + IPEndPoint socketEndPoint = new IPEndPoint(IPAddress.Any, 0); + portSocket.Bind(socketEndPoint); + socketEndPoint = (IPEndPoint)portSocket.LocalEndPoint!; + listeningPort = socketEndPoint.Port; } + finally + { + portSocket.Close(); + } + + return listeningPort; } } diff --git a/dotnet/src/webdriver/Internal/ResourceUtilities.cs b/dotnet/src/webdriver/Internal/ResourceUtilities.cs index 4087725b951d8..06c04389e5e92 100644 --- a/dotnet/src/webdriver/Internal/ResourceUtilities.cs +++ b/dotnet/src/webdriver/Internal/ResourceUtilities.cs @@ -22,114 +22,113 @@ using System.Reflection; using System.Runtime.InteropServices; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Encapsulates methods for finding and extracting WebDriver resources. +/// +internal static class ResourceUtilities { + private static string? productVersion; + private static string? platformFamily; + /// - /// Encapsulates methods for finding and extracting WebDriver resources. + /// Gets a string representing the informational version of the Selenium product. /// - internal static class ResourceUtilities + public static string ProductVersion { - private static string? productVersion; - private static string? platformFamily; - - /// - /// Gets a string representing the informational version of the Selenium product. - /// - public static string ProductVersion + get { - get + if (productVersion == null) { - if (productVersion == null) + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + var assemblyInformationalVersionAttribute = executingAssembly.GetCustomAttribute(); + if (assemblyInformationalVersionAttribute == null) { - Assembly executingAssembly = Assembly.GetExecutingAssembly(); - var assemblyInformationalVersionAttribute = executingAssembly.GetCustomAttribute(); - if (assemblyInformationalVersionAttribute == null) - { - productVersion = "Unknown"; - } - else - { - productVersion = assemblyInformationalVersionAttribute.InformationalVersion; - } + productVersion = "Unknown"; + } + else + { + productVersion = assemblyInformationalVersionAttribute.InformationalVersion; } - - return productVersion; } + + return productVersion; } + } - /// - /// Gets a string representing the platform family on which the Selenium assembly is executing. - /// - public static string PlatformFamily => platformFamily ??= GetPlatformString(); + /// + /// Gets a string representing the platform family on which the Selenium assembly is executing. + /// + public static string PlatformFamily => platformFamily ??= GetPlatformString(); - /// - /// Gets a that contains the resource to use. - /// - /// A file name in the file system containing the resource to use. - /// A string representing the resource name embedded in the - /// executing assembly, if it is not found in the file system. - /// A Stream from which the resource can be read. - /// Thrown if neither the file nor the embedded resource can be found. - /// - /// The GetResourceStream method searches for the specified resource using the following - /// algorithm: - /// - /// - /// In the same directory as the calling assembly. - /// In the full path specified by the argument. - /// Inside the calling assembly as an embedded resource. - /// - /// - /// - public static Stream GetResourceStream(string fileName, string resourceId) + /// + /// Gets a that contains the resource to use. + /// + /// A file name in the file system containing the resource to use. + /// A string representing the resource name embedded in the + /// executing assembly, if it is not found in the file system. + /// A Stream from which the resource can be read. + /// Thrown if neither the file nor the embedded resource can be found. + /// + /// The GetResourceStream method searches for the specified resource using the following + /// algorithm: + /// + /// + /// In the same directory as the calling assembly. + /// In the full path specified by the argument. + /// Inside the calling assembly as an embedded resource. + /// + /// + /// + public static Stream GetResourceStream(string fileName, string resourceId) + { + Stream? resourceStream; + string resourceFilePath = Path.Combine(FileUtilities.GetCurrentDirectory(), Path.GetFileName(fileName)); + if (File.Exists(resourceFilePath)) { - Stream? resourceStream; - string resourceFilePath = Path.Combine(FileUtilities.GetCurrentDirectory(), Path.GetFileName(fileName)); - if (File.Exists(resourceFilePath)) - { - resourceStream = new FileStream(resourceFilePath, FileMode.Open, FileAccess.Read); - } - else if (File.Exists(fileName)) + resourceStream = new FileStream(resourceFilePath, FileMode.Open, FileAccess.Read); + } + else if (File.Exists(fileName)) + { + resourceStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + } + else + { + if (string.IsNullOrEmpty(resourceId)) { - resourceStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + throw new WebDriverException("The file specified does not exist, and you have specified no internal resource ID"); } - else - { - if (string.IsNullOrEmpty(resourceId)) - { - throw new WebDriverException("The file specified does not exist, and you have specified no internal resource ID"); - } - Assembly executingAssembly = Assembly.GetExecutingAssembly(); - resourceStream = executingAssembly.GetManifestResourceStream(resourceId); - } - - if (resourceStream == null) - { - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Cannot find a file named '{0}' or an embedded resource with the id '{1}'.", resourceFilePath, resourceId)); - } + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + resourceStream = executingAssembly.GetManifestResourceStream(resourceId); + } - return resourceStream; + if (resourceStream == null) + { + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "Cannot find a file named '{0}' or an embedded resource with the id '{1}'.", resourceFilePath, resourceId)); } - private static string GetPlatformString() + return resourceStream; + } + + private static string GetPlatformString() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "windows"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return "linux"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "mac"; - } - else - { - return "unknown"; - } + return "windows"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "linux"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return "mac"; + } + else + { + return "unknown"; } } } diff --git a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs index 0c7e719fcbed9..a5860a490bf24 100644 --- a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs +++ b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs @@ -22,119 +22,118 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Converts the response to JSON +/// +internal class ResponseValueJsonConverter : JsonConverter { - /// - /// Converts the response to JSON - /// - internal class ResponseValueJsonConverter : JsonConverter + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return ProcessReadToken(ref reader, options); + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + switch (value) { - return ProcessReadToken(ref reader, options); + case null: + writer.WriteNullValue(); + break; + case Enum: + writer.WriteNumberValue(Convert.ToInt64(value)); + break; + case IEnumerable list: + writer.WriteStartArray(); + foreach (var item in list) + { + Write(writer, item, options); + } + writer.WriteEndArray(); + break; + case IDictionary dictionary: + writer.WriteStartObject(); + foreach (var pair in dictionary) + { + writer.WritePropertyName(pair.Key); + Write(writer, pair.Value, options); + } + writer.WriteEndObject(); + break; + case object obj: + JsonSerializer.Serialize(writer, obj, options.GetTypeInfo(obj.GetType())); + break; } + } + + private static object? ProcessReadToken(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + // Recursively processes a token. This is required for elements that next other elements. + object? processedObject; - public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + switch (reader.TokenType) { - switch (value) - { - case null: - writer.WriteNullValue(); - break; - case Enum: - writer.WriteNumberValue(Convert.ToInt64(value)); - break; - case IEnumerable list: - writer.WriteStartArray(); - foreach (var item in list) + case JsonTokenType.StartObject: + { + Dictionary dictionaryValue = []; + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { - Write(writer, item, options); + string elementKey = reader.GetString()!; + reader.Read(); + dictionaryValue.Add(elementKey, ProcessReadToken(ref reader, options)); } - writer.WriteEndArray(); + + processedObject = dictionaryValue; break; - case IDictionary dictionary: - writer.WriteStartObject(); - foreach (var pair in dictionary) + } + + case JsonTokenType.StartArray: + { + List arrayValue = []; + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { - writer.WritePropertyName(pair.Key); - Write(writer, pair.Value, options); + arrayValue.Add(ProcessReadToken(ref reader, options)); } - writer.WriteEndObject(); - break; - case object obj: - JsonSerializer.Serialize(writer, obj, options.GetTypeInfo(obj.GetType())); - break; - } - } - private static object? ProcessReadToken(ref Utf8JsonReader reader, JsonSerializerOptions options) - { - // Recursively processes a token. This is required for elements that next other elements. - object? processedObject; + processedObject = arrayValue.ToArray(); + break; + } - switch (reader.TokenType) - { - case JsonTokenType.StartObject: + case JsonTokenType.Null: + processedObject = null; + break; + case JsonTokenType.False: + processedObject = false; + break; + case JsonTokenType.True: + processedObject = true; + break; + case JsonTokenType.String: + processedObject = reader.GetString(); + break; + case JsonTokenType.Number: + { + if (reader.TryGetInt64(out long longValue)) { - Dictionary dictionaryValue = []; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - string elementKey = reader.GetString()!; - reader.Read(); - dictionaryValue.Add(elementKey, ProcessReadToken(ref reader, options)); - } - - processedObject = dictionaryValue; - break; + processedObject = longValue; } - - case JsonTokenType.StartArray: + else if (reader.TryGetDouble(out double doubleValue)) { - List arrayValue = []; - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - arrayValue.Add(ProcessReadToken(ref reader, options)); - } - - processedObject = arrayValue.ToArray(); - break; + processedObject = doubleValue; } - - case JsonTokenType.Null: - processedObject = null; - break; - case JsonTokenType.False: - processedObject = false; - break; - case JsonTokenType.True: - processedObject = true; - break; - case JsonTokenType.String: - processedObject = reader.GetString(); - break; - case JsonTokenType.Number: + else { - if (reader.TryGetInt64(out long longValue)) - { - processedObject = longValue; - } - else if (reader.TryGetDouble(out double doubleValue)) - { - processedObject = doubleValue; - } - else - { - throw new JsonException($"Unrecognized '{JsonElement.ParseValue(ref reader)}' token as a number value."); - } - - break; + throw new JsonException($"Unrecognized '{JsonElement.ParseValue(ref reader)}' token as a number value."); } - default: - throw new JsonException($"Unrecognized '{reader.TokenType}' token type while parsing command response."); - } + break; + } - return processedObject; + default: + throw new JsonException($"Unrecognized '{reader.TokenType}' token type while parsing command response."); } + + return processedObject; } } diff --git a/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs b/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs index 9dd40b40f45ae..70f0a3ef4490c 100644 --- a/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs +++ b/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs @@ -22,116 +22,115 @@ using System.Collections.ObjectModel; using System.Globalization; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Class to Create the capabilities of the browser you require for . +/// If you wish to use default values use the static methods +/// +internal sealed class ReturnedCapabilities : ICapabilities, IHasCapabilitiesDictionary { + private readonly Dictionary capabilities = new Dictionary(); + /// - /// Class to Create the capabilities of the browser you require for . - /// If you wish to use default values use the static methods + /// Initializes a new instance of the class /// - internal sealed class ReturnedCapabilities : ICapabilities, IHasCapabilitiesDictionary + public ReturnedCapabilities() { - private readonly Dictionary capabilities = new Dictionary(); - - /// - /// Initializes a new instance of the class - /// - public ReturnedCapabilities() - { - } + } - /// - /// Initializes a new instance of the class - /// - /// Dictionary of items for the remote driver - public ReturnedCapabilities(Dictionary? rawMap) + /// + /// Initializes a new instance of the class + /// + /// Dictionary of items for the remote driver + public ReturnedCapabilities(Dictionary? rawMap) + { + if (rawMap != null) { - if (rawMap != null) + foreach (KeyValuePair rawItem in rawMap) { - foreach (KeyValuePair rawItem in rawMap) - { - this.capabilities[rawItem.Key] = rawItem.Value; - } + this.capabilities[rawItem.Key] = rawItem.Value; } } + } - /// - /// Gets the browser name, or if not specified. - /// - public string BrowserName + /// + /// Gets the browser name, or if not specified. + /// + public string BrowserName + { + get { - get - { - object? capabilityValue = this.GetCapability(CapabilityType.BrowserName); - return capabilityValue?.ToString() ?? string.Empty; - } + object? capabilityValue = this.GetCapability(CapabilityType.BrowserName); + return capabilityValue?.ToString() ?? string.Empty; } + } - /// - /// Gets the capability value with the specified name. - /// - /// The name of the capability to get. - /// The value of the capability. - /// - /// The specified capability name is not in the set of capabilities. - /// - public object this[string capabilityName] + /// + /// Gets the capability value with the specified name. + /// + /// The name of the capability to get. + /// The value of the capability. + /// + /// The specified capability name is not in the set of capabilities. + /// + public object this[string capabilityName] + { + get { - get + if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) { - if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); - } - - return capabilityValue; + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); } + + return capabilityValue; } + } - /// - /// Gets the underlying Dictionary for a given set of capabilities. - /// - IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; + /// + /// Gets the underlying Dictionary for a given set of capabilities. + /// + IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; - /// - /// Gets the internal capabilities dictionary. - /// - internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); + /// + /// Gets the internal capabilities dictionary. + /// + internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); - /// - /// Gets a value indicating whether the browser has a given capability. - /// - /// The capability to get. - /// Returns if the browser has the capability; otherwise, . - public bool HasCapability(string capability) - { - return this.capabilities.ContainsKey(capability); - } + /// + /// Gets a value indicating whether the browser has a given capability. + /// + /// The capability to get. + /// Returns if the browser has the capability; otherwise, . + public bool HasCapability(string capability) + { + return this.capabilities.ContainsKey(capability); + } - /// - /// Gets a capability of the browser. - /// - /// The capability to get. - /// An object associated with the capability, or - /// if the capability is not set on the browser. - public object? GetCapability(string capability) + /// + /// Gets a capability of the browser. + /// + /// The capability to get. + /// An object associated with the capability, or + /// if the capability is not set on the browser. + public object? GetCapability(string capability) + { + if (this.capabilities.TryGetValue(capability, out object? capabilityValue)) { - if (this.capabilities.TryGetValue(capability, out object? capabilityValue)) - { - return capabilityValue; - } - - return null; + return capabilityValue; } - /// - /// Converts the object to a . - /// - /// The containing the capabilities. - public Dictionary ToDictionary() - { - // CONSIDER: Instead of returning the raw internal member, - // we might want to copy/clone it instead. - return this.capabilities; - } + return null; + } + + /// + /// Converts the object to a . + /// + /// The containing the capabilities. + public Dictionary ToDictionary() + { + // CONSIDER: Instead of returning the raw internal member, + // we might want to copy/clone it instead. + return this.capabilities; } } diff --git a/dotnet/src/webdriver/Internal/ReturnedCookie.cs b/dotnet/src/webdriver/Internal/ReturnedCookie.cs index ac2baa4160373..a1e058251f4c0 100644 --- a/dotnet/src/webdriver/Internal/ReturnedCookie.cs +++ b/dotnet/src/webdriver/Internal/ReturnedCookie.cs @@ -20,67 +20,66 @@ using System; using System.Globalization; -namespace OpenQA.Selenium.Internal +namespace OpenQA.Selenium.Internal; + +/// +/// Represents a cookie returned to the driver by the browser. +/// +public class ReturnedCookie : Cookie { + /// - /// Represents a cookie returned to the driver by the browser. + /// Initializes a new instance of the class with a specific name, + /// value, domain, path and expiration date. /// - public class ReturnedCookie : Cookie + /// The name of the cookie. + /// The value of the cookie. + /// The domain of the cookie. + /// The path of the cookie. + /// The expiration date of the cookie. + /// if the cookie is secure; otherwise + /// if the cookie is an HTTP-only cookie; otherwise + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value or currentUrl is . + public ReturnedCookie(string name, string value, string? domain, string? path, DateTime? expiry, bool isSecure, bool isHttpOnly) + : this(name, value, domain, path, expiry, isSecure, isHttpOnly, null) { + } - /// - /// Initializes a new instance of the class with a specific name, - /// value, domain, path and expiration date. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The domain of the cookie. - /// The path of the cookie. - /// The expiration date of the cookie. - /// if the cookie is secure; otherwise - /// if the cookie is an HTTP-only cookie; otherwise - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value or currentUrl is . - public ReturnedCookie(string name, string value, string? domain, string? path, DateTime? expiry, bool isSecure, bool isHttpOnly) - : this(name, value, domain, path, expiry, isSecure, isHttpOnly, null) - { - } - - /// - /// Initializes a new instance of the class with a specific name, - /// value, domain, path and expiration date. - /// - /// The name of the cookie. - /// The value of the cookie. - /// The domain of the cookie. - /// The path of the cookie. - /// The expiration date of the cookie. - /// if the cookie is secure; otherwise - /// if the cookie is an HTTP-only cookie; otherwise - /// The SameSite value of cookie. - /// If the name is or an empty string, - /// or if it contains a semi-colon. - /// If the value or currentUrl is . - public ReturnedCookie(string name, string value, string? domain, string? path, DateTime? expiry, bool isSecure, bool isHttpOnly, string? sameSite) - : base(name, value, domain, path, expiry, isSecure, isHttpOnly, sameSite) - { + /// + /// Initializes a new instance of the class with a specific name, + /// value, domain, path and expiration date. + /// + /// The name of the cookie. + /// The value of the cookie. + /// The domain of the cookie. + /// The path of the cookie. + /// The expiration date of the cookie. + /// if the cookie is secure; otherwise + /// if the cookie is an HTTP-only cookie; otherwise + /// The SameSite value of cookie. + /// If the name is or an empty string, + /// or if it contains a semi-colon. + /// If the value or currentUrl is . + public ReturnedCookie(string name, string value, string? domain, string? path, DateTime? expiry, bool isSecure, bool isHttpOnly, string? sameSite) + : base(name, value, domain, path, expiry, isSecure, isHttpOnly, sameSite) + { - } + } - /// - /// Creates and returns a string representation of the current cookie. - /// - /// A string representation of the current cookie. - public override string ToString() - { - return this.Name + "=" + this.Value - + (this.Expiry == null ? string.Empty : "; expires=" + this.Expiry.Value.ToUniversalTime().ToString("ddd MM/dd/yyyy HH:mm:ss UTC", CultureInfo.InvariantCulture)) - + (string.IsNullOrEmpty(this.Path) ? string.Empty : "; path=" + this.Path) - + (string.IsNullOrEmpty(this.Domain) ? string.Empty : "; domain=" + this.Domain) - + (this.Secure ? "; secure" : string.Empty) - + (this.IsHttpOnly ? "; httpOnly" : string.Empty) - + (string.IsNullOrEmpty(this.SameSite) ? string.Empty : "; sameSite=" + this.SameSite); - } + /// + /// Creates and returns a string representation of the current cookie. + /// + /// A string representation of the current cookie. + public override string ToString() + { + return this.Name + "=" + this.Value + + (this.Expiry == null ? string.Empty : "; expires=" + this.Expiry.Value.ToUniversalTime().ToString("ddd MM/dd/yyyy HH:mm:ss UTC", CultureInfo.InvariantCulture)) + + (string.IsNullOrEmpty(this.Path) ? string.Empty : "; path=" + this.Path) + + (string.IsNullOrEmpty(this.Domain) ? string.Empty : "; domain=" + this.Domain) + + (this.Secure ? "; secure" : string.Empty) + + (this.IsHttpOnly ? "; httpOnly" : string.Empty) + + (string.IsNullOrEmpty(this.SameSite) ? string.Empty : "; sameSite=" + this.SameSite); } } diff --git a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs index cc765a8d27a68..1e531aed5aa08 100644 --- a/dotnet/src/webdriver/Internal/TrimmingAttributes.cs +++ b/dotnet/src/webdriver/Internal/TrimmingAttributes.cs @@ -21,417 +21,416 @@ #if !NET8_0_OR_GREATER -namespace System.Diagnostics.CodeAnalysis +namespace System.Diagnostics.CodeAnalysis; + +/// +/// Indicates that the specified method requires the ability to generate new code at runtime, +/// for example through . +/// +/// +/// This allows tools to understand which methods are unsafe to call when compiling ahead of time. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] +internal sealed class RequiresDynamicCodeAttribute : Attribute { /// - /// Indicates that the specified method requires the ability to generate new code at runtime, - /// for example through . + /// Initializes a new instance of the class + /// with the specified message. /// - /// - /// This allows tools to understand which methods are unsafe to call when compiling ahead of time. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] - internal sealed class RequiresDynamicCodeAttribute : Attribute + /// + /// A message that contains information about the usage of dynamic code. + /// + public RequiresDynamicCodeAttribute(string message) { - /// - /// Initializes a new instance of the class - /// with the specified message. - /// - /// - /// A message that contains information about the usage of dynamic code. - /// - public RequiresDynamicCodeAttribute(string message) - { - Message = message; - } - - /// - /// Gets a message that contains information about the usage of dynamic code. - /// - public string Message { get; } - - /// - /// Gets or sets an optional URL that contains more information about the method, - /// why it requires dynamic code, and what options a consumer has to deal with it. - /// - public string? Url { get; set; } + Message = message; } /// - /// Indicates that the specified method requires dynamic access to code that is not referenced - /// statically, for example through . + /// Gets a message that contains information about the usage of dynamic code. /// - /// - /// This allows tools to understand which methods are unsafe to call when removing unreferenced - /// code from an application. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] - internal sealed class RequiresUnreferencedCodeAttribute : Attribute + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires dynamic code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } +} + +/// +/// Indicates that the specified method requires dynamic access to code that is not referenced +/// statically, for example through . +/// +/// +/// This allows tools to understand which methods are unsafe to call when removing unreferenced +/// code from an application. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] +internal sealed class RequiresUnreferencedCodeAttribute : Attribute +{ + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) { - /// - /// Initializes a new instance of the class - /// with the specified message. - /// - /// - /// A message that contains information about the usage of unreferenced code. - /// - public RequiresUnreferencedCodeAttribute(string message) - { - Message = message; - } - - /// - /// Gets a message that contains information about the usage of unreferenced code. - /// - public string Message { get; } - - /// - /// Gets or sets an optional URL that contains more information about the method, - /// why it requires unreferenced code, and what options a consumer has to deal with it. - /// - public string? Url { get; set; } + Message = message; } /// - /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a - /// single code artifact. + /// Gets a message that contains information about the usage of unreferenced code. /// - /// - /// is different than - /// in that it doesn't have a - /// . So it is always preserved in the compiled assembly. - /// - [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] - internal sealed class UnconditionalSuppressMessageAttribute : Attribute + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } +} + +/// +/// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a +/// single code artifact. +/// +/// +/// is different than +/// in that it doesn't have a +/// . So it is always preserved in the compiled assembly. +/// +[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] +internal sealed class UnconditionalSuppressMessageAttribute : Attribute +{ + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) { - /// - /// Initializes a new instance of the - /// class, specifying the category of the tool and the identifier for an analysis rule. - /// - /// The category for the attribute. - /// The identifier of the analysis rule the attribute applies to. - public UnconditionalSuppressMessageAttribute(string category, string checkId) - { - Category = category; - CheckId = checkId; - } - - /// - /// Gets the category identifying the classification of the attribute. - /// - /// - /// The property describes the tool or tool analysis category - /// for which a message suppression attribute applies. - /// - public string Category { get; } - - /// - /// Gets the identifier of the analysis tool rule to be suppressed. - /// - /// - /// Concatenated together, the and - /// properties form a unique check identifier. - /// - public string CheckId { get; } - - /// - /// Gets or sets the scope of the code that is relevant for the attribute. - /// - /// - /// The Scope property is an optional argument that specifies the metadata scope for which - /// the attribute is relevant. - /// - public string? Scope { get; set; } - - /// - /// Gets or sets a fully qualified path that represents the target of the attribute. - /// - /// - /// The property is an optional argument identifying the analysis target - /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". - /// Because it is fully qualified, it can be long, particularly for targets such as parameters. - /// The analysis tool user interface should be capable of automatically formatting the parameter. - /// - public string? Target { get; set; } - - /// - /// Gets or sets an optional argument expanding on exclusion criteria. - /// - /// - /// The property is an optional argument that specifies additional - /// exclusion where the literal metadata target is not sufficiently precise. For example, - /// the cannot be applied within a method, - /// and it may be desirable to suppress a violation against a statement in the method that will - /// give a rule violation, but not against all statements in the method. - /// - public string? MessageId { get; set; } - - /// - /// Gets or sets the justification for suppressing the code analysis message. - /// - public string? Justification { get; set; } + Category = category; + CheckId = checkId; } /// - /// States a dependency that one member has on another. + /// Gets the category identifying the classification of the attribute. /// /// - /// This can be used to inform tooling of a dependency that is otherwise not evident purely from - /// metadata and IL, for example a member relied on via reflection. + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. /// - [AttributeUsage( - AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, - AllowMultiple = true, Inherited = false)] - internal sealed class DynamicDependencyAttribute : Attribute - { - /// - /// Initializes a new instance of the class - /// with the specified signature of a member on the same type as the consumer. - /// - /// The signature of the member depended on. - public DynamicDependencyAttribute(string memberSignature) - { - MemberSignature = memberSignature; - } - - /// - /// Initializes a new instance of the class - /// with the specified signature of a member on a . - /// - /// The signature of the member depended on. - /// The containing . - public DynamicDependencyAttribute(string memberSignature, Type type) - { - MemberSignature = memberSignature; - Type = type; - } - - /// - /// Initializes a new instance of the class - /// with the specified signature of a member on a type in an assembly. - /// - /// The signature of the member depended on. - /// The full name of the type containing the specified member. - /// The assembly name of the type containing the specified member. - public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) - { - MemberSignature = memberSignature; - TypeName = typeName; - AssemblyName = assemblyName; - } - - /// - /// Initializes a new instance of the class - /// with the specified types of members on a . - /// - /// The types of members depended on. - /// The containing the specified members. - public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) - { - MemberTypes = memberTypes; - Type = type; - } - - /// - /// Initializes a new instance of the class - /// with the specified types of members on a type in an assembly. - /// - /// The types of members depended on. - /// The full name of the type containing the specified members. - /// The assembly name of the type containing the specified members. - public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) - { - MemberTypes = memberTypes; - TypeName = typeName; - AssemblyName = assemblyName; - } - - /// - /// Gets the signature of the member depended on. - /// - /// - /// Either must be a valid string or - /// must not equal , but not both. - /// - public string? MemberSignature { get; } - - /// - /// Gets the which specifies the type - /// of members depended on. - /// - /// - /// Either must be a valid string or - /// must not equal , but not both. - /// - public DynamicallyAccessedMemberTypes MemberTypes { get; } - - /// - /// Gets the containing the specified member. - /// - /// - /// If neither nor are specified, - /// the type of the consumer is assumed. - /// - public Type? Type { get; } - - /// - /// Gets the full name of the type containing the specified member. - /// - /// - /// If neither nor are specified, - /// the type of the consumer is assumed. - /// - public string? TypeName { get; } - - /// - /// Gets the assembly name of the specified type. - /// - /// - /// is only valid when is specified. - /// - public string? AssemblyName { get; } - - /// - /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". - /// - public string? Condition { get; set; } - } + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } /// - /// Indicates that certain members on a specified are accessed dynamically, - /// for example through . + /// Gets or sets the scope of the code that is relevant for the attribute. /// /// - /// This allows tools to understand which members are being accessed during the execution - /// of a program. - /// - /// This attribute is valid on members whose type is or . - /// - /// When this attribute is applied to a location of type , the assumption is - /// that the string represents a fully qualified type name. - /// - /// When this attribute is applied to a class, interface, or struct, the members specified - /// can be accessed dynamically on instances returned from calling - /// on instances of that class, interface, or struct. - /// - /// If the attribute is applied to a method it's treated as a special case and it implies - /// the attribute should be applied to the "this" parameter of the method. As such the attribute - /// should only be used on instance methods of types assignable to System.Type (or string, but no methods - /// will use it there). + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, - Inherited = false)] - internal sealed class DynamicallyAccessedMembersAttribute : Attribute + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } +} + +/// +/// States a dependency that one member has on another. +/// +/// +/// This can be used to inform tooling of a dependency that is otherwise not evident purely from +/// metadata and IL, for example a member relied on via reflection. +/// +[AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] +internal sealed class DynamicDependencyAttribute : Attribute +{ + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on the same type as the consumer. + /// + /// The signature of the member depended on. + public DynamicDependencyAttribute(string memberSignature) + { + MemberSignature = memberSignature; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a . + /// + /// The signature of the member depended on. + /// The containing . + public DynamicDependencyAttribute(string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a type in an assembly. + /// + /// The signature of the member depended on. + /// The full name of the type containing the specified member. + /// The assembly name of the type containing the specified member. + public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) { - /// - /// Initializes a new instance of the class - /// with the specified member types. - /// - /// The types of members dynamically accessed. - public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) - { - MemberTypes = memberTypes; - } - - /// - /// Gets the which specifies the type - /// of members dynamically accessed. - /// - public DynamicallyAccessedMemberTypes MemberTypes { get; } + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; } /// - /// Specifies the types of members that are dynamically accessed. - /// - /// This enumeration has a attribute that allows a - /// bitwise combination of its member values. + /// Initializes a new instance of the class + /// with the specified types of members on a . + /// + /// The types of members depended on. + /// The containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a type in an assembly. + /// + /// The types of members depended on. + /// The full name of the type containing the specified members. + /// The assembly name of the type containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Gets the signature of the member depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public string? MemberSignature { get; } + + /// + /// Gets the which specifies the type + /// of members depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + /// + /// Gets the containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public Type? Type { get; } + + /// + /// Gets the full name of the type containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public string? TypeName { get; } + + /// + /// Gets the assembly name of the specified type. + /// + /// + /// is only valid when is specified. + /// + public string? AssemblyName { get; } + + /// + /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". + /// + public string? Condition { get; set; } +} + +/// +/// Indicates that certain members on a specified are accessed dynamically, +/// for example through . +/// +/// +/// This allows tools to understand which members are being accessed during the execution +/// of a program. +/// +/// This attribute is valid on members whose type is or . +/// +/// When this attribute is applied to a location of type , the assumption is +/// that the string represents a fully qualified type name. +/// +/// When this attribute is applied to a class, interface, or struct, the members specified +/// can be accessed dynamically on instances returned from calling +/// on instances of that class, interface, or struct. +/// +/// If the attribute is applied to a method it's treated as a special case and it implies +/// the attribute should be applied to the "this" parameter of the method. As such the attribute +/// should only be used on instance methods of types assignable to System.Type (or string, but no methods +/// will use it there). +/// +[AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] +internal sealed class DynamicallyAccessedMembersAttribute : Attribute +{ + /// + /// Initializes a new instance of the class + /// with the specified member types. /// - [Flags] - internal enum DynamicallyAccessedMemberTypes + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) { - /// - /// Specifies no members. - /// - None = 0, - - /// - /// Specifies the default, parameterless public constructor. - /// - PublicParameterlessConstructor = 0x0001, - - /// - /// Specifies all public constructors. - /// - PublicConstructors = 0x0002 | PublicParameterlessConstructor, - - /// - /// Specifies all non-public constructors. - /// - NonPublicConstructors = 0x0004, - - /// - /// Specifies all public methods. - /// - PublicMethods = 0x0008, - - /// - /// Specifies all non-public methods. - /// - NonPublicMethods = 0x0010, - - /// - /// Specifies all public fields. - /// - PublicFields = 0x0020, - - /// - /// Specifies all non-public fields. - /// - NonPublicFields = 0x0040, - - /// - /// Specifies all public nested types. - /// - PublicNestedTypes = 0x0080, - - /// - /// Specifies all non-public nested types. - /// - NonPublicNestedTypes = 0x0100, - - /// - /// Specifies all public properties. - /// - PublicProperties = 0x0200, - - /// - /// Specifies all non-public properties. - /// - NonPublicProperties = 0x0400, - - /// - /// Specifies all public events. - /// - PublicEvents = 0x0800, - - /// - /// Specifies all non-public events. - /// - NonPublicEvents = 0x1000, - - /// - /// Specifies all interfaces implemented by the type. - /// - Interfaces = 0x2000, - - /// - /// Specifies all members. - /// - All = ~None + MemberTypes = memberTypes; } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } +} + +/// +/// Specifies the types of members that are dynamically accessed. +/// +/// This enumeration has a attribute that allows a +/// bitwise combination of its member values. +/// +[Flags] +internal enum DynamicallyAccessedMemberTypes +{ + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all interfaces implemented by the type. + /// + Interfaces = 0x2000, + + /// + /// Specifies all members. + /// + All = ~None } #endif diff --git a/dotnet/src/webdriver/InvalidCookieDomainException.cs b/dotnet/src/webdriver/InvalidCookieDomainException.cs index 870245060d115..940ef3821cd9a 100644 --- a/dotnet/src/webdriver/InvalidCookieDomainException.cs +++ b/dotnet/src/webdriver/InvalidCookieDomainException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when the users attempts to set a cookie with an invalid domain. +/// +[Serializable] +public class InvalidCookieDomainException : WebDriverException { /// - /// The exception that is thrown when the users attempts to set a cookie with an invalid domain. + /// Initializes a new instance of the class. /// - [Serializable] - public class InvalidCookieDomainException : WebDriverException + public InvalidCookieDomainException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public InvalidCookieDomainException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public InvalidCookieDomainException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public InvalidCookieDomainException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public InvalidCookieDomainException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public InvalidCookieDomainException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/InvalidElementStateException.cs b/dotnet/src/webdriver/InvalidElementStateException.cs index 0d8330c7d05e2..5e5fa7e1b56c0 100644 --- a/dotnet/src/webdriver/InvalidElementStateException.cs +++ b/dotnet/src/webdriver/InvalidElementStateException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a reference to an element is no longer valid. +/// +[Serializable] +public class InvalidElementStateException : WebDriverException { /// - /// The exception that is thrown when a reference to an element is no longer valid. + /// Initializes a new instance of the class. /// - [Serializable] - public class InvalidElementStateException : WebDriverException + public InvalidElementStateException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public InvalidElementStateException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public InvalidElementStateException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public InvalidElementStateException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public InvalidElementStateException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public InvalidElementStateException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/InvalidSelectorException.cs b/dotnet/src/webdriver/InvalidSelectorException.cs index 7134edc144d27..fcf2753f3ab8b 100644 --- a/dotnet/src/webdriver/InvalidSelectorException.cs +++ b/dotnet/src/webdriver/InvalidSelectorException.cs @@ -19,58 +19,57 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an invalid selector is used. +/// +[Serializable] +public class InvalidSelectorException : WebDriverException { /// - /// The exception that is thrown when an invalid selector is used. + /// Link to the documentation for this error /// - [Serializable] - public class InvalidSelectorException : WebDriverException - { - /// - /// Link to the documentation for this error - /// - private static string supportUrl = baseSupportUrl + "#invalid-selector-exception"; + private static string supportUrl = baseSupportUrl + "#invalid-selector-exception"; - /// - /// Initializes a new instance of the class. - /// - public InvalidSelectorException() - : base(GetMessage("")) - { - } + /// + /// Initializes a new instance of the class. + /// + public InvalidSelectorException() + : base(GetMessage("")) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public InvalidSelectorException(string? message) - : base(GetMessage(message)) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public InvalidSelectorException(string? message) + : base(GetMessage(message)) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public InvalidSelectorException(string? message, Exception? innerException) - : base(GetMessage(message), innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public InvalidSelectorException(string? message, Exception? innerException) + : base(GetMessage(message), innerException) + { + } - /// - /// Add information about obtaining additional support from documentation to this exception. - /// - /// The original message for exception - /// The final message for exception - protected static string GetMessage(string? message) - { - return $"{message}; {supportMsg}{supportUrl}"; - } + /// + /// Add information about obtaining additional support from documentation to this exception. + /// + /// The original message for exception + /// The final message for exception + protected static string GetMessage(string? message) + { + return $"{message}; {supportMsg}{supportUrl}"; } } diff --git a/dotnet/src/webdriver/JavaScriptCallbackExecutedEventArgs.cs b/dotnet/src/webdriver/JavaScriptCallbackExecutedEventArgs.cs index aabf783d23540..ae53a3ade81a2 100644 --- a/dotnet/src/webdriver/JavaScriptCallbackExecutedEventArgs.cs +++ b/dotnet/src/webdriver/JavaScriptCallbackExecutedEventArgs.cs @@ -19,32 +19,31 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the JavaScriptCallbackExecuted event. +/// +public class JavaScriptCallbackExecutedEventArgs : EventArgs { /// - /// Provides data for the JavaScriptCallbackExecuted event. + /// Initializes a new instance of the type. /// - public class JavaScriptCallbackExecutedEventArgs : EventArgs + /// The payload sent from the JavaScript callback. + /// The binding name of the JavaScript callback that was execute. + public JavaScriptCallbackExecutedEventArgs(string scriptPayload, string bindingName) { - /// - /// Initializes a new instance of the type. - /// - /// The payload sent from the JavaScript callback. - /// The binding name of the JavaScript callback that was execute. - public JavaScriptCallbackExecutedEventArgs(string scriptPayload, string bindingName) - { - this.ScriptPayload = scriptPayload; - this.BindingName = bindingName; - } + this.ScriptPayload = scriptPayload; + this.BindingName = bindingName; + } - /// - /// Gets or sets the payload sent from the JavaScript callback. - /// - public string ScriptPayload { get; set; } + /// + /// Gets or sets the payload sent from the JavaScript callback. + /// + public string ScriptPayload { get; set; } - /// - /// Gets or sets the binding name of the JavaScript callback that was execute. - /// - public string BindingName { get; set; } - } + /// + /// Gets or sets the binding name of the JavaScript callback that was execute. + /// + public string BindingName { get; set; } } diff --git a/dotnet/src/webdriver/JavaScriptConsoleApiCalledEventArgs.cs b/dotnet/src/webdriver/JavaScriptConsoleApiCalledEventArgs.cs index 94296fa05b232..aeba71f4cf4ed 100644 --- a/dotnet/src/webdriver/JavaScriptConsoleApiCalledEventArgs.cs +++ b/dotnet/src/webdriver/JavaScriptConsoleApiCalledEventArgs.cs @@ -19,39 +19,38 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the JavaScriptConsoleApiCalled event. +/// +public class JavaScriptConsoleApiCalledEventArgs : EventArgs { /// - /// Provides data for the JavaScriptConsoleApiCalled event. + /// Initializes a new instance of the type. /// - public class JavaScriptConsoleApiCalledEventArgs : EventArgs + /// The content of the message written to the JavaScript console. + /// The time stamp of the message written to the JavaScript console. + /// The type of message written to the JavaScript console. + public JavaScriptConsoleApiCalledEventArgs(string? messageContent, DateTime messageTimeStamp, string messageType) { - /// - /// Initializes a new instance of the type. - /// - /// The content of the message written to the JavaScript console. - /// The time stamp of the message written to the JavaScript console. - /// The type of message written to the JavaScript console. - public JavaScriptConsoleApiCalledEventArgs(string? messageContent, DateTime messageTimeStamp, string messageType) - { - this.MessageContent = messageContent; - this.MessageTimeStamp = messageTimeStamp; - this.MessageType = messageType; - } + this.MessageContent = messageContent; + this.MessageTimeStamp = messageTimeStamp; + this.MessageType = messageType; + } - /// - /// Gets or sets the content of the message written to the JavaScript console. - /// - public string? MessageContent { get; set; } + /// + /// Gets or sets the content of the message written to the JavaScript console. + /// + public string? MessageContent { get; set; } - /// - /// Gets or sets the time stamp of the message written to the JavaScript console. - /// - public DateTime MessageTimeStamp { get; set; } + /// + /// Gets or sets the time stamp of the message written to the JavaScript console. + /// + public DateTime MessageTimeStamp { get; set; } - /// - /// Gets or sets the type of message written to the JavaScript console. - /// - public string MessageType { get; set; } - } + /// + /// Gets or sets the type of message written to the JavaScript console. + /// + public string MessageType { get; set; } } diff --git a/dotnet/src/webdriver/JavaScriptEngine.cs b/dotnet/src/webdriver/JavaScriptEngine.cs index 3f210c7a4a28b..3465c46b98cbb 100644 --- a/dotnet/src/webdriver/JavaScriptEngine.cs +++ b/dotnet/src/webdriver/JavaScriptEngine.cs @@ -28,437 +28,436 @@ using System.Text.Json; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides methods allowing the user to manage settings in the browser's JavaScript engine. +/// +[RequiresUnreferencedCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] +[RequiresDynamicCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] +public class JavaScriptEngine : IJavaScriptEngine { + private const string MonitorBindingName = "__webdriver_attribute"; + + private readonly IWebDriver driver; + private readonly Lazy session; + private readonly Dictionary initializationScripts = new Dictionary(); + private readonly Dictionary pinnedScripts = new Dictionary(); + private readonly HashSet bindings = new HashSet(); + private bool isEnabled = false; + private bool isDisposed = false; + /// - /// Provides methods allowing the user to manage settings in the browser's JavaScript engine. + /// Initializes a new instance of the class. /// - [RequiresUnreferencedCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] - [RequiresDynamicCode("JavaScriptEngine is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] - public class JavaScriptEngine : IJavaScriptEngine + /// The instance in which the JavaScript engine is executing. + public JavaScriptEngine(IWebDriver driver) { - private const string MonitorBindingName = "__webdriver_attribute"; - - private readonly IWebDriver driver; - private readonly Lazy session; - private readonly Dictionary initializationScripts = new Dictionary(); - private readonly Dictionary pinnedScripts = new Dictionary(); - private readonly HashSet bindings = new HashSet(); - private bool isEnabled = false; - private bool isDisposed = false; - - /// - /// Initializes a new instance of the class. - /// - /// The instance in which the JavaScript engine is executing. - public JavaScriptEngine(IWebDriver driver) + // Use of Lazy means this exception won't be thrown until the user first + // attempts to access the DevTools session, probably on the first call to + // StartEventMonitoring() or in adding scripts to the instance. + this.driver = driver; + this.session = new Lazy(() => { - // Use of Lazy means this exception won't be thrown until the user first - // attempts to access the DevTools session, probably on the first call to - // StartEventMonitoring() or in adding scripts to the instance. - this.driver = driver; - this.session = new Lazy(() => + if (driver is not IDevTools devToolsDriver) { - if (driver is not IDevTools devToolsDriver) - { - throw new WebDriverException("Driver must implement IDevTools to use these features"); - } - - return devToolsDriver.GetDevToolsSession(); - }); - } - - /// - /// Occurs when a JavaScript callback with a named binding is executed. - /// - public event EventHandler? JavaScriptCallbackExecuted; - - /// - /// Occurs when an exception is thrown by JavaScript being executed in the browser. - /// - public event EventHandler? JavaScriptExceptionThrown; - - /// - /// Occurs when methods on the JavaScript console are called. - /// - public event EventHandler? JavaScriptConsoleApiCalled; - - /// - /// Occurs when a value of an attribute in an element is being changed. - /// - public event EventHandler? DomMutated; - - /// - /// Gets the read-only list of initialization scripts added for this JavaScript engine. - /// - public IReadOnlyList InitializationScripts - { - get - { - // Return a copy. - return new List(this.initializationScripts.Values); + throw new WebDriverException("Driver must implement IDevTools to use these features"); } - } - /// - /// Gets the read-only list of bindings added for this JavaScript engine. - /// - public IReadOnlyList ScriptCallbackBindings - { - get - { - // Return a copy. - return new List(this.bindings); - } - } + return devToolsDriver.GetDevToolsSession(); + }); + } - /// - /// Asynchronously starts monitoring for events from the browser's JavaScript engine. - /// - /// A task that represents the asynchronous operation. - public async Task StartEventMonitoring() - { - this.session.Value.Domains.JavaScript.BindingCalled += OnScriptBindingCalled; - this.session.Value.Domains.JavaScript.ExceptionThrown += OnJavaScriptExceptionThrown; - this.session.Value.Domains.JavaScript.ConsoleApiCalled += OnConsoleApiCalled; - await this.EnableDomains().ConfigureAwait(false); - } + /// + /// Occurs when a JavaScript callback with a named binding is executed. + /// + public event EventHandler? JavaScriptCallbackExecuted; - /// - /// Stops monitoring for events from the browser's JavaScript engine. - /// - public void StopEventMonitoring() - { - this.session.Value.Domains.JavaScript.ConsoleApiCalled -= OnConsoleApiCalled; - this.session.Value.Domains.JavaScript.ExceptionThrown -= OnJavaScriptExceptionThrown; - this.session.Value.Domains.JavaScript.BindingCalled -= OnScriptBindingCalled; - } + /// + /// Occurs when an exception is thrown by JavaScript being executed in the browser. + /// + public event EventHandler? JavaScriptExceptionThrown; - /// - /// Enables monitoring for DOM changes. - /// - /// A task that represents the asynchronous operation. - public async Task EnableDomMutationMonitoring() - { - // Execute the script to have it enabled on the currently loaded page. - string script = GetMutationListenerScript(); - await this.session.Value.Domains.JavaScript.Evaluate(script).ConfigureAwait(false); + /// + /// Occurs when methods on the JavaScript console are called. + /// + public event EventHandler? JavaScriptConsoleApiCalled; - await this.AddScriptCallbackBinding(MonitorBindingName).ConfigureAwait(false); - await this.AddInitializationScript(MonitorBindingName, script).ConfigureAwait(false); - } + /// + /// Occurs when a value of an attribute in an element is being changed. + /// + public event EventHandler? DomMutated; - /// - /// Disables monitoring for DOM changes. - /// - /// A task that represents the asynchronous operation. - public async Task DisableDomMutationMonitoring() + /// + /// Gets the read-only list of initialization scripts added for this JavaScript engine. + /// + public IReadOnlyList InitializationScripts + { + get { - await this.RemoveScriptCallbackBinding(MonitorBindingName).ConfigureAwait(false); - await this.RemoveInitializationScript(MonitorBindingName).ConfigureAwait(false); + // Return a copy. + return new List(this.initializationScripts.Values); } + } - /// - /// Asynchronously adds JavaScript to be loaded on every document load. - /// - /// The friendly name by which to refer to this initialization script. - /// The JavaScript to be loaded on every page. - /// A task containing an object representing the script to be loaded on each page. - /// If or are . - public async Task AddInitializationScript(string scriptName, string script) + /// + /// Gets the read-only list of bindings added for this JavaScript engine. + /// + public IReadOnlyList ScriptCallbackBindings + { + get { - if (scriptName is null) - { - throw new ArgumentNullException(nameof(scriptName)); - } + // Return a copy. + return new List(this.bindings); + } + } - if (script is null) - { - throw new ArgumentNullException(nameof(script)); - } + /// + /// Asynchronously starts monitoring for events from the browser's JavaScript engine. + /// + /// A task that represents the asynchronous operation. + public async Task StartEventMonitoring() + { + this.session.Value.Domains.JavaScript.BindingCalled += OnScriptBindingCalled; + this.session.Value.Domains.JavaScript.ExceptionThrown += OnJavaScriptExceptionThrown; + this.session.Value.Domains.JavaScript.ConsoleApiCalled += OnConsoleApiCalled; + await this.EnableDomains().ConfigureAwait(false); + } - if (this.initializationScripts.TryGetValue(scriptName, out InitializationScript? existingScript)) - { - return existingScript; - } + /// + /// Stops monitoring for events from the browser's JavaScript engine. + /// + public void StopEventMonitoring() + { + this.session.Value.Domains.JavaScript.ConsoleApiCalled -= OnConsoleApiCalled; + this.session.Value.Domains.JavaScript.ExceptionThrown -= OnJavaScriptExceptionThrown; + this.session.Value.Domains.JavaScript.BindingCalled -= OnScriptBindingCalled; + } - await this.EnableDomains().ConfigureAwait(false); + /// + /// Enables monitoring for DOM changes. + /// + /// A task that represents the asynchronous operation. + public async Task EnableDomMutationMonitoring() + { + // Execute the script to have it enabled on the currently loaded page. + string script = GetMutationListenerScript(); + await this.session.Value.Domains.JavaScript.Evaluate(script).ConfigureAwait(false); - string scriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(script).ConfigureAwait(false); + await this.AddScriptCallbackBinding(MonitorBindingName).ConfigureAwait(false); + await this.AddInitializationScript(MonitorBindingName, script).ConfigureAwait(false); + } - InitializationScript initializationScript = new InitializationScript(scriptId, scriptName, script); - this.initializationScripts[scriptName] = initializationScript; - return initializationScript; - } + /// + /// Disables monitoring for DOM changes. + /// + /// A task that represents the asynchronous operation. + public async Task DisableDomMutationMonitoring() + { + await this.RemoveScriptCallbackBinding(MonitorBindingName).ConfigureAwait(false); + await this.RemoveInitializationScript(MonitorBindingName).ConfigureAwait(false); + } - /// - /// Asynchronously removes JavaScript from being loaded on every document load. - /// - /// The friendly name of the initialization script to be removed. - /// A task that represents the asynchronous operation. - /// If is . - public async Task RemoveInitializationScript(string scriptName) + /// + /// Asynchronously adds JavaScript to be loaded on every document load. + /// + /// The friendly name by which to refer to this initialization script. + /// The JavaScript to be loaded on every page. + /// A task containing an object representing the script to be loaded on each page. + /// If or are . + public async Task AddInitializationScript(string scriptName, string script) + { + if (scriptName is null) { - if (scriptName is null) - { - throw new ArgumentNullException(nameof(scriptName)); - } - - if (this.initializationScripts.TryGetValue(scriptName, out InitializationScript? script)) - { - string scriptId = script.ScriptId; - await this.session.Value.Domains.JavaScript.RemoveScriptToEvaluateOnNewDocument(scriptId).ConfigureAwait(false); - this.initializationScripts.Remove(scriptName); - } + throw new ArgumentNullException(nameof(scriptName)); } - /// - /// Asynchronously removes all initialization scripts from being loaded on every document load. - /// - /// A task that represents the asynchronous operation. - public async Task ClearInitializationScripts() + if (script is null) { - // Use a copy of the list to prevent the iterator from becoming invalid - // when we modify the collection. - List scriptNames = new List(this.initializationScripts.Keys); - foreach (string scriptName in scriptNames) - { - await this.RemoveInitializationScript(scriptName).ConfigureAwait(false); - } + throw new ArgumentNullException(nameof(script)); } - /// - /// Pins a JavaScript snippet for execution in the browser without transmitting the - /// entire script across the wire for every execution. - /// - /// The JavaScript to pin - /// A task containing a object to use to execute the script. - /// If is . - public async Task PinScript(string script) + if (this.initializationScripts.TryGetValue(scriptName, out InitializationScript? existingScript)) { - if (script == null) - { - throw new ArgumentNullException(nameof(script)); - } + return existingScript; + } - string newScriptHandle = Guid.NewGuid().ToString("N"); + await this.EnableDomains().ConfigureAwait(false); - // We do an "Evaluate" first so as to immediately create the script on the loaded - // page, then will add it to the initialization of future pages. - await this.EnableDomains().ConfigureAwait(false); + string scriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(script).ConfigureAwait(false); - string creationScript = PinnedScript.MakeCreationScript(newScriptHandle, script); - await this.session.Value.Domains.JavaScript.Evaluate(creationScript).ConfigureAwait(false); - string scriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(creationScript).ConfigureAwait(false); + InitializationScript initializationScript = new InitializationScript(scriptId, scriptName, script); + this.initializationScripts[scriptName] = initializationScript; + return initializationScript; + } - PinnedScript pinnedScript = new PinnedScript(script, newScriptHandle, scriptId); - this.pinnedScripts[pinnedScript.Handle] = pinnedScript; - return pinnedScript; + /// + /// Asynchronously removes JavaScript from being loaded on every document load. + /// + /// The friendly name of the initialization script to be removed. + /// A task that represents the asynchronous operation. + /// If is . + public async Task RemoveInitializationScript(string scriptName) + { + if (scriptName is null) + { + throw new ArgumentNullException(nameof(scriptName)); } - /// - /// Unpins a previously pinned script from the browser. - /// - /// The object to unpin. - /// A task that represents the asynchronous operation. - /// If is . - public async Task UnpinScript(PinnedScript script) + if (this.initializationScripts.TryGetValue(scriptName, out InitializationScript? script)) { - if (script == null) - { - throw new ArgumentNullException(nameof(script)); - } + string scriptId = script.ScriptId; + await this.session.Value.Domains.JavaScript.RemoveScriptToEvaluateOnNewDocument(scriptId).ConfigureAwait(false); + this.initializationScripts.Remove(scriptName); + } + } - if (this.pinnedScripts.ContainsKey(script.Handle)) - { - await this.session.Value.Domains.JavaScript.Evaluate(script.MakeRemovalScript()).ConfigureAwait(false); - await this.session.Value.Domains.JavaScript.RemoveScriptToEvaluateOnNewDocument(script.ScriptId).ConfigureAwait(false); - this.pinnedScripts.Remove(script.Handle); - } + /// + /// Asynchronously removes all initialization scripts from being loaded on every document load. + /// + /// A task that represents the asynchronous operation. + public async Task ClearInitializationScripts() + { + // Use a copy of the list to prevent the iterator from becoming invalid + // when we modify the collection. + List scriptNames = new List(this.initializationScripts.Keys); + foreach (string scriptName in scriptNames) + { + await this.RemoveInitializationScript(scriptName).ConfigureAwait(false); } + } - /// - /// Asynchronously adds a binding to a callback method that will raise an event when the named - /// binding is called by JavaScript executing in the browser. - /// - /// The name of the callback that will trigger events when called by JavaScript executing in the browser. - /// A task that represents the asynchronous operation. - /// If is . - /// If A binding with the specified name already exists. - public async Task AddScriptCallbackBinding(string bindingName) + /// + /// Pins a JavaScript snippet for execution in the browser without transmitting the + /// entire script across the wire for every execution. + /// + /// The JavaScript to pin + /// A task containing a object to use to execute the script. + /// If is . + public async Task PinScript(string script) + { + if (script == null) { - if (bindingName is null) - { - throw new ArgumentNullException(nameof(bindingName)); - } + throw new ArgumentNullException(nameof(script)); + } - if (!this.bindings.Add(bindingName)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "A binding named {0} has already been added", bindingName)); - } + string newScriptHandle = Guid.NewGuid().ToString("N"); - await this.EnableDomains().ConfigureAwait(false); - await this.session.Value.Domains.JavaScript.AddBinding(bindingName).ConfigureAwait(false); - } + // We do an "Evaluate" first so as to immediately create the script on the loaded + // page, then will add it to the initialization of future pages. + await this.EnableDomains().ConfigureAwait(false); + + string creationScript = PinnedScript.MakeCreationScript(newScriptHandle, script); + await this.session.Value.Domains.JavaScript.Evaluate(creationScript).ConfigureAwait(false); + string scriptId = await this.session.Value.Domains.JavaScript.AddScriptToEvaluateOnNewDocument(creationScript).ConfigureAwait(false); + + PinnedScript pinnedScript = new PinnedScript(script, newScriptHandle, scriptId); + this.pinnedScripts[pinnedScript.Handle] = pinnedScript; + return pinnedScript; + } - /// - /// Asynchronously removes a binding to a JavaScript callback. - /// - /// The name of the callback to be removed. - /// A task that represents the asynchronous operation. - /// If is . - public async Task RemoveScriptCallbackBinding(string bindingName) + /// + /// Unpins a previously pinned script from the browser. + /// + /// The object to unpin. + /// A task that represents the asynchronous operation. + /// If is . + public async Task UnpinScript(PinnedScript script) + { + if (script == null) { - if (bindingName is null) - { - throw new ArgumentNullException(nameof(bindingName)); - } + throw new ArgumentNullException(nameof(script)); + } - await this.session.Value.Domains.JavaScript.RemoveBinding(bindingName).ConfigureAwait(false); - _ = this.bindings.Remove(bindingName); + if (this.pinnedScripts.ContainsKey(script.Handle)) + { + await this.session.Value.Domains.JavaScript.Evaluate(script.MakeRemovalScript()).ConfigureAwait(false); + await this.session.Value.Domains.JavaScript.RemoveScriptToEvaluateOnNewDocument(script.ScriptId).ConfigureAwait(false); + this.pinnedScripts.Remove(script.Handle); } + } - /// - /// Asynchronously removes all bindings to JavaScript callbacks. - /// - /// A task that represents the asynchronous operation. - public async Task ClearScriptCallbackBindings() + /// + /// Asynchronously adds a binding to a callback method that will raise an event when the named + /// binding is called by JavaScript executing in the browser. + /// + /// The name of the callback that will trigger events when called by JavaScript executing in the browser. + /// A task that represents the asynchronous operation. + /// If is . + /// If A binding with the specified name already exists. + public async Task AddScriptCallbackBinding(string bindingName) + { + if (bindingName is null) { - // Use a copy of the list to prevent the iterator from becoming invalid - // when we modify the collection. - List bindingList = new List(this.bindings); - foreach (string binding in bindingList) - { - await this.RemoveScriptCallbackBinding(binding).ConfigureAwait(false); - } + throw new ArgumentNullException(nameof(bindingName)); } - /// - /// Asynchronously removes all bindings to JavaScript callbacks, all - /// initialization scripts from being loaded for each document, and unpins - /// all pinned scripts. - /// - /// A task that represents the asynchronous operation. - public async Task ClearAll() + if (!this.bindings.Add(bindingName)) { - await this.ClearPinnedScripts().ConfigureAwait(false); - await this.ClearInitializationScripts().ConfigureAwait(false); - await this.ClearScriptCallbackBindings().ConfigureAwait(false); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "A binding named {0} has already been added", bindingName)); } - /// - /// Asynchronously removes all bindings to JavaScript callbacks, all - /// initialization scripts from being loaded for each document, all - /// pinned scripts, and stops listening for events. - /// - /// A task that represents the asynchronous operation. - public async Task Reset() + await this.EnableDomains().ConfigureAwait(false); + await this.session.Value.Domains.JavaScript.AddBinding(bindingName).ConfigureAwait(false); + } + + /// + /// Asynchronously removes a binding to a JavaScript callback. + /// + /// The name of the callback to be removed. + /// A task that represents the asynchronous operation. + /// If is . + public async Task RemoveScriptCallbackBinding(string bindingName) + { + if (bindingName is null) { - this.StopEventMonitoring(); - await ClearAll().ConfigureAwait(false); + throw new ArgumentNullException(nameof(bindingName)); } - /// - /// Releases all resources associated with this . - /// - public void Dispose() + await this.session.Value.Domains.JavaScript.RemoveBinding(bindingName).ConfigureAwait(false); + _ = this.bindings.Remove(bindingName); + } + + /// + /// Asynchronously removes all bindings to JavaScript callbacks. + /// + /// A task that represents the asynchronous operation. + public async Task ClearScriptCallbackBindings() + { + // Use a copy of the list to prevent the iterator from becoming invalid + // when we modify the collection. + List bindingList = new List(this.bindings); + foreach (string binding in bindingList) { - this.Dispose(true); - GC.SuppressFinalize(this); + await this.RemoveScriptCallbackBinding(binding).ConfigureAwait(false); } + } + + /// + /// Asynchronously removes all bindings to JavaScript callbacks, all + /// initialization scripts from being loaded for each document, and unpins + /// all pinned scripts. + /// + /// A task that represents the asynchronous operation. + public async Task ClearAll() + { + await this.ClearPinnedScripts().ConfigureAwait(false); + await this.ClearInitializationScripts().ConfigureAwait(false); + await this.ClearScriptCallbackBindings().ConfigureAwait(false); + } - /// - /// Releases all resources associated with this . - /// - /// if the Dispose method was explicitly called; otherwise, . - protected virtual void Dispose(bool disposing) + /// + /// Asynchronously removes all bindings to JavaScript callbacks, all + /// initialization scripts from being loaded for each document, all + /// pinned scripts, and stops listening for events. + /// + /// A task that represents the asynchronous operation. + public async Task Reset() + { + this.StopEventMonitoring(); + await ClearAll().ConfigureAwait(false); + } + + /// + /// Releases all resources associated with this . + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases all resources associated with this . + /// + /// if the Dispose method was explicitly called; otherwise, . + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) { - if (!this.isDisposed) + if (disposing) { - if (disposing) + if (this.session.IsValueCreated) { - if (this.session.IsValueCreated) - { - this.session.Value.Dispose(); - } + this.session.Value.Dispose(); } - - this.isDisposed = true; } + + this.isDisposed = true; } + } - private async Task ClearPinnedScripts() + private async Task ClearPinnedScripts() + { + // Use a copy of the list to prevent the iterator from becoming invalid + // when we modify the collection. + List scriptHandles = new List(this.pinnedScripts.Keys); + foreach (string scriptHandle in scriptHandles) { - // Use a copy of the list to prevent the iterator from becoming invalid - // when we modify the collection. - List scriptHandles = new List(this.pinnedScripts.Keys); - foreach (string scriptHandle in scriptHandles) - { - await this.UnpinScript(this.pinnedScripts[scriptHandle]).ConfigureAwait(false); - } + await this.UnpinScript(this.pinnedScripts[scriptHandle]).ConfigureAwait(false); } + } - private async Task EnableDomains() + private async Task EnableDomains() + { + if (!this.isEnabled) { - if (!this.isEnabled) - { - await this.session.Value.Domains.JavaScript.EnablePage().ConfigureAwait(false); - await this.session.Value.Domains.JavaScript.EnableRuntime().ConfigureAwait(false); - this.isEnabled = true; - } + await this.session.Value.Domains.JavaScript.EnablePage().ConfigureAwait(false); + await this.session.Value.Domains.JavaScript.EnableRuntime().ConfigureAwait(false); + this.isEnabled = true; } + } - private static string GetMutationListenerScript() + private static string GetMutationListenerScript() + { + string listenerScript = string.Empty; + using (Stream resourceStream = ResourceUtilities.GetResourceStream("mutation-listener.js", "mutation-listener.js")) { - string listenerScript = string.Empty; - using (Stream resourceStream = ResourceUtilities.GetResourceStream("mutation-listener.js", "mutation-listener.js")) + using (StreamReader resourceReader = new StreamReader(resourceStream)) { - using (StreamReader resourceReader = new StreamReader(resourceStream)) - { - listenerScript = resourceReader.ReadToEnd(); - } + listenerScript = resourceReader.ReadToEnd(); } - - return listenerScript; } - private void OnScriptBindingCalled(object? sender, BindingCalledEventArgs e) - { - if (e.Name == MonitorBindingName) - { - DomMutationData valueChangeData = JsonSerializer.Deserialize(e.Payload) ?? throw new JsonException("DomMutationData returned null"); - var locator = By.CssSelector($"*[data-__webdriver_id='{valueChangeData.TargetId}']"); - valueChangeData.Element = driver.FindElements(locator).FirstOrDefault(); + return listenerScript; + } - this.DomMutated?.Invoke(this, new DomMutatedEventArgs(valueChangeData)); - } + private void OnScriptBindingCalled(object? sender, BindingCalledEventArgs e) + { + if (e.Name == MonitorBindingName) + { + DomMutationData valueChangeData = JsonSerializer.Deserialize(e.Payload) ?? throw new JsonException("DomMutationData returned null"); + var locator = By.CssSelector($"*[data-__webdriver_id='{valueChangeData.TargetId}']"); + valueChangeData.Element = driver.FindElements(locator).FirstOrDefault(); - this.JavaScriptCallbackExecuted?.Invoke(this, new JavaScriptCallbackExecutedEventArgs - ( - scriptPayload: e.Payload, - bindingName: e.Name - )); + this.DomMutated?.Invoke(this, new DomMutatedEventArgs(valueChangeData)); } - private void OnJavaScriptExceptionThrown(object? sender, ExceptionThrownEventArgs e) - { - this.JavaScriptExceptionThrown?.Invoke(this, new JavaScriptExceptionThrownEventArgs(e.Message)); - } + this.JavaScriptCallbackExecuted?.Invoke(this, new JavaScriptCallbackExecutedEventArgs + ( + scriptPayload: e.Payload, + bindingName: e.Name + )); + } + + private void OnJavaScriptExceptionThrown(object? sender, ExceptionThrownEventArgs e) + { + this.JavaScriptExceptionThrown?.Invoke(this, new JavaScriptExceptionThrownEventArgs(e.Message)); + } - private void OnConsoleApiCalled(object? sender, ConsoleApiCalledEventArgs e) + private void OnConsoleApiCalled(object? sender, ConsoleApiCalledEventArgs e) + { + if (this.JavaScriptConsoleApiCalled != null) { - if (this.JavaScriptConsoleApiCalled != null) + for (int i = 0; i < e.Arguments.Count; i++) { - for (int i = 0; i < e.Arguments.Count; i++) - { - this.JavaScriptConsoleApiCalled(this, new JavaScriptConsoleApiCalledEventArgs - ( - messageContent: e.Arguments[i].Value, - messageTimeStamp: e.Timestamp, - messageType: e.Type - )); - } + this.JavaScriptConsoleApiCalled(this, new JavaScriptConsoleApiCalledEventArgs + ( + messageContent: e.Arguments[i].Value, + messageTimeStamp: e.Timestamp, + messageType: e.Type + )); } } } diff --git a/dotnet/src/webdriver/JavaScriptException.cs b/dotnet/src/webdriver/JavaScriptException.cs index cf20af8c5d155..b432df5f187e1 100644 --- a/dotnet/src/webdriver/JavaScriptException.cs +++ b/dotnet/src/webdriver/JavaScriptException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents exceptions that are thrown when an error occurs during execution of JavaScript. +/// +[Serializable] +public class JavaScriptException : WebDriverException { /// - /// Represents exceptions that are thrown when an error occurs during execution of JavaScript. + /// Initializes a new instance of the class. /// - [Serializable] - public class JavaScriptException : WebDriverException + public JavaScriptException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public JavaScriptException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public JavaScriptException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public JavaScriptException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public JavaScriptException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public JavaScriptException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/JavaScriptExceptionThrownEventArgs.cs b/dotnet/src/webdriver/JavaScriptExceptionThrownEventArgs.cs index bf0e1da43fa26..4b54c9f2e7970 100644 --- a/dotnet/src/webdriver/JavaScriptExceptionThrownEventArgs.cs +++ b/dotnet/src/webdriver/JavaScriptExceptionThrownEventArgs.cs @@ -19,25 +19,24 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the JavaScriptExceptionThrown event. +/// +public class JavaScriptExceptionThrownEventArgs : EventArgs { /// - /// Provides data for the JavaScriptExceptionThrown event. + /// Initializes a new instance of the type. /// - public class JavaScriptExceptionThrownEventArgs : EventArgs + /// The message of the exception thrown by JavaScript executing in the browser. + public JavaScriptExceptionThrownEventArgs(string message) { - /// - /// Initializes a new instance of the type. - /// - /// The message of the exception thrown by JavaScript executing in the browser. - public JavaScriptExceptionThrownEventArgs(string message) - { - Message = message; - } - - /// - /// Gets or sets the message of the exception thrown by JavaScript executing in the browser. - /// - public string Message { get; set; } + Message = message; } + + /// + /// Gets or sets the message of the exception thrown by JavaScript executing in the browser. + /// + public string Message { get; set; } } diff --git a/dotnet/src/webdriver/Keys.cs b/dotnet/src/webdriver/Keys.cs index e8b1e30b047ec..b8e2ca9422b55 100644 --- a/dotnet/src/webdriver/Keys.cs +++ b/dotnet/src/webdriver/Keys.cs @@ -21,415 +21,414 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Representations of keys able to be pressed that are not text keys for sending to the browser. +/// +public static class Keys { /// - /// Representations of keys able to be pressed that are not text keys for sending to the browser. + /// Represents the NUL keystroke. + /// + public static readonly string Null = Convert.ToString(Convert.ToChar(0xE000, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Cancel keystroke. + /// + public static readonly string Cancel = Convert.ToString(Convert.ToChar(0xE001, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Help keystroke. + /// + public static readonly string Help = Convert.ToString(Convert.ToChar(0xE002, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Backspace key. + /// + public static readonly string Backspace = Convert.ToString(Convert.ToChar(0xE003, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Tab key. + /// + public static readonly string Tab = Convert.ToString(Convert.ToChar(0xE004, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Clear keystroke. + /// + public static readonly string Clear = Convert.ToString(Convert.ToChar(0xE005, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Return key. + /// + public static readonly string Return = Convert.ToString(Convert.ToChar(0xE006, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Enter key. + /// + public static readonly string Enter = Convert.ToString(Convert.ToChar(0xE007, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Shift key. + /// + public static readonly string Shift = Convert.ToString(Convert.ToChar(0xE008, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Shift key. + /// + public static readonly string LeftShift = Convert.ToString(Convert.ToChar(0xE008, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the Control key. + /// + public static readonly string Control = Convert.ToString(Convert.ToChar(0xE009, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Control key. + /// + public static readonly string LeftControl = Convert.ToString(Convert.ToChar(0xE009, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the Alt key. + /// + public static readonly string Alt = Convert.ToString(Convert.ToChar(0xE00A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Alt key. + /// + public static readonly string LeftAlt = Convert.ToString(Convert.ToChar(0xE00A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the Pause key. + /// + public static readonly string Pause = Convert.ToString(Convert.ToChar(0xE00B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Escape key. + /// + public static readonly string Escape = Convert.ToString(Convert.ToChar(0xE00C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Spacebar key. + /// + public static readonly string Space = Convert.ToString(Convert.ToChar(0xE00D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Page Up key. + /// + public static readonly string PageUp = Convert.ToString(Convert.ToChar(0xE00E, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Page Down key. + /// + public static readonly string PageDown = Convert.ToString(Convert.ToChar(0xE00F, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the End key. + /// + public static readonly string End = Convert.ToString(Convert.ToChar(0xE010, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Home key. + /// + public static readonly string Home = Convert.ToString(Convert.ToChar(0xE011, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the left arrow key. + /// + public static readonly string Left = Convert.ToString(Convert.ToChar(0xE012, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the left arrow key. + /// + public static readonly string ArrowLeft = Convert.ToString(Convert.ToChar(0xE012, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the up arrow key. + /// + public static readonly string Up = Convert.ToString(Convert.ToChar(0xE013, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the up arrow key. + /// + public static readonly string ArrowUp = Convert.ToString(Convert.ToChar(0xE013, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the right arrow key. + /// + public static readonly string Right = Convert.ToString(Convert.ToChar(0xE014, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the right arrow key. + /// + public static readonly string ArrowRight = Convert.ToString(Convert.ToChar(0xE014, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the down arrow key. + /// + public static readonly string Down = Convert.ToString(Convert.ToChar(0xE015, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the down arrow key. + /// + public static readonly string ArrowDown = Convert.ToString(Convert.ToChar(0xE015, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias + + /// + /// Represents the Insert key. + /// + public static readonly string Insert = Convert.ToString(Convert.ToChar(0xE016, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Delete key. + /// + public static readonly string Delete = Convert.ToString(Convert.ToChar(0xE017, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the semi-colon key. + /// + public static readonly string Semicolon = Convert.ToString(Convert.ToChar(0xE018, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the equal sign key. + /// + public static readonly string Equal = Convert.ToString(Convert.ToChar(0xE019, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + // Number pad keys + + /// + /// Represents the number pad 0 key. + /// + public static readonly string NumberPad0 = Convert.ToString(Convert.ToChar(0xE01A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 1 key. + /// + public static readonly string NumberPad1 = Convert.ToString(Convert.ToChar(0xE01B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 2 key. + /// + public static readonly string NumberPad2 = Convert.ToString(Convert.ToChar(0xE01C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 3 key. + /// + public static readonly string NumberPad3 = Convert.ToString(Convert.ToChar(0xE01D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 4 key. + /// + public static readonly string NumberPad4 = Convert.ToString(Convert.ToChar(0xE01E, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 5 key. + /// + public static readonly string NumberPad5 = Convert.ToString(Convert.ToChar(0xE01F, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 6 key. + /// + public static readonly string NumberPad6 = Convert.ToString(Convert.ToChar(0xE020, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 7 key. + /// + public static readonly string NumberPad7 = Convert.ToString(Convert.ToChar(0xE021, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 8 key. + /// + public static readonly string NumberPad8 = Convert.ToString(Convert.ToChar(0xE022, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad 9 key. + /// + public static readonly string NumberPad9 = Convert.ToString(Convert.ToChar(0xE023, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad multiplication key. + /// + public static readonly string Multiply = Convert.ToString(Convert.ToChar(0xE024, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad addition key. + /// + public static readonly string Add = Convert.ToString(Convert.ToChar(0xE025, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad thousands separator key. + /// + public static readonly string Separator = Convert.ToString(Convert.ToChar(0xE026, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad subtraction key. + /// + public static readonly string Subtract = Convert.ToString(Convert.ToChar(0xE027, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad decimal separator key. + /// + public static readonly string Decimal = Convert.ToString(Convert.ToChar(0xE028, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the number pad division key. + /// + public static readonly string Divide = Convert.ToString(Convert.ToChar(0xE029, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + // Function keys + + /// + /// Represents the function key F1. /// - public static class Keys + public static readonly string F1 = Convert.ToString(Convert.ToChar(0xE031, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F2. + /// + public static readonly string F2 = Convert.ToString(Convert.ToChar(0xE032, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F3. + /// + public static readonly string F3 = Convert.ToString(Convert.ToChar(0xE033, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F4. + /// + public static readonly string F4 = Convert.ToString(Convert.ToChar(0xE034, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F5. + /// + public static readonly string F5 = Convert.ToString(Convert.ToChar(0xE035, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F6. + /// + public static readonly string F6 = Convert.ToString(Convert.ToChar(0xE036, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F7. + /// + public static readonly string F7 = Convert.ToString(Convert.ToChar(0xE037, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F8. + /// + public static readonly string F8 = Convert.ToString(Convert.ToChar(0xE038, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F9. + /// + public static readonly string F9 = Convert.ToString(Convert.ToChar(0xE039, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F10. + /// + public static readonly string F10 = Convert.ToString(Convert.ToChar(0xE03A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F11. + /// + public static readonly string F11 = Convert.ToString(Convert.ToChar(0xE03B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key F12. + /// + public static readonly string F12 = Convert.ToString(Convert.ToChar(0xE03C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key META. + /// + public static readonly string Meta = Convert.ToString(Convert.ToChar(0xE03D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the function key COMMAND. + /// + public static readonly string Command = Convert.ToString(Convert.ToChar(0xE03D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Represents the Zenkaku/Hankaku key. + /// + public static readonly string ZenkakuHankaku = Convert.ToString(Convert.ToChar(0xE040, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + private static Dictionary? descriptions; + + /// + /// Gets the description of a specific key. + /// + /// The key value for which to get the description. + /// The description of the key. + /// If is . + internal static object GetDescription(string value) { - /// - /// Represents the NUL keystroke. - /// - public static readonly string Null = Convert.ToString(Convert.ToChar(0xE000, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Cancel keystroke. - /// - public static readonly string Cancel = Convert.ToString(Convert.ToChar(0xE001, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Help keystroke. - /// - public static readonly string Help = Convert.ToString(Convert.ToChar(0xE002, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Backspace key. - /// - public static readonly string Backspace = Convert.ToString(Convert.ToChar(0xE003, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Tab key. - /// - public static readonly string Tab = Convert.ToString(Convert.ToChar(0xE004, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Clear keystroke. - /// - public static readonly string Clear = Convert.ToString(Convert.ToChar(0xE005, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Return key. - /// - public static readonly string Return = Convert.ToString(Convert.ToChar(0xE006, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Enter key. - /// - public static readonly string Enter = Convert.ToString(Convert.ToChar(0xE007, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Shift key. - /// - public static readonly string Shift = Convert.ToString(Convert.ToChar(0xE008, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Shift key. - /// - public static readonly string LeftShift = Convert.ToString(Convert.ToChar(0xE008, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the Control key. - /// - public static readonly string Control = Convert.ToString(Convert.ToChar(0xE009, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Control key. - /// - public static readonly string LeftControl = Convert.ToString(Convert.ToChar(0xE009, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the Alt key. - /// - public static readonly string Alt = Convert.ToString(Convert.ToChar(0xE00A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Alt key. - /// - public static readonly string LeftAlt = Convert.ToString(Convert.ToChar(0xE00A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the Pause key. - /// - public static readonly string Pause = Convert.ToString(Convert.ToChar(0xE00B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Escape key. - /// - public static readonly string Escape = Convert.ToString(Convert.ToChar(0xE00C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Spacebar key. - /// - public static readonly string Space = Convert.ToString(Convert.ToChar(0xE00D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Page Up key. - /// - public static readonly string PageUp = Convert.ToString(Convert.ToChar(0xE00E, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Page Down key. - /// - public static readonly string PageDown = Convert.ToString(Convert.ToChar(0xE00F, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the End key. - /// - public static readonly string End = Convert.ToString(Convert.ToChar(0xE010, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Home key. - /// - public static readonly string Home = Convert.ToString(Convert.ToChar(0xE011, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the left arrow key. - /// - public static readonly string Left = Convert.ToString(Convert.ToChar(0xE012, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the left arrow key. - /// - public static readonly string ArrowLeft = Convert.ToString(Convert.ToChar(0xE012, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the up arrow key. - /// - public static readonly string Up = Convert.ToString(Convert.ToChar(0xE013, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the up arrow key. - /// - public static readonly string ArrowUp = Convert.ToString(Convert.ToChar(0xE013, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the right arrow key. - /// - public static readonly string Right = Convert.ToString(Convert.ToChar(0xE014, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the right arrow key. - /// - public static readonly string ArrowRight = Convert.ToString(Convert.ToChar(0xE014, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the down arrow key. - /// - public static readonly string Down = Convert.ToString(Convert.ToChar(0xE015, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the down arrow key. - /// - public static readonly string ArrowDown = Convert.ToString(Convert.ToChar(0xE015, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); // alias - - /// - /// Represents the Insert key. - /// - public static readonly string Insert = Convert.ToString(Convert.ToChar(0xE016, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Delete key. - /// - public static readonly string Delete = Convert.ToString(Convert.ToChar(0xE017, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the semi-colon key. - /// - public static readonly string Semicolon = Convert.ToString(Convert.ToChar(0xE018, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the equal sign key. - /// - public static readonly string Equal = Convert.ToString(Convert.ToChar(0xE019, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - // Number pad keys - - /// - /// Represents the number pad 0 key. - /// - public static readonly string NumberPad0 = Convert.ToString(Convert.ToChar(0xE01A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 1 key. - /// - public static readonly string NumberPad1 = Convert.ToString(Convert.ToChar(0xE01B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 2 key. - /// - public static readonly string NumberPad2 = Convert.ToString(Convert.ToChar(0xE01C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 3 key. - /// - public static readonly string NumberPad3 = Convert.ToString(Convert.ToChar(0xE01D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 4 key. - /// - public static readonly string NumberPad4 = Convert.ToString(Convert.ToChar(0xE01E, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 5 key. - /// - public static readonly string NumberPad5 = Convert.ToString(Convert.ToChar(0xE01F, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 6 key. - /// - public static readonly string NumberPad6 = Convert.ToString(Convert.ToChar(0xE020, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 7 key. - /// - public static readonly string NumberPad7 = Convert.ToString(Convert.ToChar(0xE021, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 8 key. - /// - public static readonly string NumberPad8 = Convert.ToString(Convert.ToChar(0xE022, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad 9 key. - /// - public static readonly string NumberPad9 = Convert.ToString(Convert.ToChar(0xE023, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad multiplication key. - /// - public static readonly string Multiply = Convert.ToString(Convert.ToChar(0xE024, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad addition key. - /// - public static readonly string Add = Convert.ToString(Convert.ToChar(0xE025, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad thousands separator key. - /// - public static readonly string Separator = Convert.ToString(Convert.ToChar(0xE026, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad subtraction key. - /// - public static readonly string Subtract = Convert.ToString(Convert.ToChar(0xE027, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad decimal separator key. - /// - public static readonly string Decimal = Convert.ToString(Convert.ToChar(0xE028, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the number pad division key. - /// - public static readonly string Divide = Convert.ToString(Convert.ToChar(0xE029, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - // Function keys - - /// - /// Represents the function key F1. - /// - public static readonly string F1 = Convert.ToString(Convert.ToChar(0xE031, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F2. - /// - public static readonly string F2 = Convert.ToString(Convert.ToChar(0xE032, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F3. - /// - public static readonly string F3 = Convert.ToString(Convert.ToChar(0xE033, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F4. - /// - public static readonly string F4 = Convert.ToString(Convert.ToChar(0xE034, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F5. - /// - public static readonly string F5 = Convert.ToString(Convert.ToChar(0xE035, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F6. - /// - public static readonly string F6 = Convert.ToString(Convert.ToChar(0xE036, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F7. - /// - public static readonly string F7 = Convert.ToString(Convert.ToChar(0xE037, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F8. - /// - public static readonly string F8 = Convert.ToString(Convert.ToChar(0xE038, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F9. - /// - public static readonly string F9 = Convert.ToString(Convert.ToChar(0xE039, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F10. - /// - public static readonly string F10 = Convert.ToString(Convert.ToChar(0xE03A, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F11. - /// - public static readonly string F11 = Convert.ToString(Convert.ToChar(0xE03B, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key F12. - /// - public static readonly string F12 = Convert.ToString(Convert.ToChar(0xE03C, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key META. - /// - public static readonly string Meta = Convert.ToString(Convert.ToChar(0xE03D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the function key COMMAND. - /// - public static readonly string Command = Convert.ToString(Convert.ToChar(0xE03D, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Represents the Zenkaku/Hankaku key. - /// - public static readonly string ZenkakuHankaku = Convert.ToString(Convert.ToChar(0xE040, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - private static Dictionary? descriptions; - - /// - /// Gets the description of a specific key. - /// - /// The key value for which to get the description. - /// The description of the key. - /// If is . - internal static object GetDescription(string value) + if (descriptions == null) + { + descriptions = new Dictionary(); + descriptions.Add(Null, "Null"); + descriptions.Add(Cancel, "Cancel"); + descriptions.Add(Help, "Help"); + descriptions.Add(Backspace, "Backspace"); + descriptions.Add(Tab, "Tab"); + descriptions.Add(Clear, "Clear"); + descriptions.Add(Return, "Return"); + descriptions.Add(Enter, "Enter"); + descriptions.Add(Shift, "Shift"); + descriptions.Add(Control, "Control"); + descriptions.Add(Alt, "Alt"); + descriptions.Add(Pause, "Pause"); + descriptions.Add(Escape, "Escape"); + descriptions.Add(Space, "Space"); + descriptions.Add(PageUp, "Page Up"); + descriptions.Add(PageDown, "PageDown"); + descriptions.Add(End, "End"); + descriptions.Add(Home, "Home"); + descriptions.Add(Left, "Left"); + descriptions.Add(Up, "Up"); + descriptions.Add(Right, "Right"); + descriptions.Add(Down, "Down"); + descriptions.Add(Insert, "Insert"); + descriptions.Add(Delete, "Delete"); + descriptions.Add(Semicolon, "Semicolon"); + descriptions.Add(Equal, "Equal"); + descriptions.Add(NumberPad0, "Number Pad 0"); + descriptions.Add(NumberPad1, "Number Pad 1"); + descriptions.Add(NumberPad2, "Number Pad 2"); + descriptions.Add(NumberPad3, "Number Pad 3"); + descriptions.Add(NumberPad4, "Number Pad 4"); + descriptions.Add(NumberPad5, "Number Pad 5"); + descriptions.Add(NumberPad6, "Number Pad 6"); + descriptions.Add(NumberPad7, "Number Pad 7"); + descriptions.Add(NumberPad8, "Number Pad 8"); + descriptions.Add(NumberPad9, "Number Pad 9"); + descriptions.Add(Multiply, "Multiply"); + descriptions.Add(Add, "Add"); + descriptions.Add(Separator, "Separator"); + descriptions.Add(Subtract, "Subtract"); + descriptions.Add(Decimal, "Decimal"); + descriptions.Add(Divide, "Divide"); + descriptions.Add(F1, "F1"); + descriptions.Add(F2, "F2"); + descriptions.Add(F3, "F3"); + descriptions.Add(F4, "F4"); + descriptions.Add(F5, "F5"); + descriptions.Add(F6, "F6"); + descriptions.Add(F7, "F7"); + descriptions.Add(F8, "F8"); + descriptions.Add(F9, "F9"); + descriptions.Add(F10, "F10"); + descriptions.Add(F11, "F11"); + descriptions.Add(F12, "F12"); + descriptions.Add(Meta, "Meta"); + descriptions.Add(Command, "Command"); + descriptions.Add(ZenkakuHankaku, "Zenkaku Hankaku"); + } + + if (descriptions.TryGetValue(value, out string? description)) { - if (descriptions == null) - { - descriptions = new Dictionary(); - descriptions.Add(Null, "Null"); - descriptions.Add(Cancel, "Cancel"); - descriptions.Add(Help, "Help"); - descriptions.Add(Backspace, "Backspace"); - descriptions.Add(Tab, "Tab"); - descriptions.Add(Clear, "Clear"); - descriptions.Add(Return, "Return"); - descriptions.Add(Enter, "Enter"); - descriptions.Add(Shift, "Shift"); - descriptions.Add(Control, "Control"); - descriptions.Add(Alt, "Alt"); - descriptions.Add(Pause, "Pause"); - descriptions.Add(Escape, "Escape"); - descriptions.Add(Space, "Space"); - descriptions.Add(PageUp, "Page Up"); - descriptions.Add(PageDown, "PageDown"); - descriptions.Add(End, "End"); - descriptions.Add(Home, "Home"); - descriptions.Add(Left, "Left"); - descriptions.Add(Up, "Up"); - descriptions.Add(Right, "Right"); - descriptions.Add(Down, "Down"); - descriptions.Add(Insert, "Insert"); - descriptions.Add(Delete, "Delete"); - descriptions.Add(Semicolon, "Semicolon"); - descriptions.Add(Equal, "Equal"); - descriptions.Add(NumberPad0, "Number Pad 0"); - descriptions.Add(NumberPad1, "Number Pad 1"); - descriptions.Add(NumberPad2, "Number Pad 2"); - descriptions.Add(NumberPad3, "Number Pad 3"); - descriptions.Add(NumberPad4, "Number Pad 4"); - descriptions.Add(NumberPad5, "Number Pad 5"); - descriptions.Add(NumberPad6, "Number Pad 6"); - descriptions.Add(NumberPad7, "Number Pad 7"); - descriptions.Add(NumberPad8, "Number Pad 8"); - descriptions.Add(NumberPad9, "Number Pad 9"); - descriptions.Add(Multiply, "Multiply"); - descriptions.Add(Add, "Add"); - descriptions.Add(Separator, "Separator"); - descriptions.Add(Subtract, "Subtract"); - descriptions.Add(Decimal, "Decimal"); - descriptions.Add(Divide, "Divide"); - descriptions.Add(F1, "F1"); - descriptions.Add(F2, "F2"); - descriptions.Add(F3, "F3"); - descriptions.Add(F4, "F4"); - descriptions.Add(F5, "F5"); - descriptions.Add(F6, "F6"); - descriptions.Add(F7, "F7"); - descriptions.Add(F8, "F8"); - descriptions.Add(F9, "F9"); - descriptions.Add(F10, "F10"); - descriptions.Add(F11, "F11"); - descriptions.Add(F12, "F12"); - descriptions.Add(Meta, "Meta"); - descriptions.Add(Command, "Command"); - descriptions.Add(ZenkakuHankaku, "Zenkaku Hankaku"); - } - - if (descriptions.TryGetValue(value, out string? description)) - { - return description; - } - - return value; + return description; } + + return value; } } diff --git a/dotnet/src/webdriver/LogEntry.cs b/dotnet/src/webdriver/LogEntry.cs index 39af12278fb60..3d69603bb37c0 100644 --- a/dotnet/src/webdriver/LogEntry.cs +++ b/dotnet/src/webdriver/LogEntry.cs @@ -21,82 +21,81 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents an entry in a log from a driver instance. +/// +public class LogEntry { + /// - /// Represents an entry in a log from a driver instance. + /// Initializes a new instance of the class. /// - public class LogEntry + private LogEntry() { + } - /// - /// Initializes a new instance of the class. - /// - private LogEntry() - { - } + /// + /// Gets the timestamp value of the log entry. + /// + public DateTime Timestamp { get; private set; } = DateTime.MinValue; - /// - /// Gets the timestamp value of the log entry. - /// - public DateTime Timestamp { get; private set; } = DateTime.MinValue; + /// + /// Gets the logging level of the log entry. + /// + public LogLevel Level { get; private set; } = LogLevel.All; - /// - /// Gets the logging level of the log entry. - /// - public LogLevel Level { get; private set; } = LogLevel.All; + /// + /// Gets the message of the log entry. + /// + public string Message { get; private set; } = string.Empty; - /// - /// Gets the message of the log entry. - /// - public string Message { get; private set; } = string.Empty; + private static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - private static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + /// + /// Returns a string that represents the current . + /// + /// A string that represents the current . + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "[{0:yyyy-MM-ddTHH:mm:ssZ}] [{1}] {2}", this.Timestamp, this.Level, this.Message); + } - /// - /// Returns a string that represents the current . - /// - /// A string that represents the current . - public override string ToString() + /// + /// Creates a from a dictionary as deserialized from JSON. + /// + /// The from + /// which to create the . + /// A with the values in the dictionary. + internal static LogEntry FromDictionary(Dictionary entryDictionary) + { + LogEntry entry = new LogEntry(); + if (entryDictionary.TryGetValue("message", out object? message)) { - return string.Format(CultureInfo.InvariantCulture, "[{0:yyyy-MM-ddTHH:mm:ssZ}] [{1}] {2}", this.Timestamp, this.Level, this.Message); + entry.Message = message?.ToString() ?? string.Empty; } - /// - /// Creates a from a dictionary as deserialized from JSON. - /// - /// The from - /// which to create the . - /// A with the values in the dictionary. - internal static LogEntry FromDictionary(Dictionary entryDictionary) + if (entryDictionary.TryGetValue("timestamp", out object? timestamp)) { - LogEntry entry = new LogEntry(); - if (entryDictionary.TryGetValue("message", out object? message)) - { - entry.Message = message?.ToString() ?? string.Empty; - } + double timestampValue = Convert.ToDouble(timestamp, CultureInfo.InvariantCulture); + entry.Timestamp = UnixEpoch.AddMilliseconds(timestampValue); + } - if (entryDictionary.TryGetValue("timestamp", out object? timestamp)) + if (entryDictionary.TryGetValue("level", out object? level)) + { + if (Enum.TryParse(level?.ToString(), ignoreCase: true, out LogLevel result)) { - double timestampValue = Convert.ToDouble(timestamp, CultureInfo.InvariantCulture); - entry.Timestamp = UnixEpoch.AddMilliseconds(timestampValue); + entry.Level = result; } - - if (entryDictionary.TryGetValue("level", out object? level)) + else { - if (Enum.TryParse(level?.ToString(), ignoreCase: true, out LogLevel result)) - { - entry.Level = result; - } - else - { - // If the requested log level string is not a valid log level, - // ignore it and use LogLevel.All. - entry.Level = LogLevel.All; - } + // If the requested log level string is not a valid log level, + // ignore it and use LogLevel.All. + entry.Level = LogLevel.All; } - - return entry; } + + return entry; } } diff --git a/dotnet/src/webdriver/LogLevel.cs b/dotnet/src/webdriver/LogLevel.cs index 962d32cae682e..56f17c55a1ccd 100644 --- a/dotnet/src/webdriver/LogLevel.cs +++ b/dotnet/src/webdriver/LogLevel.cs @@ -17,41 +17,40 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the levels of logging available to driver instances. +/// +public enum LogLevel { /// - /// Represents the levels of logging available to driver instances. + /// Show all log messages. /// - public enum LogLevel - { - /// - /// Show all log messages. - /// - All, + All, - /// - /// Show messages with information useful for debugging. - /// - Debug, + /// + /// Show messages with information useful for debugging. + /// + Debug, - /// - /// Show informational messages. - /// - Info, + /// + /// Show informational messages. + /// + Info, - /// - /// Show messages corresponding to non-critical issues. - /// - Warning, + /// + /// Show messages corresponding to non-critical issues. + /// + Warning, - /// - /// Show messages corresponding to critical issues. - /// - Severe, + /// + /// Show messages corresponding to critical issues. + /// + Severe, - /// - /// Show no log messages. - /// - Off - } + /// + /// Show no log messages. + /// + Off } diff --git a/dotnet/src/webdriver/LogType.cs b/dotnet/src/webdriver/LogType.cs index 9d87c62638890..0c8dd93bfc1ff 100644 --- a/dotnet/src/webdriver/LogType.cs +++ b/dotnet/src/webdriver/LogType.cs @@ -17,41 +17,40 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Class containing names of common log types. +/// +public static class LogType { /// - /// Class containing names of common log types. + /// Log messages from the client language bindings. /// - public static class LogType - { - /// - /// Log messages from the client language bindings. - /// - public static readonly string Client = "client"; + public static readonly string Client = "client"; - /// - /// Logs from the current WebDriver instance. - /// - public static readonly string Driver = "driver"; + /// + /// Logs from the current WebDriver instance. + /// + public static readonly string Driver = "driver"; - /// - /// Logs from the browser. - /// - public static readonly string Browser = "browser"; + /// + /// Logs from the browser. + /// + public static readonly string Browser = "browser"; - /// - /// Logs from the server. - /// - public static readonly string Server = "server"; + /// + /// Logs from the server. + /// + public static readonly string Server = "server"; - /// - /// Profiling logs. - /// - public static readonly string Profiler = "profiler"; + /// + /// Profiling logs. + /// + public static readonly string Profiler = "profiler"; - /// - /// Performance logs. - /// - public static readonly string Performance = "performance"; - } + /// + /// Performance logs. + /// + public static readonly string Performance = "performance"; } diff --git a/dotnet/src/webdriver/Logs.cs b/dotnet/src/webdriver/Logs.cs index 32157f20e56ed..f72f194ad5d9d 100644 --- a/dotnet/src/webdriver/Logs.cs +++ b/dotnet/src/webdriver/Logs.cs @@ -21,85 +21,84 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for examining logs for the driver during the test. +/// +public class Logs : ILogs { + private readonly WebDriver driver; + /// - /// Provides a mechanism for examining logs for the driver during the test. + /// Initializes a new instance of the class. /// - public class Logs : ILogs + /// Instance of the driver currently in use + /// If is . + public Logs(WebDriver driver) { - private readonly WebDriver driver; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the driver currently in use - /// If is . - public Logs(WebDriver driver) - { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - /// - /// Gets the list of available log types for this driver. - /// - public ReadOnlyCollection AvailableLogTypes + /// + /// Gets the list of available log types for this driver. + /// + public ReadOnlyCollection AvailableLogTypes + { + get { - get + List availableLogTypes = new List(); + try { - List availableLogTypes = new List(); - try + Response commandResponse = this.driver.Execute(DriverCommand.GetAvailableLogTypes, null); + if (commandResponse.Value is object[] responseValue) { - Response commandResponse = this.driver.Execute(DriverCommand.GetAvailableLogTypes, null); - if (commandResponse.Value is object[] responseValue) + foreach (object logKind in responseValue) { - foreach (object logKind in responseValue) - { - availableLogTypes.Add(logKind.ToString()!); - } + availableLogTypes.Add(logKind.ToString()!); } } - catch (NotImplementedException) - { - // Swallow for backwards compatibility - } - - return availableLogTypes.AsReadOnly(); } + catch (NotImplementedException) + { + // Swallow for backwards compatibility + } + + return availableLogTypes.AsReadOnly(); } + } - /// - /// Gets the set of objects for a specified log. - /// - /// The log for which to retrieve the log entries. - /// Log types can be found in the class. - /// The list of objects for the specified log. - /// If is . - public ReadOnlyCollection GetLog(string logKind) + /// + /// Gets the set of objects for a specified log. + /// + /// The log for which to retrieve the log entries. + /// Log types can be found in the class. + /// The list of objects for the specified log. + /// If is . + public ReadOnlyCollection GetLog(string logKind) + { + if (logKind is null) { - if (logKind is null) - { - throw new ArgumentNullException(nameof(logKind)); - } + throw new ArgumentNullException(nameof(logKind)); + } - List entries = new List(); + List entries = new List(); - Dictionary parameters = new Dictionary(); - parameters.Add("type", logKind); - Response commandResponse = this.driver.Execute(DriverCommand.GetLog, parameters); + Dictionary parameters = new Dictionary(); + parameters.Add("type", logKind); + Response commandResponse = this.driver.Execute(DriverCommand.GetLog, parameters); - if (commandResponse.Value is object?[] responseValue) + if (commandResponse.Value is object?[] responseValue) + { + foreach (object? rawEntry in responseValue) { - foreach (object? rawEntry in responseValue) + if (rawEntry is Dictionary entryDictionary) { - if (rawEntry is Dictionary entryDictionary) - { - entries.Add(LogEntry.FromDictionary(entryDictionary)); - } + entries.Add(LogEntry.FromDictionary(entryDictionary)); } } - - return entries.AsReadOnly(); } + + return entries.AsReadOnly(); } } diff --git a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs index 0c445ed7acb83..64c01324ee266 100644 --- a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs +++ b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs @@ -19,44 +19,43 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents exceptions that are thrown when an attempt is made to move the mouse +/// pointer outside the bounds of the browser view port. +/// +[Serializable] +public class MoveTargetOutOfBoundsException : WebDriverException { /// - /// Represents exceptions that are thrown when an attempt is made to move the mouse - /// pointer outside the bounds of the browser view port. + /// Initializes a new instance of the class. /// - [Serializable] - public class MoveTargetOutOfBoundsException : WebDriverException + public MoveTargetOutOfBoundsException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public MoveTargetOutOfBoundsException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public MoveTargetOutOfBoundsException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public MoveTargetOutOfBoundsException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public MoveTargetOutOfBoundsException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public MoveTargetOutOfBoundsException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/Navigator.cs b/dotnet/src/webdriver/Navigator.cs index e17f4e3b210c9..95ebc2a3af741 100644 --- a/dotnet/src/webdriver/Navigator.cs +++ b/dotnet/src/webdriver/Navigator.cs @@ -21,146 +21,145 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for Navigating with the driver. +/// +internal sealed class Navigator : INavigation { + private readonly WebDriver driver; + /// - /// Provides a mechanism for Navigating with the driver. + /// Initializes a new instance of the class /// - internal sealed class Navigator : INavigation + /// Driver in use + /// If is null. + public Navigator(WebDriver driver) { - private readonly WebDriver driver; - - /// - /// Initializes a new instance of the class - /// - /// Driver in use - /// If is null. - public Navigator(WebDriver driver) - { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - /// - /// Move back a single entry in the browser's history. - /// - public void Back() + /// + /// Move back a single entry in the browser's history. + /// + public void Back() + { + Task.Run(async delegate { - Task.Run(async delegate - { - await this.BackAsync(); - }).GetAwaiter().GetResult(); - } + await this.BackAsync(); + }).GetAwaiter().GetResult(); + } - /// - /// Move back a single entry in the browser's history as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - public async Task BackAsync() - { - await this.driver.ExecuteAsync(DriverCommand.GoBack, null).ConfigureAwait(false); - } + /// + /// Move back a single entry in the browser's history as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + public async Task BackAsync() + { + await this.driver.ExecuteAsync(DriverCommand.GoBack, null).ConfigureAwait(false); + } - /// - /// Move a single "item" forward in the browser's history. - /// - public void Forward() + /// + /// Move a single "item" forward in the browser's history. + /// + public void Forward() + { + Task.Run(async delegate { - Task.Run(async delegate - { - await this.ForwardAsync(); - }).GetAwaiter().GetResult(); - } + await this.ForwardAsync(); + }).GetAwaiter().GetResult(); + } - /// - /// Move a single "item" forward in the browser's history as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - public async Task ForwardAsync() - { - await this.driver.ExecuteAsync(DriverCommand.GoForward, null).ConfigureAwait(false); - } + /// + /// Move a single "item" forward in the browser's history as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + public async Task ForwardAsync() + { + await this.driver.ExecuteAsync(DriverCommand.GoForward, null).ConfigureAwait(false); + } - /// - /// Navigate to a url. - /// - /// String of where you want the browser to go to - /// If is . - public void GoToUrl(string url) + /// + /// Navigate to a url. + /// + /// String of where you want the browser to go to + /// If is . + public void GoToUrl(string url) + { + Task.Run(async delegate { - Task.Run(async delegate - { - await this.GoToUrlAsync(url); - }).GetAwaiter().GetResult(); - } + await this.GoToUrlAsync(url); + }).GetAwaiter().GetResult(); + } - /// - /// Navigate to a url as an asynchronous task. - /// - /// String of where you want the browser to go. - /// A task object representing the asynchronous operation. - /// If is . - public async Task GoToUrlAsync(string url) + /// + /// Navigate to a url as an asynchronous task. + /// + /// String of where you want the browser to go. + /// A task object representing the asynchronous operation. + /// If is . + public async Task GoToUrlAsync(string url) + { + if (url == null) { - if (url == null) - { - throw new ArgumentNullException(nameof(url), "URL cannot be null."); - } - - Dictionary parameters = new Dictionary - { - { "url", url } - }; - await this.driver.ExecuteAsync(DriverCommand.Get, parameters).ConfigureAwait(false); + throw new ArgumentNullException(nameof(url), "URL cannot be null."); } - /// - /// Navigate to a url. - /// - /// Uri object of where you want the browser to go. - /// If is . - public void GoToUrl(Uri url) + Dictionary parameters = new Dictionary { - Task.Run(async delegate - { - await this.GoToUrlAsync(url); - }).GetAwaiter().GetResult(); - } + { "url", url } + }; + await this.driver.ExecuteAsync(DriverCommand.Get, parameters).ConfigureAwait(false); + } - /// - /// Navigate to a url as an asynchronous task. - /// - /// Uri object of where you want the browser to go. - /// A task object representing the asynchronous operation. - /// If is . - public async Task GoToUrlAsync(Uri url) + /// + /// Navigate to a url. + /// + /// Uri object of where you want the browser to go. + /// If is . + public void GoToUrl(Uri url) + { + Task.Run(async delegate { - if (url == null) - { - throw new ArgumentNullException(nameof(url), "URL cannot be null."); - } - - await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false); - } + await this.GoToUrlAsync(url); + }).GetAwaiter().GetResult(); + } - /// - /// Reload the current page. - /// - public void Refresh() + /// + /// Navigate to a url as an asynchronous task. + /// + /// Uri object of where you want the browser to go. + /// A task object representing the asynchronous operation. + /// If is . + public async Task GoToUrlAsync(Uri url) + { + if (url == null) { - Task.Run(async delegate - { - await this.RefreshAsync(); - }).GetAwaiter().GetResult(); + throw new ArgumentNullException(nameof(url), "URL cannot be null."); } - /// - /// Reload the current page as an asynchronous task. - /// - /// A task object representing the asynchronous operation. - public async Task RefreshAsync() + await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false); + } + + /// + /// Reload the current page. + /// + public void Refresh() + { + Task.Run(async delegate { - // driver.SwitchTo().DefaultContent(); - await this.driver.ExecuteAsync(DriverCommand.Refresh, null).ConfigureAwait(false); - } + await this.RefreshAsync(); + }).GetAwaiter().GetResult(); + } + + /// + /// Reload the current page as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + public async Task RefreshAsync() + { + // driver.SwitchTo().DefaultContent(); + await this.driver.ExecuteAsync(DriverCommand.Refresh, null).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/NetworkAuthenticationHandler.cs b/dotnet/src/webdriver/NetworkAuthenticationHandler.cs index 6725cdf8164b3..60dbada0ace10 100644 --- a/dotnet/src/webdriver/NetworkAuthenticationHandler.cs +++ b/dotnet/src/webdriver/NetworkAuthenticationHandler.cs @@ -20,25 +20,24 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Allows a user to supply authentication information for network requests. +/// +public class NetworkAuthenticationHandler { /// - /// Allows a user to supply authentication information for network requests. + /// Gets or sets a function that takes a object, and returns a + /// value indicating whether the supplied URI matches the specified criteria. /// - public class NetworkAuthenticationHandler - { - /// - /// Gets or sets a function that takes a object, and returns a - /// value indicating whether the supplied URI matches the specified criteria. - /// - [DisallowNull] - public Func? UriMatcher { get; set; } + [DisallowNull] + public Func? UriMatcher { get; set; } - /// - /// Gets or sets the credentials to use when responding to an authentication request - /// that matches the specified criteria. - /// - [DisallowNull] - public ICredentials? Credentials { get; set; } - } + /// + /// Gets or sets the credentials to use when responding to an authentication request + /// that matches the specified criteria. + /// + [DisallowNull] + public ICredentials? Credentials { get; set; } } diff --git a/dotnet/src/webdriver/NetworkManager.cs b/dotnet/src/webdriver/NetworkManager.cs index 2567991a5762e..26f84db946d24 100644 --- a/dotnet/src/webdriver/NetworkManager.cs +++ b/dotnet/src/webdriver/NetworkManager.cs @@ -23,258 +23,257 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides methods for monitoring, intercepting, and modifying network requests and responses. +/// +public class NetworkManager : INetwork { + private readonly Lazy session; + private readonly List requestHandlers = new List(); + private readonly List responseHandlers = new List(); + private readonly List authenticationHandlers = new List(); + /// - /// Provides methods for monitoring, intercepting, and modifying network requests and responses. + /// Initializes a new instance of the class. /// - public class NetworkManager : INetwork + /// The instance on which the network should be monitored. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] + [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] + public NetworkManager(IWebDriver driver) { - private readonly Lazy session; - private readonly List requestHandlers = new List(); - private readonly List responseHandlers = new List(); - private readonly List authenticationHandlers = new List(); - - /// - /// Initializes a new instance of the class. - /// - /// The instance on which the network should be monitored. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] - [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = "Warnings are added to StartMonitoring and StopMonitoring")] - public NetworkManager(IWebDriver driver) + // Use of Lazy means this exception won't be thrown until the user first + // attempts to access the DevTools session, probably on the first call to + // StartMonitoring(). + this.session = new Lazy(() => { - // Use of Lazy means this exception won't be thrown until the user first - // attempts to access the DevTools session, probably on the first call to - // StartMonitoring(). - this.session = new Lazy(() => + if (driver is not IDevTools devToolsDriver) { - if (driver is not IDevTools devToolsDriver) - { - throw new WebDriverException("Driver must implement IDevTools to use these features"); - } + throw new WebDriverException("Driver must implement IDevTools to use these features"); + } - return devToolsDriver.GetDevToolsSession(); - }); - } + return devToolsDriver.GetDevToolsSession(); + }); + } - /// - /// Occurs when a browser sends a network request. - /// - public event EventHandler? NetworkRequestSent; - - /// - /// Occurs when a browser receives a network response. - /// - public event EventHandler? NetworkResponseReceived; - - /// - /// Asynchronously starts monitoring for network traffic. - /// - /// A task that represents the asynchronous operation. - [RequiresUnreferencedCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] - [RequiresDynamicCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] - public async Task StartMonitoring() + /// + /// Occurs when a browser sends a network request. + /// + public event EventHandler? NetworkRequestSent; + + /// + /// Occurs when a browser receives a network response. + /// + public event EventHandler? NetworkResponseReceived; + + /// + /// Asynchronously starts monitoring for network traffic. + /// + /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("NetworkManager is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] + public async Task StartMonitoring() + { + this.session.Value.Domains.Network.RequestPaused += OnRequestPaused; + this.session.Value.Domains.Network.AuthRequired += OnAuthRequired; + this.session.Value.Domains.Network.ResponsePaused += OnResponsePaused; + await this.session.Value.Domains.Network.EnableFetchForAllPatterns().ConfigureAwait(false); + await this.session.Value.Domains.Network.EnableNetwork().ConfigureAwait(false); + await this.session.Value.Domains.Network.DisableNetworkCaching().ConfigureAwait(false); + } + + /// + /// Asynchronously stops monitoring for network traffic. + /// + /// A task that represents the asynchronous operation. + [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] + [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] + public async Task StopMonitoring() + { + this.session.Value.Domains.Network.ResponsePaused -= OnResponsePaused; + this.session.Value.Domains.Network.AuthRequired -= OnAuthRequired; + this.session.Value.Domains.Network.RequestPaused -= OnRequestPaused; + await this.session.Value.Domains.Network.EnableNetworkCaching().ConfigureAwait(false); + } + + /// + /// Adds a to examine incoming network requests, + /// and optionally modify the request or provide a response. + /// + /// The to add. + /// If is . + public void AddRequestHandler(NetworkRequestHandler handler) + { + if (handler == null) { - this.session.Value.Domains.Network.RequestPaused += OnRequestPaused; - this.session.Value.Domains.Network.AuthRequired += OnAuthRequired; - this.session.Value.Domains.Network.ResponsePaused += OnResponsePaused; - await this.session.Value.Domains.Network.EnableFetchForAllPatterns().ConfigureAwait(false); - await this.session.Value.Domains.Network.EnableNetwork().ConfigureAwait(false); - await this.session.Value.Domains.Network.DisableNetworkCaching().ConfigureAwait(false); + throw new ArgumentNullException(nameof(handler), "Request handler cannot be null"); } - /// - /// Asynchronously stops monitoring for network traffic. - /// - /// A task that represents the asynchronous operation. - [RequiresUnreferencedCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported")] - [RequiresDynamicCode("Network monitoring is currently implemented with CDP. When it is implemented with BiDi, AOT will be supported.")] - public async Task StopMonitoring() + if (handler.RequestMatcher == null) { - this.session.Value.Domains.Network.ResponsePaused -= OnResponsePaused; - this.session.Value.Domains.Network.AuthRequired -= OnAuthRequired; - this.session.Value.Domains.Network.RequestPaused -= OnRequestPaused; - await this.session.Value.Domains.Network.EnableNetworkCaching().ConfigureAwait(false); + throw new ArgumentException("Matcher for request cannot be null", nameof(handler)); } - /// - /// Adds a to examine incoming network requests, - /// and optionally modify the request or provide a response. - /// - /// The to add. - /// If is . - public void AddRequestHandler(NetworkRequestHandler handler) + if (handler.RequestTransformer == null && handler.ResponseSupplier == null) { - if (handler == null) - { - throw new ArgumentNullException(nameof(handler), "Request handler cannot be null"); - } + throw new ArgumentException("Request transformer and response supplier cannot both be null", nameof(handler)); + } - if (handler.RequestMatcher == null) - { - throw new ArgumentException("Matcher for request cannot be null", nameof(handler)); - } + this.requestHandlers.Add(handler); + } - if (handler.RequestTransformer == null && handler.ResponseSupplier == null) - { - throw new ArgumentException("Request transformer and response supplier cannot both be null", nameof(handler)); - } + /// + /// Clears all added instances. + /// + public void ClearRequestHandlers() + { + this.requestHandlers.Clear(); + } - this.requestHandlers.Add(handler); + /// + /// Adds a to supply authentication + /// credentials for network requests. + /// + /// The to add. + public void AddAuthenticationHandler(NetworkAuthenticationHandler handler) + { + if (handler == null) + { + throw new ArgumentNullException(nameof(handler), "Authentication handler cannot be null"); } - /// - /// Clears all added instances. - /// - public void ClearRequestHandlers() + if (handler.UriMatcher == null) { - this.requestHandlers.Clear(); + throw new ArgumentException("Matcher for delegate for URL cannot be null", nameof(handler)); } - /// - /// Adds a to supply authentication - /// credentials for network requests. - /// - /// The to add. - public void AddAuthenticationHandler(NetworkAuthenticationHandler handler) + if (handler.Credentials == null) { - if (handler == null) - { - throw new ArgumentNullException(nameof(handler), "Authentication handler cannot be null"); - } + throw new ArgumentException("Credentials to use for authentication cannot be null", nameof(handler)); + } - if (handler.UriMatcher == null) - { - throw new ArgumentException("Matcher for delegate for URL cannot be null", nameof(handler)); - } + if (handler.Credentials is not PasswordCredentials) + { + throw new ArgumentException("Credentials must contain user name and password (PasswordCredentials)", nameof(handler)); + } - if (handler.Credentials == null) - { - throw new ArgumentException("Credentials to use for authentication cannot be null", nameof(handler)); - } + this.authenticationHandlers.Add(handler); + } - if (handler.Credentials is not PasswordCredentials) - { - throw new ArgumentException("Credentials must contain user name and password (PasswordCredentials)", nameof(handler)); - } + /// + /// Clears all added instances. + /// + public void ClearAuthenticationHandlers() + { + this.authenticationHandlers.Clear(); + } - this.authenticationHandlers.Add(handler); + /// + /// Adds a to examine received network responses, + /// and optionally modify the response. + /// + /// The to add. + public void AddResponseHandler(NetworkResponseHandler handler) + { + if (handler == null) + { + throw new ArgumentNullException(nameof(handler), "Request handler cannot be null"); } - /// - /// Clears all added instances. - /// - public void ClearAuthenticationHandlers() + if (handler.ResponseMatcher == null) { - this.authenticationHandlers.Clear(); + throw new ArgumentException("Matcher for response cannot be null", nameof(handler)); } - /// - /// Adds a to examine received network responses, - /// and optionally modify the response. - /// - /// The to add. - public void AddResponseHandler(NetworkResponseHandler handler) - { - if (handler == null) - { - throw new ArgumentNullException(nameof(handler), "Request handler cannot be null"); - } + this.responseHandlers.Add(handler); + } - if (handler.ResponseMatcher == null) + /// + /// Clears all added instances. + /// + public void ClearResponseHandlers() + { + this.responseHandlers.Clear(); + } + + private async Task OnAuthRequired(object sender, AuthRequiredEventArgs e) + { + string requestId = e.RequestId; + Uri uri = new Uri(e.Uri); + bool successfullyAuthenticated = false; + foreach (var authenticationHandler in this.authenticationHandlers) + { + if (authenticationHandler.UriMatcher!.Invoke(uri)) { - throw new ArgumentException("Matcher for response cannot be null", nameof(handler)); + PasswordCredentials credentials = (PasswordCredentials)authenticationHandler.Credentials!; + await this.session.Value.Domains.Network.ContinueWithAuth(e.RequestId, credentials.UserName, credentials.Password).ConfigureAwait(false); + successfullyAuthenticated = true; + break; } - - this.responseHandlers.Add(handler); } - /// - /// Clears all added instances. - /// - public void ClearResponseHandlers() + if (!successfullyAuthenticated) { - this.responseHandlers.Clear(); + await this.session.Value.Domains.Network.CancelAuth(e.RequestId).ConfigureAwait(false); } + } - private async Task OnAuthRequired(object sender, AuthRequiredEventArgs e) + private async Task OnRequestPaused(object sender, RequestPausedEventArgs e) + { + if (this.NetworkRequestSent != null) { - string requestId = e.RequestId; - Uri uri = new Uri(e.Uri); - bool successfullyAuthenticated = false; - foreach (var authenticationHandler in this.authenticationHandlers) - { - if (authenticationHandler.UriMatcher!.Invoke(uri)) - { - PasswordCredentials credentials = (PasswordCredentials)authenticationHandler.Credentials!; - await this.session.Value.Domains.Network.ContinueWithAuth(e.RequestId, credentials.UserName, credentials.Password).ConfigureAwait(false); - successfullyAuthenticated = true; - break; - } - } - - if (!successfullyAuthenticated) - { - await this.session.Value.Domains.Network.CancelAuth(e.RequestId).ConfigureAwait(false); - } + this.NetworkRequestSent(this, new NetworkRequestSentEventArgs(e.RequestData)); } - private async Task OnRequestPaused(object sender, RequestPausedEventArgs e) + foreach (var handler in this.requestHandlers) { - if (this.NetworkRequestSent != null) + if (handler.RequestMatcher!.Invoke(e.RequestData)) { - this.NetworkRequestSent(this, new NetworkRequestSentEventArgs(e.RequestData)); - } + if (handler.RequestTransformer != null) + { + await this.session.Value.Domains.Network.ContinueRequest(handler.RequestTransformer(e.RequestData)).ConfigureAwait(false); + return; + } - foreach (var handler in this.requestHandlers) - { - if (handler.RequestMatcher!.Invoke(e.RequestData)) + if (handler.ResponseSupplier != null) { - if (handler.RequestTransformer != null) - { - await this.session.Value.Domains.Network.ContinueRequest(handler.RequestTransformer(e.RequestData)).ConfigureAwait(false); - return; - } - - if (handler.ResponseSupplier != null) - { - await this.session.Value.Domains.Network.ContinueRequestWithResponse(e.RequestData, handler.ResponseSupplier(e.RequestData)).ConfigureAwait(false); - return; - } + await this.session.Value.Domains.Network.ContinueRequestWithResponse(e.RequestData, handler.ResponseSupplier(e.RequestData)).ConfigureAwait(false); + return; } } - - await this.session.Value.Domains.Network.ContinueRequestWithoutModification(e.RequestData).ConfigureAwait(false); } - private async Task OnResponsePaused(object sender, ResponsePausedEventArgs e) + await this.session.Value.Domains.Network.ContinueRequestWithoutModification(e.RequestData).ConfigureAwait(false); + } + + private async Task OnResponsePaused(object sender, ResponsePausedEventArgs e) + { + if (e.ResponseData.Headers.Count > 0) { - if (e.ResponseData.Headers.Count > 0) - { - // If no headers are present, the body cannot be retrieved. - await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData).ConfigureAwait(false); - } + // If no headers are present, the body cannot be retrieved. + await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData).ConfigureAwait(false); + } - if (this.NetworkResponseReceived != null) - { - this.NetworkResponseReceived(this, new NetworkResponseReceivedEventArgs(e.ResponseData)); - } + if (this.NetworkResponseReceived != null) + { + this.NetworkResponseReceived(this, new NetworkResponseReceivedEventArgs(e.ResponseData)); + } - foreach (var handler in this.responseHandlers) + foreach (var handler in this.responseHandlers) + { + if (handler.ResponseMatcher!.Invoke(e.ResponseData)) { - if (handler.ResponseMatcher!.Invoke(e.ResponseData)) - { - // NOTE: We create a dummy HttpRequestData object here, because the ContinueRequestWithResponse - // method demands one; however, the only property used by that method is the RequestId property. - // It might be better to refactor that method signature to simply pass the request ID, or - // alternatively, just pass the response data, which should also contain the request ID anyway. - HttpRequestData requestData = new HttpRequestData { RequestId = e.ResponseData.RequestId }; - await this.session.Value.Domains.Network.ContinueRequestWithResponse(requestData, handler.ResponseTransformer!(e.ResponseData)).ConfigureAwait(false); - return; - } + // NOTE: We create a dummy HttpRequestData object here, because the ContinueRequestWithResponse + // method demands one; however, the only property used by that method is the RequestId property. + // It might be better to refactor that method signature to simply pass the request ID, or + // alternatively, just pass the response data, which should also contain the request ID anyway. + HttpRequestData requestData = new HttpRequestData { RequestId = e.ResponseData.RequestId }; + await this.session.Value.Domains.Network.ContinueRequestWithResponse(requestData, handler.ResponseTransformer!(e.ResponseData)).ConfigureAwait(false); + return; } - - await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData).ConfigureAwait(false); } + + await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/NetworkRequestHandler.cs b/dotnet/src/webdriver/NetworkRequestHandler.cs index bab0d7b6a4bb1..f0be0a43de469 100644 --- a/dotnet/src/webdriver/NetworkRequestHandler.cs +++ b/dotnet/src/webdriver/NetworkRequestHandler.cs @@ -20,32 +20,31 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Allows a user to handle a network request, potentially modifying the request or providing a known response. +/// +public class NetworkRequestHandler { /// - /// Allows a user to handle a network request, potentially modifying the request or providing a known response. + /// Gets or sets a function that evaluates request data in an object, + /// and returns a value indicating whether the data matches the specified criteria. /// - public class NetworkRequestHandler - { - /// - /// Gets or sets a function that evaluates request data in an object, - /// and returns a value indicating whether the data matches the specified criteria. - /// - [DisallowNull] - public Func? RequestMatcher { get; set; } + [DisallowNull] + public Func? RequestMatcher { get; set; } - /// - /// Gets or sets a function that accepts an object describing a network - /// request to be sent, and returns a modified object to use in the actual - /// network request. - /// - public Func? RequestTransformer { get; set; } + /// + /// Gets or sets a function that accepts an object describing a network + /// request to be sent, and returns a modified object to use in the actual + /// network request. + /// + public Func? RequestTransformer { get; set; } - /// - /// Gets or sets a function that accepts an object describing a network - /// request to be sent, and returns an object as the response for the - /// request, bypassing the actual network request. - /// - public Func? ResponseSupplier { get; set; } - } + /// + /// Gets or sets a function that accepts an object describing a network + /// request to be sent, and returns an object as the response for the + /// request, bypassing the actual network request. + /// + public Func? ResponseSupplier { get; set; } } diff --git a/dotnet/src/webdriver/NetworkRequestSentEventArgs.cs b/dotnet/src/webdriver/NetworkRequestSentEventArgs.cs index ef80285af0afd..4b4c9cff8fdd3 100644 --- a/dotnet/src/webdriver/NetworkRequestSentEventArgs.cs +++ b/dotnet/src/webdriver/NetworkRequestSentEventArgs.cs @@ -20,57 +20,56 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the NetworkRequestSent event of an object implementing the interface. +/// +public class NetworkRequestSentEventArgs : EventArgs { + private readonly Dictionary requestHeaders = new Dictionary(); + /// - /// Provides data for the NetworkRequestSent event of an object implementing the interface. + /// Initializes a new instance of the class. /// - public class NetworkRequestSentEventArgs : EventArgs + /// The that describes the network request. + public NetworkRequestSentEventArgs(HttpRequestData requestData) { - private readonly Dictionary requestHeaders = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The that describes the network request. - public NetworkRequestSentEventArgs(HttpRequestData requestData) + this.RequestId = requestData.RequestId; + this.RequestUrl = requestData.Url; + this.RequestMethod = requestData.Method; + this.RequestPostData = requestData.PostData; + if (requestData.Headers != null) { - this.RequestId = requestData.RequestId; - this.RequestUrl = requestData.Url; - this.RequestMethod = requestData.Method; - this.RequestPostData = requestData.PostData; - if (requestData.Headers != null) + foreach (KeyValuePair header in requestData.Headers) { - foreach (KeyValuePair header in requestData.Headers) - { - this.requestHeaders[header.Key] = header.Value; - } + this.requestHeaders[header.Key] = header.Value; } } + } - /// - /// Gets the internal request ID of the network request. - /// - public string? RequestId { get; } + /// + /// Gets the internal request ID of the network request. + /// + public string? RequestId { get; } - /// - /// Gets the URL of the network request. - /// - public string? RequestUrl { get; } + /// + /// Gets the URL of the network request. + /// + public string? RequestUrl { get; } - /// - /// Gets the HTTP method of the network request. - /// - public string? RequestMethod { get; } + /// + /// Gets the HTTP method of the network request. + /// + public string? RequestMethod { get; } - /// - /// Gets the post data of the network request, if any. - /// - public string? RequestPostData { get; } + /// + /// Gets the post data of the network request, if any. + /// + public string? RequestPostData { get; } - /// - /// Gets the collection of headers associated with the network request. - /// - public IReadOnlyDictionary RequestHeaders => requestHeaders; - } + /// + /// Gets the collection of headers associated with the network request. + /// + public IReadOnlyDictionary RequestHeaders => requestHeaders; } diff --git a/dotnet/src/webdriver/NetworkResponseHandler.cs b/dotnet/src/webdriver/NetworkResponseHandler.cs index 72cedde4641ce..898e0a0649d64 100644 --- a/dotnet/src/webdriver/NetworkResponseHandler.cs +++ b/dotnet/src/webdriver/NetworkResponseHandler.cs @@ -20,26 +20,25 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Allows a user to handle a returned network, potentially modifying it before processing by the browser. +/// +public class NetworkResponseHandler { /// - /// Allows a user to handle a returned network, potentially modifying it before processing by the browser. + /// Gets or sets a function that evaluates returned response data in an object, + /// and returns a value indicating whether the data matches the specified criteria. /// - public class NetworkResponseHandler - { - /// - /// Gets or sets a function that evaluates returned response data in an object, - /// and returns a value indicating whether the data matches the specified criteria. - /// - [DisallowNull] - public Func? ResponseMatcher { get; set; } + [DisallowNull] + public Func? ResponseMatcher { get; set; } - /// - /// Gets or sets a function that accepts an object describing a network - /// response received by the browser, and returns a modified object to used - /// as the actual network response. - /// - [DisallowNull] - public Func? ResponseTransformer { get; set; } - } + /// + /// Gets or sets a function that accepts an object describing a network + /// response received by the browser, and returns a modified object to used + /// as the actual network response. + /// + [DisallowNull] + public Func? ResponseTransformer { get; set; } } diff --git a/dotnet/src/webdriver/NetworkResponseReceivedEventArgs.cs b/dotnet/src/webdriver/NetworkResponseReceivedEventArgs.cs index 671ad50383c09..8b5d618420a43 100644 --- a/dotnet/src/webdriver/NetworkResponseReceivedEventArgs.cs +++ b/dotnet/src/webdriver/NetworkResponseReceivedEventArgs.cs @@ -20,68 +20,67 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides data for the NetworkResponseReceived event of an object implementing the interface. +/// +public class NetworkResponseReceivedEventArgs : EventArgs { + private readonly Dictionary responseHeaders = new Dictionary(); + /// - /// Provides data for the NetworkResponseReceived event of an object implementing the interface. + /// Initializes a new instance of the class. /// - public class NetworkResponseReceivedEventArgs : EventArgs + /// The that describes the network response. + public NetworkResponseReceivedEventArgs(HttpResponseData responseData) { - private readonly Dictionary responseHeaders = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The that describes the network response. - public NetworkResponseReceivedEventArgs(HttpResponseData responseData) + this.RequestId = responseData.RequestId; + this.ResponseUrl = responseData.Url; + this.ResponseStatusCode = responseData.StatusCode; + this.ResponseContent = responseData.Content; + this.ResponseResourceType = responseData.ResourceType; + foreach (KeyValuePair header in responseData.Headers) { - this.RequestId = responseData.RequestId; - this.ResponseUrl = responseData.Url; - this.ResponseStatusCode = responseData.StatusCode; - this.ResponseContent = responseData.Content; - this.ResponseResourceType = responseData.ResourceType; - foreach (KeyValuePair header in responseData.Headers) - { - this.responseHeaders[header.Key] = header.Value; - } + this.responseHeaders[header.Key] = header.Value; } + } - /// - /// Gets the request ID of the network request that generated this response. - /// - public string? RequestId { get; } + /// + /// Gets the request ID of the network request that generated this response. + /// + public string? RequestId { get; } - /// - /// Gets the URL of the network response. - /// - public string? ResponseUrl { get; } + /// + /// Gets the URL of the network response. + /// + public string? ResponseUrl { get; } - /// - /// Gets the HTTP status code of the network response. - /// - public long ResponseStatusCode { get; } + /// + /// Gets the HTTP status code of the network response. + /// + public long ResponseStatusCode { get; } - /// - /// Gets the body of the network response. - /// - /// - /// This property is an alias for .ReadAsString() to keep backward compatibility. - /// - public string? ResponseBody => this.ResponseContent?.ReadAsString(); + /// + /// Gets the body of the network response. + /// + /// + /// This property is an alias for .ReadAsString() to keep backward compatibility. + /// + public string? ResponseBody => this.ResponseContent?.ReadAsString(); - /// - /// Gets the content of the network response, if any. - /// - public HttpResponseContent? ResponseContent { get; } + /// + /// Gets the content of the network response, if any. + /// + public HttpResponseContent? ResponseContent { get; } - /// - /// Gets the type of resource of the network response. - /// - public string? ResponseResourceType { get; } + /// + /// Gets the type of resource of the network response. + /// + public string? ResponseResourceType { get; } - /// - /// Gets the headers associated with this network response. - /// - public IReadOnlyDictionary ResponseHeaders => this.responseHeaders; - } + /// + /// Gets the headers associated with this network response. + /// + public IReadOnlyDictionary ResponseHeaders => this.responseHeaders; } diff --git a/dotnet/src/webdriver/NoAlertPresentException.cs b/dotnet/src/webdriver/NoAlertPresentException.cs index 32201da680e49..4ec263f999bd5 100644 --- a/dotnet/src/webdriver/NoAlertPresentException.cs +++ b/dotnet/src/webdriver/NoAlertPresentException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an alert is not found. +/// +[Serializable] +public class NoAlertPresentException : NotFoundException { /// - /// The exception that is thrown when an alert is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NoAlertPresentException : NotFoundException + public NoAlertPresentException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NoAlertPresentException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoAlertPresentException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoAlertPresentException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoAlertPresentException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoAlertPresentException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/NoSuchCookieException.cs b/dotnet/src/webdriver/NoSuchCookieException.cs index 441e7e35ab888..c23ea28a1cd6b 100644 --- a/dotnet/src/webdriver/NoSuchCookieException.cs +++ b/dotnet/src/webdriver/NoSuchCookieException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a cookie is not found. +/// +[Serializable] +public class NoSuchCookieException : NotFoundException { /// - /// The exception that is thrown when a cookie is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NoSuchCookieException : NotFoundException + public NoSuchCookieException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NoSuchCookieException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchCookieException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchCookieException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchCookieException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchCookieException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/NoSuchDriverException.cs b/dotnet/src/webdriver/NoSuchDriverException.cs index 33401719d7cee..da76e422e9d08 100644 --- a/dotnet/src/webdriver/NoSuchDriverException.cs +++ b/dotnet/src/webdriver/NoSuchDriverException.cs @@ -19,59 +19,58 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a driver is not found. +/// +[Serializable] +public class NoSuchDriverException : NotFoundException { + /// - /// The exception that is thrown when a driver is not found. + /// Link to the documentation for this error /// - [Serializable] - public class NoSuchDriverException : NotFoundException - { + private static string supportUrl = baseSupportUrl + "/driver_location"; - /// - /// Link to the documentation for this error - /// - private static string supportUrl = baseSupportUrl + "/driver_location"; - - /// - /// Initializes a new instance of the class. - /// - public NoSuchDriverException() - : base(GetMessage("")) - { - } + /// + /// Initializes a new instance of the class. + /// + public NoSuchDriverException() + : base(GetMessage("")) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchDriverException(string? message) - : base(GetMessage(message)) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchDriverException(string? message) + : base(GetMessage(message)) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchDriverException(string? message, Exception? innerException) - : base(GetMessage(message), innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchDriverException(string? message, Exception? innerException) + : base(GetMessage(message), innerException) + { + } - /// - /// Add information about obtaining additional support from documentation to this exception. - /// - /// The original message for exception - /// The final message for exception - protected static string GetMessage(string? message) - { - return $"{message}; {supportMsg}{supportUrl}"; - } + /// + /// Add information about obtaining additional support from documentation to this exception. + /// + /// The original message for exception + /// The final message for exception + protected static string GetMessage(string? message) + { + return $"{message}; {supportMsg}{supportUrl}"; } } diff --git a/dotnet/src/webdriver/NoSuchElementException.cs b/dotnet/src/webdriver/NoSuchElementException.cs index 1520be14c8808..b90a62e7cea53 100644 --- a/dotnet/src/webdriver/NoSuchElementException.cs +++ b/dotnet/src/webdriver/NoSuchElementException.cs @@ -19,59 +19,58 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an element is not found. +/// +[Serializable] +public class NoSuchElementException : NotFoundException { + /// - /// The exception that is thrown when an element is not found. + /// Link to the documentation for this error /// - [Serializable] - public class NoSuchElementException : NotFoundException - { + private static string supportUrl = baseSupportUrl + "#no-such-element-exception"; - /// - /// Link to the documentation for this error - /// - private static string supportUrl = baseSupportUrl + "#no-such-element-exception"; - - /// - /// Initializes a new instance of the class. - /// - public NoSuchElementException() - : base(GetMessage("")) - { - } + /// + /// Initializes a new instance of the class. + /// + public NoSuchElementException() + : base(GetMessage("")) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchElementException(string? message) - : base(GetMessage(message)) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchElementException(string? message) + : base(GetMessage(message)) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchElementException(string? message, Exception? innerException) - : base(GetMessage(message), innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchElementException(string? message, Exception? innerException) + : base(GetMessage(message), innerException) + { + } - /// - /// Add information about obtaining additional support from documentation to this exception. - /// - /// The original message for exception - /// The final message for exception - protected static string GetMessage(string? message) - { - return $"{message}; {supportMsg}{supportUrl}"; - } + /// + /// Add information about obtaining additional support from documentation to this exception. + /// + /// The original message for exception + /// The final message for exception + protected static string GetMessage(string? message) + { + return $"{message}; {supportMsg}{supportUrl}"; } } diff --git a/dotnet/src/webdriver/NoSuchFrameException.cs b/dotnet/src/webdriver/NoSuchFrameException.cs index 0b3f948034e2d..68f7972d6a3bf 100644 --- a/dotnet/src/webdriver/NoSuchFrameException.cs +++ b/dotnet/src/webdriver/NoSuchFrameException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a frame is not found. +/// +[Serializable] +public class NoSuchFrameException : NotFoundException { /// - /// The exception that is thrown when a frame is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NoSuchFrameException : NotFoundException + public NoSuchFrameException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NoSuchFrameException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchFrameException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchFrameException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchFrameException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchFrameException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/NoSuchShadowRootException.cs b/dotnet/src/webdriver/NoSuchShadowRootException.cs index a192b9f6e9ccf..e2128f744d758 100644 --- a/dotnet/src/webdriver/NoSuchShadowRootException.cs +++ b/dotnet/src/webdriver/NoSuchShadowRootException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a shadow root is not found. +/// +[Serializable] +public class NoSuchShadowRootException : NotFoundException { /// - /// The exception that is thrown when a shadow root is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NoSuchShadowRootException : NotFoundException + public NoSuchShadowRootException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NoSuchShadowRootException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchShadowRootException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchShadowRootException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchShadowRootException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchShadowRootException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/NoSuchWindowException.cs b/dotnet/src/webdriver/NoSuchWindowException.cs index 2a26923083c2a..0a6789c37a38f 100644 --- a/dotnet/src/webdriver/NoSuchWindowException.cs +++ b/dotnet/src/webdriver/NoSuchWindowException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a window is not found. +/// +[Serializable] +public class NoSuchWindowException : NotFoundException { /// - /// The exception that is thrown when a window is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NoSuchWindowException : NotFoundException + public NoSuchWindowException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NoSuchWindowException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NoSuchWindowException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NoSuchWindowException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NoSuchWindowException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NoSuchWindowException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/NotFoundException.cs b/dotnet/src/webdriver/NotFoundException.cs index 00d40c691ef42..c61154c621f25 100644 --- a/dotnet/src/webdriver/NotFoundException.cs +++ b/dotnet/src/webdriver/NotFoundException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an item is not found. +/// +[Serializable] +public class NotFoundException : WebDriverException { /// - /// The exception that is thrown when an item is not found. + /// Initializes a new instance of the class. /// - [Serializable] - public class NotFoundException : WebDriverException + public NotFoundException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public NotFoundException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public NotFoundException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public NotFoundException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public NotFoundException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public NotFoundException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/OptionsManager.cs b/dotnet/src/webdriver/OptionsManager.cs index 4a903ea2a3fb4..aff48c2da1f81 100644 --- a/dotnet/src/webdriver/OptionsManager.cs +++ b/dotnet/src/webdriver/OptionsManager.cs @@ -17,50 +17,49 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for setting options needed for the driver during the test. +/// +internal sealed class OptionsManager : IOptions { + private readonly WebDriver driver; + /// - /// Provides a mechanism for setting options needed for the driver during the test. + /// Initializes a new instance of the class /// - internal sealed class OptionsManager : IOptions + /// Instance of the driver currently in use + public OptionsManager(WebDriver driver) { - private readonly WebDriver driver; - - /// - /// Initializes a new instance of the class - /// - /// Instance of the driver currently in use - public OptionsManager(WebDriver driver) - { - this.driver = driver ?? throw new System.ArgumentNullException(nameof(driver)); - } - - /// - /// Gets an object allowing the user to manipulate cookies on the page. - /// - public ICookieJar Cookies => new CookieJar(this.driver); + this.driver = driver ?? throw new System.ArgumentNullException(nameof(driver)); + } - /// - /// Gets an object allowing the user to manipulate the currently-focused browser window. - /// - /// "Currently-focused" is defined as the browser window having the window handle - /// returned when IWebDriver.CurrentWindowHandle is called. - public IWindow Window => new Window(this.driver); + /// + /// Gets an object allowing the user to manipulate cookies on the page. + /// + public ICookieJar Cookies => new CookieJar(this.driver); - /// - /// Gets an object allowing the user to examine the logs of the current driver instance. - /// - public ILogs Logs => new Logs(this.driver); + /// + /// Gets an object allowing the user to manipulate the currently-focused browser window. + /// + /// "Currently-focused" is defined as the browser window having the window handle + /// returned when IWebDriver.CurrentWindowHandle is called. + public IWindow Window => new Window(this.driver); - /// - /// Provides access to the timeouts defined for this driver. - /// - /// An object implementing the interface. - public ITimeouts Timeouts() - { - return new Timeouts(this.driver); - } + /// + /// Gets an object allowing the user to examine the logs of the current driver instance. + /// + public ILogs Logs => new Logs(this.driver); - public INetwork Network => this.driver.Network; + /// + /// Provides access to the timeouts defined for this driver. + /// + /// An object implementing the interface. + public ITimeouts Timeouts() + { + return new Timeouts(this.driver); } + + public INetwork Network => this.driver.Network; } diff --git a/dotnet/src/webdriver/PasswordCredentials.cs b/dotnet/src/webdriver/PasswordCredentials.cs index bda5638ed98d7..ddbfc454c1c62 100644 --- a/dotnet/src/webdriver/PasswordCredentials.cs +++ b/dotnet/src/webdriver/PasswordCredentials.cs @@ -17,40 +17,39 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// A credentials provider that uses a user name and password for authentication. +/// +public class PasswordCredentials : ICredentials { /// - /// A credentials provider that uses a user name and password for authentication. + /// Initializes a new instance of the class. /// - public class PasswordCredentials : ICredentials + public PasswordCredentials() + : this(null, null) { - /// - /// Initializes a new instance of the class. - /// - public PasswordCredentials() - : this(null, null) - { - } + } - /// - /// Initializes a new instance of the class with the specified user name and password. - /// - /// The user name for the credentials. - /// The password for the credentials. - public PasswordCredentials(string? userName, string? password) - { - UserName = userName; - Password = password; - } + /// + /// Initializes a new instance of the class with the specified user name and password. + /// + /// The user name for the credentials. + /// The password for the credentials. + public PasswordCredentials(string? userName, string? password) + { + UserName = userName; + Password = password; + } - /// - /// Gets the user name. - /// - public string? UserName { get; } + /// + /// Gets the user name. + /// + public string? UserName { get; } - /// - /// Gets the password. - /// - public string? Password { get; } - } + /// + /// Gets the password. + /// + public string? Password { get; } } diff --git a/dotnet/src/webdriver/PinnedScript.cs b/dotnet/src/webdriver/PinnedScript.cs index 4e952c3dd6039..64c65f7281cf1 100644 --- a/dotnet/src/webdriver/PinnedScript.cs +++ b/dotnet/src/webdriver/PinnedScript.cs @@ -19,66 +19,65 @@ using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// A class representing a pinned JavaScript function that can be repeatedly called +/// without sending the entire script across the wire for every execution. +/// +public sealed class PinnedScript { /// - /// A class representing a pinned JavaScript function that can be repeatedly called - /// without sending the entire script across the wire for every execution. + /// Initializes a new instance of the class. /// - public sealed class PinnedScript + /// The body of the JavaScript function to pin. + /// The unique handle for this pinned script. + /// The internal ID of this script. + /// + /// This constructor is explicitly internal. Creation of pinned script objects + /// is strictly the perview of Selenium, and should not be required by external + /// libraries. + /// + internal PinnedScript(string script, string stringHandle, string scriptId) { - /// - /// Initializes a new instance of the class. - /// - /// The body of the JavaScript function to pin. - /// The unique handle for this pinned script. - /// The internal ID of this script. - /// - /// This constructor is explicitly internal. Creation of pinned script objects - /// is strictly the perview of Selenium, and should not be required by external - /// libraries. - /// - internal PinnedScript(string script, string stringHandle, string scriptId) - { - this.Source = script; - this.Handle = stringHandle; - this.ScriptId = scriptId; - } - - /// - /// Gets the unique handle for this pinned script. - /// - public string Handle { get; } + this.Source = script; + this.Handle = stringHandle; + this.ScriptId = scriptId; + } - /// - /// Gets the source representing the body of the function in the pinned script. - /// - public string Source { get; } + /// + /// Gets the unique handle for this pinned script. + /// + public string Handle { get; } - internal static string MakeCreationScript(string scriptHandle, string scriptSource) - { - return string.Format(CultureInfo.InvariantCulture, "function __webdriver_{0}(arguments) {{ {1} }}", scriptHandle, scriptSource); - } + /// + /// Gets the source representing the body of the function in the pinned script. + /// + public string Source { get; } - /// - /// Gets the script used to execute the pinned script in the browser. - /// - internal string MakeExecutionScript() - { - return string.Format(CultureInfo.InvariantCulture, "return __webdriver_{0}(arguments)", this.Handle); - } + internal static string MakeCreationScript(string scriptHandle, string scriptSource) + { + return string.Format(CultureInfo.InvariantCulture, "function __webdriver_{0}(arguments) {{ {1} }}", scriptHandle, scriptSource); + } - /// - /// Gets the script used to remove the pinned script from the browser. - /// - internal string MakeRemovalScript() - { - return string.Format(CultureInfo.InvariantCulture, "__webdriver_{0} = undefined", this.Handle); - } + /// + /// Gets the script used to execute the pinned script in the browser. + /// + internal string MakeExecutionScript() + { + return string.Format(CultureInfo.InvariantCulture, "return __webdriver_{0}(arguments)", this.Handle); + } - /// - /// Gets or sets the ID of this script. - /// - internal string ScriptId { get; } + /// + /// Gets the script used to remove the pinned script from the browser. + /// + internal string MakeRemovalScript() + { + return string.Format(CultureInfo.InvariantCulture, "__webdriver_{0} = undefined", this.Handle); } + + /// + /// Gets or sets the ID of this script. + /// + internal string ScriptId { get; } } diff --git a/dotnet/src/webdriver/Platform.cs b/dotnet/src/webdriver/Platform.cs index 30fe2852190d0..52f3de0f2a9ec 100644 --- a/dotnet/src/webdriver/Platform.cs +++ b/dotnet/src/webdriver/Platform.cs @@ -19,184 +19,183 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the known and supported Platforms that WebDriver runs on. +/// +/// The class maps closely to the Operating System, +/// but differs slightly, because this class is used to extract information such as +/// program locations and line endings. +public enum PlatformType { /// - /// Represents the known and supported Platforms that WebDriver runs on. + /// Any platform. This value is never returned by a driver, but can be used to find + /// drivers with certain capabilities. /// - /// The class maps closely to the Operating System, - /// but differs slightly, because this class is used to extract information such as - /// program locations and line endings. - public enum PlatformType - { - /// - /// Any platform. This value is never returned by a driver, but can be used to find - /// drivers with certain capabilities. - /// - Any, - - /// - /// Any version of Microsoft Windows. This value is never returned by a driver, - /// but can be used to find drivers with certain capabilities. - /// - Windows, - - /// - /// Any Windows NT-based version of Microsoft Windows. This value is never returned - /// by a driver, but can be used to find drivers with certain capabilities. This value - /// is equivalent to PlatformType.Windows. - /// - WinNT = Windows, - - /// - /// Versions of Microsoft Windows that are compatible with Windows XP. - /// - XP, - - /// - /// Versions of Microsoft Windows that are compatible with Windows Vista. - /// - Vista, - - /// - /// Any version of the Macintosh OS - /// - Mac, - - /// - /// Any version of the Unix operating system. - /// - Unix, - - /// - /// Any version of the Linux operating system. - /// - Linux, - - /// - /// A version of the Android mobile operating system. - /// - Android - } + Any, + + /// + /// Any version of Microsoft Windows. This value is never returned by a driver, + /// but can be used to find drivers with certain capabilities. + /// + Windows, + + /// + /// Any Windows NT-based version of Microsoft Windows. This value is never returned + /// by a driver, but can be used to find drivers with certain capabilities. This value + /// is equivalent to PlatformType.Windows. + /// + WinNT = Windows, + + /// + /// Versions of Microsoft Windows that are compatible with Windows XP. + /// + XP, + + /// + /// Versions of Microsoft Windows that are compatible with Windows Vista. + /// + Vista, + + /// + /// Any version of the Macintosh OS + /// + Mac, /// - /// Represents the platform on which tests are to be run. + /// Any version of the Unix operating system. /// - public class Platform + Unix, + + /// + /// Any version of the Linux operating system. + /// + Linux, + + /// + /// A version of the Android mobile operating system. + /// + Android +} + +/// +/// Represents the platform on which tests are to be run. +/// +public class Platform +{ + private static Platform? current; + + /// + /// Initializes a new instance of the class for a specific platform type. + /// + /// The platform type. + public Platform(PlatformType typeValue) { - private static Platform? current; + this.PlatformType = typeValue; + } - /// - /// Initializes a new instance of the class for a specific platform type. - /// - /// The platform type. - public Platform(PlatformType typeValue) - { - this.PlatformType = typeValue; - } + private Platform() + { + this.MajorVersion = Environment.OSVersion.Version.Major; + this.MinorVersion = Environment.OSVersion.Version.Minor; - private Platform() + switch (Environment.OSVersion.Platform) { - this.MajorVersion = Environment.OSVersion.Version.Major; - this.MinorVersion = Environment.OSVersion.Version.Minor; - - switch (Environment.OSVersion.Platform) - { - case PlatformID.Win32NT: - if (this.MajorVersion == 5) - { - this.PlatformType = PlatformType.XP; - } - else if (this.MajorVersion == 6) - { - this.PlatformType = PlatformType.Vista; - } - else - { - this.PlatformType = PlatformType.Windows; - } - - break; - - // Thanks to a bug in Mono Mac and Linux will be treated the same https://bugzilla.novell.com/show_bug.cgi?id=515570 but adding this in case - case PlatformID.MacOSX: - this.PlatformType = PlatformType.Mac; - break; - - case PlatformID.Unix: - this.PlatformType = PlatformType.Unix; - break; - } + case PlatformID.Win32NT: + if (this.MajorVersion == 5) + { + this.PlatformType = PlatformType.XP; + } + else if (this.MajorVersion == 6) + { + this.PlatformType = PlatformType.Vista; + } + else + { + this.PlatformType = PlatformType.Windows; + } + + break; + + // Thanks to a bug in Mono Mac and Linux will be treated the same https://bugzilla.novell.com/show_bug.cgi?id=515570 but adding this in case + case PlatformID.MacOSX: + this.PlatformType = PlatformType.Mac; + break; + + case PlatformID.Unix: + this.PlatformType = PlatformType.Unix; + break; } + } - /// - /// Gets the current platform. - /// - public static Platform CurrentPlatform => current ??= new Platform(); - - /// - /// Gets the major version of the platform operating system. - /// - public int MajorVersion { get; } - - /// - /// Gets the major version of the platform operating system. - /// - public int MinorVersion { get; } - - /// - /// Gets the type of the platform. - /// - public PlatformType PlatformType { get; } - - /// - /// Gets the value of the platform type for transmission using the JSON Wire Protocol. - /// - public string ProtocolPlatformType => this.PlatformType.ToString("G").ToUpperInvariant(); - - /// - /// Compares the platform to the specified type. - /// - /// A value to compare to. - /// if the platforms match; otherwise . - public bool IsPlatformType(PlatformType compareTo) - { - return compareTo switch - { - PlatformType.Any => true, - PlatformType.Windows => this.PlatformType is PlatformType.Windows or PlatformType.XP or PlatformType.Vista, - PlatformType.Vista => this.PlatformType is PlatformType.Windows or PlatformType.Vista, - PlatformType.XP => this.PlatformType is PlatformType.Windows or PlatformType.XP, - PlatformType.Linux => this.PlatformType is PlatformType.Linux or PlatformType.Unix, - _ => this.PlatformType == compareTo, - }; - } + /// + /// Gets the current platform. + /// + public static Platform CurrentPlatform => current ??= new Platform(); - /// - /// Returns the string value for this platform type. - /// - /// The string value for this platform type. - public override string ToString() - { - return this.PlatformType.ToString(); - } + /// + /// Gets the major version of the platform operating system. + /// + public int MajorVersion { get; } + + /// + /// Gets the major version of the platform operating system. + /// + public int MinorVersion { get; } + + /// + /// Gets the type of the platform. + /// + public PlatformType PlatformType { get; } + + /// + /// Gets the value of the platform type for transmission using the JSON Wire Protocol. + /// + public string ProtocolPlatformType => this.PlatformType.ToString("G").ToUpperInvariant(); - /// - /// Creates a object from a string name of the platform. - /// - /// The name of the platform to create. - /// The Platform object represented by the string name. - internal static Platform FromString(string platformName) + /// + /// Compares the platform to the specified type. + /// + /// A value to compare to. + /// if the platforms match; otherwise . + public bool IsPlatformType(PlatformType compareTo) + { + return compareTo switch { - if (Enum.TryParse(platformName, ignoreCase: true, out PlatformType platformTypeFromString)) - { - return new Platform(platformTypeFromString); - } + PlatformType.Any => true, + PlatformType.Windows => this.PlatformType is PlatformType.Windows or PlatformType.XP or PlatformType.Vista, + PlatformType.Vista => this.PlatformType is PlatformType.Windows or PlatformType.Vista, + PlatformType.XP => this.PlatformType is PlatformType.Windows or PlatformType.XP, + PlatformType.Linux => this.PlatformType is PlatformType.Linux or PlatformType.Unix, + _ => this.PlatformType == compareTo, + }; + } - // If the requested platform string is not a valid platform type, - // ignore it and use PlatformType.Any. + /// + /// Returns the string value for this platform type. + /// + /// The string value for this platform type. + public override string ToString() + { + return this.PlatformType.ToString(); + } - return new Platform(PlatformType.Any); + /// + /// Creates a object from a string name of the platform. + /// + /// The name of the platform to create. + /// The Platform object represented by the string name. + internal static Platform FromString(string platformName) + { + if (Enum.TryParse(platformName, ignoreCase: true, out PlatformType platformTypeFromString)) + { + return new Platform(platformTypeFromString); } + + // If the requested platform string is not a valid platform type, + // ignore it and use PlatformType.Any. + + return new Platform(PlatformType.Any); } } diff --git a/dotnet/src/webdriver/PrintDocument.cs b/dotnet/src/webdriver/PrintDocument.cs index 7303602bd0324..86f4484a05c83 100644 --- a/dotnet/src/webdriver/PrintDocument.cs +++ b/dotnet/src/webdriver/PrintDocument.cs @@ -20,53 +20,52 @@ using System; using System.IO; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents a printed document in the form of a PDF document. +/// +public class PrintDocument : EncodedFile { /// - /// Represents a printed document in the form of a PDF document. + /// Initializes a new instance of the class. /// - public class PrintDocument : EncodedFile + /// The printed document as a Base64-encoded string. + /// If is . + /// + /// The length of , ignoring white-space characters, is not zero or a multiple of 4. + /// -or- + /// The format of is invalid. contains a non-base-64 character, + /// more than two padding characters, or a non-white space-character among the padding characters. + /// + public PrintDocument(string base64EncodedDocument) : base(base64EncodedDocument) { - /// - /// Initializes a new instance of the class. - /// - /// The printed document as a Base64-encoded string. - /// If is . - /// - /// The length of , ignoring white-space characters, is not zero or a multiple of 4. - /// -or- - /// The format of is invalid. contains a non-base-64 character, - /// more than two padding characters, or a non-white space-character among the padding characters. - /// - public PrintDocument(string base64EncodedDocument) : base(base64EncodedDocument) + } + + /// + /// Saves this as a PDF formatted file, overwriting the file if it already exists. + /// + /// The full path and file name to save the printed document to. + /// + /// If is or whitespace. + /// -or- + /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment. + /// + /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment. + /// The specified path is invalid, such as being on an unmapped drive. + /// The specified path, file name, or both exceed the system-defined maximum length. + public override void SaveAsFile(string fileName) + { + if (string.IsNullOrEmpty(fileName)) { + throw new ArgumentException("The file name to be saved cannot be null or the empty string", nameof(fileName)); } - /// - /// Saves this as a PDF formatted file, overwriting the file if it already exists. - /// - /// The full path and file name to save the printed document to. - /// - /// If is or whitespace. - /// -or- - /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in an NTFS environment. - /// - /// refers to a non-file device, such as "con:", "com1:", "lpt1:", etc. in a non-NTFS environment. - /// The specified path is invalid, such as being on an unmapped drive. - /// The specified path, file name, or both exceed the system-defined maximum length. - public override void SaveAsFile(string fileName) + using (MemoryStream imageStream = new MemoryStream(this.AsByteArray)) { - if (string.IsNullOrEmpty(fileName)) - { - throw new ArgumentException("The file name to be saved cannot be null or the empty string", nameof(fileName)); - } - - using (MemoryStream imageStream = new MemoryStream(this.AsByteArray)) + using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) { - using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) - { - imageStream.WriteTo(fileStream); - } + imageStream.WriteTo(fileStream); } } } diff --git a/dotnet/src/webdriver/PrintOptions.cs b/dotnet/src/webdriver/PrintOptions.cs index 26ccc51ae3869..5a915adcd7fa8 100644 --- a/dotnet/src/webdriver/PrintOptions.cs +++ b/dotnet/src/webdriver/PrintOptions.cs @@ -21,403 +21,402 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the orientation of the page in the printed document. +/// +public enum PrintOrientation { /// - /// Represents the orientation of the page in the printed document. + /// Print the document in portrait mode. /// - public enum PrintOrientation - { - /// - /// Print the document in portrait mode. - /// - Portrait, - - /// - /// Print the document in landscape mode. - /// - Landscape - } + Portrait, /// - /// Represents the options to send for printing a page. + /// Print the document in landscape mode. /// - public class PrintOptions - { - private const double DefaultMarginSize = 1.0; - private const double DefaultPageHeight = 21.59; - private const double DefaultPageWidth = 27.94; - private const double CentimetersPerInch = 2.54; + Landscape +} - private double scale = 1.0; - private PageSize pageSize = new PageSize(); - private Margins margins = new Margins(); - private readonly HashSet pageRanges = new HashSet(); +/// +/// Represents the options to send for printing a page. +/// +public class PrintOptions +{ + private const double DefaultMarginSize = 1.0; + private const double DefaultPageHeight = 21.59; + private const double DefaultPageWidth = 27.94; + private const double CentimetersPerInch = 2.54; - /// - /// Gets or sets the orientation of the pages in the printed document. - /// - public PrintOrientation Orientation { get; set; } = PrintOrientation.Portrait; + private double scale = 1.0; + private PageSize pageSize = new PageSize(); + private Margins margins = new Margins(); + private readonly HashSet pageRanges = new HashSet(); - /// - /// Gets or sets the amount which the printed content is zoomed. Valid values are 0.1 to 2.0. - /// - /// If the value is not set between 0.1 and 2.0. - public double ScaleFactor + /// + /// Gets or sets the orientation of the pages in the printed document. + /// + public PrintOrientation Orientation { get; set; } = PrintOrientation.Portrait; + + /// + /// Gets or sets the amount which the printed content is zoomed. Valid values are 0.1 to 2.0. + /// + /// If the value is not set between 0.1 and 2.0. + public double ScaleFactor + { + get => this.scale; + set { - get => this.scale; - set + if (value < 0.1 || value > 2.0) { - if (value < 0.1 || value > 2.0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Scale factor must be between 0.1 and 2.0."); - } - - this.scale = value; + throw new ArgumentOutOfRangeException(nameof(value), "Scale factor must be between 0.1 and 2.0."); } + + this.scale = value; } + } - /// - /// Gets or sets a value indicating whether to print background images in the printed document. - /// - public bool OutputBackgroundImages { get; set; } + /// + /// Gets or sets a value indicating whether to print background images in the printed document. + /// + public bool OutputBackgroundImages { get; set; } - /// - /// Gets or sets a value indicating whether to shrink the content to fit the printed page size. - /// - public bool ShrinkToFit { get; set; } = true; + /// + /// Gets or sets a value indicating whether to shrink the content to fit the printed page size. + /// + public bool ShrinkToFit { get; set; } = true; - /// - /// Gets or sets the dimensions for each page in the printed document. - /// - /// If the value is set to . - public PageSize PageDimensions + /// + /// Gets or sets the dimensions for each page in the printed document. + /// + /// If the value is set to . + public PageSize PageDimensions + { + get => this.pageSize; + set => this.pageSize = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets the margins for each page in the doucment. + /// + /// If the value is set to . + public Margins PageMargins + { + get => this.margins; + set => this.margins = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Adds a page to the list of pages to be included in the document. + /// + /// The page number to be included in the document. + /// If is negative. + /// If the requested page has already been added. + public void AddPageToPrint(int pageNumber) + { + if (pageNumber < 0) { - get => this.pageSize; - set => this.pageSize = value ?? throw new ArgumentNullException(nameof(value)); + throw new ArgumentOutOfRangeException(nameof(pageNumber), "Page number must be greater than or equal to zero"); } - /// - /// Gets or sets the margins for each page in the doucment. - /// - /// If the value is set to . - public Margins PageMargins + if (this.pageRanges.Contains(pageNumber)) { - get => this.margins; - set => this.margins = value ?? throw new ArgumentNullException(nameof(value)); + throw new ArgumentException("Cannot add the same page number twice", nameof(pageNumber)); } - /// - /// Adds a page to the list of pages to be included in the document. - /// - /// The page number to be included in the document. - /// If is negative. - /// If the requested page has already been added. - public void AddPageToPrint(int pageNumber) - { - if (pageNumber < 0) - { - throw new ArgumentOutOfRangeException(nameof(pageNumber), "Page number must be greater than or equal to zero"); - } + this.pageRanges.Add(pageNumber); + } - if (this.pageRanges.Contains(pageNumber)) - { - throw new ArgumentException("Cannot add the same page number twice", nameof(pageNumber)); - } + /// + /// Adds a range of pages to be included in the document. + /// + /// A string of the form "x-y" representing the page numbers to include. + /// + /// If is or . + /// -or- + /// If the requested is already included. + /// -or- + /// If the requested has multiple '-' separators. + /// -or- + /// If a bound value is neither empty nor a number. + /// + /// + /// If has a negative lower bound. + /// -or- + /// If has an upper bound less than the lower bound. + /// + public void AddPageRangeToPrint(string pageRange) + { + if (string.IsNullOrEmpty(pageRange)) + { + throw new ArgumentException("Page range cannot be null or the empty string", nameof(pageRange)); + } - this.pageRanges.Add(pageNumber); + if (this.pageRanges.Contains(pageRange)) + { + throw new ArgumentException("Cannot add the same page range twice", nameof(pageRange)); } - /// - /// Adds a range of pages to be included in the document. - /// - /// A string of the form "x-y" representing the page numbers to include. - /// - /// If is or . - /// -or- - /// If the requested is already included. - /// -or- - /// If the requested has multiple '-' separators. - /// -or- - /// If a bound value is neither empty nor a number. - /// - /// - /// If has a negative lower bound. - /// -or- - /// If has an upper bound less than the lower bound. - /// - public void AddPageRangeToPrint(string pageRange) + string[] pageRangeParts = pageRange.Trim().Split('-'); + if (pageRangeParts.Length > 2) { - if (string.IsNullOrEmpty(pageRange)) - { - throw new ArgumentException("Page range cannot be null or the empty string", nameof(pageRange)); - } + throw new ArgumentException("Page range cannot have multiple separators", nameof(pageRange)); + } - if (this.pageRanges.Contains(pageRange)) - { - throw new ArgumentException("Cannot add the same page range twice", nameof(pageRange)); - } + int startPage = ParsePageRangePart(pageRangeParts[0], 1); + if (startPage < 1) + { + throw new ArgumentOutOfRangeException(nameof(pageRange), "Start of a page range must be greater than or equal to 1"); + } - string[] pageRangeParts = pageRange.Trim().Split('-'); - if (pageRangeParts.Length > 2) + if (pageRangeParts.Length == 2) + { + int endPage = ParsePageRangePart(pageRangeParts[1], int.MaxValue); + if (endPage < startPage) { - throw new ArgumentException("Page range cannot have multiple separators", nameof(pageRange)); + throw new ArgumentOutOfRangeException(nameof(pageRange), "End of a page range must be greater than or equal to the start of the page range"); } + } - int startPage = ParsePageRangePart(pageRangeParts[0], 1); - if (startPage < 1) - { - throw new ArgumentOutOfRangeException(nameof(pageRange), "Start of a page range must be greater than or equal to 1"); - } + this.pageRanges.Add(pageRange); + } - if (pageRangeParts.Length == 2) - { - int endPage = ParsePageRangePart(pageRangeParts[1], int.MaxValue); - if (endPage < startPage) - { - throw new ArgumentOutOfRangeException(nameof(pageRange), "End of a page range must be greater than or equal to the start of the page range"); - } - } + internal Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); - this.pageRanges.Add(pageRange); + if (this.Orientation != PrintOrientation.Portrait) + { + toReturn["orientation"] = this.Orientation.ToString().ToLowerInvariant(); } - internal Dictionary ToDictionary() + if (this.scale != 1.0) { - Dictionary toReturn = new Dictionary(); + toReturn["scale"] = this.scale; + } - if (this.Orientation != PrintOrientation.Portrait) - { - toReturn["orientation"] = this.Orientation.ToString().ToLowerInvariant(); - } + if (this.OutputBackgroundImages) + { + toReturn["background"] = this.OutputBackgroundImages; + } - if (this.scale != 1.0) - { - toReturn["scale"] = this.scale; - } + if (!this.ShrinkToFit) + { + toReturn["shrinkToFit"] = this.ShrinkToFit; + } - if (this.OutputBackgroundImages) - { - toReturn["background"] = this.OutputBackgroundImages; - } + if (this.pageSize.Height != DefaultPageHeight || this.pageSize.Width != DefaultPageWidth) + { + Dictionary pageSizeDictionary = new Dictionary(); + pageSizeDictionary["width"] = this.pageSize.Width; + pageSizeDictionary["height"] = this.pageSize.Height; + toReturn["page"] = pageSizeDictionary; + } - if (!this.ShrinkToFit) - { - toReturn["shrinkToFit"] = this.ShrinkToFit; - } + if (this.margins.Top != DefaultMarginSize || this.margins.Bottom != DefaultMarginSize || this.margins.Left != DefaultMarginSize || this.margins.Right != DefaultMarginSize) + { + Dictionary marginsDictionary = new Dictionary(); + marginsDictionary["top"] = this.margins.Top; + marginsDictionary["bottom"] = this.margins.Bottom; + marginsDictionary["left"] = this.margins.Left; + marginsDictionary["right"] = this.margins.Right; + toReturn["margin"] = marginsDictionary; + } - if (this.pageSize.Height != DefaultPageHeight || this.pageSize.Width != DefaultPageWidth) - { - Dictionary pageSizeDictionary = new Dictionary(); - pageSizeDictionary["width"] = this.pageSize.Width; - pageSizeDictionary["height"] = this.pageSize.Height; - toReturn["page"] = pageSizeDictionary; - } + if (this.pageRanges.Count > 0) + { + toReturn["pageRanges"] = new List(this.pageRanges); + } - if (this.margins.Top != DefaultMarginSize || this.margins.Bottom != DefaultMarginSize || this.margins.Left != DefaultMarginSize || this.margins.Right != DefaultMarginSize) - { - Dictionary marginsDictionary = new Dictionary(); - marginsDictionary["top"] = this.margins.Top; - marginsDictionary["bottom"] = this.margins.Bottom; - marginsDictionary["left"] = this.margins.Left; - marginsDictionary["right"] = this.margins.Right; - toReturn["margin"] = marginsDictionary; - } + return toReturn; + } - if (this.pageRanges.Count > 0) - { - toReturn["pageRanges"] = new List(this.pageRanges); - } + private static int ParsePageRangePart(string pageRangePart, int defaultValue) + { + pageRangePart = pageRangePart.Trim(); - return toReturn; + if (string.IsNullOrEmpty(pageRangePart)) + { + return defaultValue; } - private static int ParsePageRangePart(string pageRangePart, int defaultValue) + if (int.TryParse(pageRangePart, NumberStyles.Integer, CultureInfo.InvariantCulture, out int pageRangePartValue)) { - pageRangePart = pageRangePart.Trim(); + return pageRangePartValue; + } - if (string.IsNullOrEmpty(pageRangePart)) - { - return defaultValue; - } + throw new ArgumentException("Parts of a page range must be an empty string or an integer"); + } - if (int.TryParse(pageRangePart, NumberStyles.Integer, CultureInfo.InvariantCulture, out int pageRangePartValue)) - { - return pageRangePartValue; - } + /// + /// An object representing the page size of the print options. + /// + public class PageSize + { + private double height = DefaultPageHeight; + private double width = DefaultPageWidth; - throw new ArgumentException("Parts of a page range must be an empty string or an integer"); - } + /// + /// Represents the A4 paper size. + /// Width: 21.0 cm, Height: 29.7 cm + /// + public static PageSize A4 => new PageSize { Width = 21.0, Height = 29.7 }; // cm + + /// + /// Represents the Legal paper size. + /// Width: 21.59 cm, Height: 35.56 cm + /// + public static PageSize Legal => new PageSize { Width = 21.59, Height = 35.56 }; // cm /// - /// An object representing the page size of the print options. + /// Represents the Letter paper size. + /// Width: 21.59 cm, Height: 27.94 cm /// - public class PageSize + public static PageSize Letter => new PageSize { Width = 21.59, Height = 27.94 }; // cm + + /// + /// Represents the Tabloid paper size. + /// Width: 27.94 cm, Height: 43.18 cm + /// + public static PageSize Tabloid => new PageSize { Width = 27.94, Height = 43.18 }; // cm + + /// + /// Gets or sets the height of each page in centimeters. + /// + /// If the value is set to a negative value. + public double Height { - private double height = DefaultPageHeight; - private double width = DefaultPageWidth; - - /// - /// Represents the A4 paper size. - /// Width: 21.0 cm, Height: 29.7 cm - /// - public static PageSize A4 => new PageSize { Width = 21.0, Height = 29.7 }; // cm - - /// - /// Represents the Legal paper size. - /// Width: 21.59 cm, Height: 35.56 cm - /// - public static PageSize Legal => new PageSize { Width = 21.59, Height = 35.56 }; // cm - - /// - /// Represents the Letter paper size. - /// Width: 21.59 cm, Height: 27.94 cm - /// - public static PageSize Letter => new PageSize { Width = 21.59, Height = 27.94 }; // cm - - /// - /// Represents the Tabloid paper size. - /// Width: 27.94 cm, Height: 43.18 cm - /// - public static PageSize Tabloid => new PageSize { Width = 27.94, Height = 43.18 }; // cm - - /// - /// Gets or sets the height of each page in centimeters. - /// - /// If the value is set to a negative value. - public double Height + get => this.height; + set { - get => this.height; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Height must be greater than or equal to zero."); - } - - this.height = value; + throw new ArgumentOutOfRangeException(nameof(value), "Height must be greater than or equal to zero."); } + + this.height = value; } + } - /// - /// Gets or sets the width of each page in centimeters. - /// - /// If the value is set to a negative value. - public double Width + /// + /// Gets or sets the width of each page in centimeters. + /// + /// If the value is set to a negative value. + public double Width + { + get => this.width; + set { - get => this.width; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Width must be greater than or equal to zero."); - } - - this.width = value; + throw new ArgumentOutOfRangeException(nameof(value), "Width must be greater than or equal to zero."); } - } - /// - /// Gets or sets the height of each page in inches. - /// - /// If the value is set to a negative value. - public double HeightInInches - { - get => this.Height / CentimetersPerInch; - set => this.Height = value * CentimetersPerInch; + this.width = value; } + } - /// - /// Gets or sets the width of each page in inches. - /// - /// If the value is set to a negative value. - public double WidthInInches - { - get => this.Width / CentimetersPerInch; - set => this.Width = value * CentimetersPerInch; - } + /// + /// Gets or sets the height of each page in inches. + /// + /// If the value is set to a negative value. + public double HeightInInches + { + get => this.Height / CentimetersPerInch; + set => this.Height = value * CentimetersPerInch; } /// - /// An object representing the margins for printing. + /// Gets or sets the width of each page in inches. /// - public class Margins + /// If the value is set to a negative value. + public double WidthInInches { - private double top = DefaultMarginSize; - private double bottom = DefaultMarginSize; - private double left = DefaultMarginSize; - private double right = DefaultMarginSize; - - /// - /// Gets or sets the top margin of the print options. - /// - /// If the value is set to a negative value. - public double Top + get => this.Width / CentimetersPerInch; + set => this.Width = value * CentimetersPerInch; + } + } + + /// + /// An object representing the margins for printing. + /// + public class Margins + { + private double top = DefaultMarginSize; + private double bottom = DefaultMarginSize; + private double left = DefaultMarginSize; + private double right = DefaultMarginSize; + + /// + /// Gets or sets the top margin of the print options. + /// + /// If the value is set to a negative value. + public double Top + { + get => this.top; + set { - get => this.top; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Top margin must be greater than or equal to zero."); - } - - this.top = value; + throw new ArgumentOutOfRangeException(nameof(value), "Top margin must be greater than or equal to zero."); } + + this.top = value; } + } - /// - /// Gets or sets the bottom margin of the print options. - /// - /// If the value is set to a negative value. - public double Bottom + /// + /// Gets or sets the bottom margin of the print options. + /// + /// If the value is set to a negative value. + public double Bottom + { + get => this.bottom; + set { - get => this.bottom; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Bottom margin must be greater than or equal to zero."); - } - - this.bottom = value; + throw new ArgumentOutOfRangeException(nameof(value), "Bottom margin must be greater than or equal to zero."); } + + this.bottom = value; } + } - /// - /// Gets or sets the left margin of the print options. - /// - /// If the value is set to a negative value. - public double Left + /// + /// Gets or sets the left margin of the print options. + /// + /// If the value is set to a negative value. + public double Left + { + get => this.left; + set { - get => this.left; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Left margin must be greater than or equal to zero."); - } - - this.left = value; + throw new ArgumentOutOfRangeException(nameof(value), "Left margin must be greater than or equal to zero."); } + + this.left = value; } + } - /// - /// Gets or sets the right margin of the print options. - /// - /// If the value is set to a negative value. - public double Right + /// + /// Gets or sets the right margin of the print options. + /// + /// If the value is set to a negative value. + public double Right + { + get => this.right; + set { - get => this.right; - set + if (value < 0) { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Right margin must be greater than or equal to zero."); - } - - this.right = value; + throw new ArgumentOutOfRangeException(nameof(value), "Right margin must be greater than or equal to zero."); } + + this.right = value; } } } diff --git a/dotnet/src/webdriver/Proxy.cs b/dotnet/src/webdriver/Proxy.cs index ce5e94d84b497..71046907e9745 100644 --- a/dotnet/src/webdriver/Proxy.cs +++ b/dotnet/src/webdriver/Proxy.cs @@ -23,537 +23,536 @@ using System.Globalization; using System.Text.Json.Serialization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Describes the kind of proxy. +/// +/// +/// Keep these in sync with the Firefox preferences numbers: +/// http://kb.mozillazine.org/Network.proxy.type +/// +public enum ProxyKind { /// - /// Describes the kind of proxy. + /// Direct connection, no proxy (default on Windows). /// - /// - /// Keep these in sync with the Firefox preferences numbers: - /// http://kb.mozillazine.org/Network.proxy.type - /// - public enum ProxyKind + Direct = 0, + + /// + /// Manual proxy settings (e.g., for httpProxy). + /// + Manual, + + /// + /// Proxy automatic configuration from URL. + /// + ProxyAutoConfigure, + + /// + /// Use proxy automatic detection. + /// + AutoDetect = 4, + + /// + /// Use the system values for proxy settings (default on Linux). + /// + System, + + /// + /// No proxy type is specified. + /// + Unspecified +} + +/// +/// Describes proxy settings to be used with a driver instance. +/// +public class Proxy +{ + private ProxyKind proxyKind = ProxyKind.Unspecified; + private bool isAutoDetect; + private string? ftpProxyLocation; + private string? httpProxyLocation; + private string? proxyAutoConfigUrl; + private string? sslProxyLocation; + private string? socksProxyLocation; + private string? socksUserName; + private string? socksPassword; + private int? socksVersion; + private List noProxyAddresses = new List(); + + /// + /// Initializes a new instance of the class. + /// + public Proxy() { - /// - /// Direct connection, no proxy (default on Windows). - /// - Direct = 0, - - /// - /// Manual proxy settings (e.g., for httpProxy). - /// - Manual, - - /// - /// Proxy automatic configuration from URL. - /// - ProxyAutoConfigure, - - /// - /// Use proxy automatic detection. - /// - AutoDetect = 4, - - /// - /// Use the system values for proxy settings (default on Linux). - /// - System, - - /// - /// No proxy type is specified. - /// - Unspecified } /// - /// Describes proxy settings to be used with a driver instance. + /// Initializes a new instance of the class with the given proxy settings. /// - public class Proxy + /// A dictionary of settings to use with the proxy. + /// If is . + /// If The "noProxy" value is a list with a element. + public Proxy(Dictionary settings) { - private ProxyKind proxyKind = ProxyKind.Unspecified; - private bool isAutoDetect; - private string? ftpProxyLocation; - private string? httpProxyLocation; - private string? proxyAutoConfigUrl; - private string? sslProxyLocation; - private string? socksProxyLocation; - private string? socksUserName; - private string? socksPassword; - private int? socksVersion; - private List noProxyAddresses = new List(); - - /// - /// Initializes a new instance of the class. - /// - public Proxy() + if (settings == null) { + throw new ArgumentNullException(nameof(settings), "settings dictionary cannot be null"); } - /// - /// Initializes a new instance of the class with the given proxy settings. - /// - /// A dictionary of settings to use with the proxy. - /// If is . - /// If The "noProxy" value is a list with a element. - public Proxy(Dictionary settings) + if (settings.TryGetValue("proxyType", out object? proxyTypeObj) && proxyTypeObj?.ToString() is string proxyType) { - if (settings == null) + // Special-case "PAC" since that is the correct serialization. + if (proxyType.Equals("pac", StringComparison.InvariantCultureIgnoreCase)) { - throw new ArgumentNullException(nameof(settings), "settings dictionary cannot be null"); + this.Kind = ProxyKind.ProxyAutoConfigure; } - - if (settings.TryGetValue("proxyType", out object? proxyTypeObj) && proxyTypeObj?.ToString() is string proxyType) + else { - // Special-case "PAC" since that is the correct serialization. - if (proxyType.Equals("pac", StringComparison.InvariantCultureIgnoreCase)) - { - this.Kind = ProxyKind.ProxyAutoConfigure; - } - else - { - ProxyKind rawType = (ProxyKind)Enum.Parse(typeof(ProxyKind), proxyType, ignoreCase: true); - this.Kind = rawType; - } + ProxyKind rawType = (ProxyKind)Enum.Parse(typeof(ProxyKind), proxyType, ignoreCase: true); + this.Kind = rawType; } + } - if (settings.TryGetValue("ftpProxy", out object? ftpProxyObj) && ftpProxyObj?.ToString() is string ftpProxy) - { - this.FtpProxy = ftpProxy; - } + if (settings.TryGetValue("ftpProxy", out object? ftpProxyObj) && ftpProxyObj?.ToString() is string ftpProxy) + { + this.FtpProxy = ftpProxy; + } + + if (settings.TryGetValue("httpProxy", out object? httpProxyObj) && httpProxyObj?.ToString() is string httpProxy) + { + this.HttpProxy = httpProxy; + } - if (settings.TryGetValue("httpProxy", out object? httpProxyObj) && httpProxyObj?.ToString() is string httpProxy) + if (settings.TryGetValue("noProxy", out object? noProxy) && noProxy != null) + { + List bypassAddresses = new List(); + if (noProxy is string addressesAsString) { - this.HttpProxy = httpProxy; + bypassAddresses.AddRange(addressesAsString.Split(';')); } - - if (settings.TryGetValue("noProxy", out object? noProxy) && noProxy != null) + else { - List bypassAddresses = new List(); - if (noProxy is string addressesAsString) - { - bypassAddresses.AddRange(addressesAsString.Split(';')); - } - else + if (noProxy is object?[] addressesAsArray) { - if (noProxy is object?[] addressesAsArray) + foreach (object? address in addressesAsArray) { - foreach (object? address in addressesAsArray) - { - bypassAddresses.Add(address?.ToString() ?? throw new ArgumentException("Proxy bypass address list \"noProxy\" contained a null element", nameof(settings))); - } + bypassAddresses.Add(address?.ToString() ?? throw new ArgumentException("Proxy bypass address list \"noProxy\" contained a null element", nameof(settings))); } } - - this.AddBypassAddresses(bypassAddresses); - } - - if (settings.TryGetValue("proxyAutoconfigUrl", out object? proxyAutoconfigUrlObj) && proxyAutoconfigUrlObj?.ToString() is string proxyAutoconfigUrl) - { - this.ProxyAutoConfigUrl = proxyAutoconfigUrl; - } - - if (settings.TryGetValue("sslProxy", out object? sslProxyObj) && sslProxyObj?.ToString() is string sslProxy) - { - this.SslProxy = sslProxy; - } - - if (settings.TryGetValue("socksProxy", out object? socksProxyObj) && socksProxyObj?.ToString() is string socksProxy) - { - this.SocksProxy = socksProxy; - } - - if (settings.TryGetValue("socksUsername", out object? socksUsernameObj) && socksUsernameObj?.ToString() is string socksUsername) - { - this.SocksUserName = socksUsername; } - if (settings.TryGetValue("socksPassword", out object? socksPasswordObj) && socksPasswordObj?.ToString() is string socksPassword) - { - this.SocksPassword = socksPassword; - } - - if (settings.TryGetValue("socksVersion", out object? socksVersion) && socksVersion != null) - { - this.SocksVersion = Convert.ToInt32(socksVersion); - } - - if (settings.TryGetValue("autodetect", out object? autodetect) && autodetect != null) - { - this.IsAutoDetect = Convert.ToBoolean(autodetect); - } + this.AddBypassAddresses(bypassAddresses); } - /// - /// Gets or sets the type of proxy. - /// - [JsonIgnore] - public ProxyKind Kind + if (settings.TryGetValue("proxyAutoconfigUrl", out object? proxyAutoconfigUrlObj) && proxyAutoconfigUrlObj?.ToString() is string proxyAutoconfigUrl) { - get => this.proxyKind; - - set - { - this.VerifyProxyTypeCompatilibily(value); - this.proxyKind = value; - } + this.ProxyAutoConfigUrl = proxyAutoconfigUrl; } - /// - /// Gets the type of proxy as a string for JSON serialization. - /// - [JsonPropertyName("proxyType")] - public string SerializableProxyKind + if (settings.TryGetValue("sslProxy", out object? sslProxyObj) && sslProxyObj?.ToString() is string sslProxy) { - get - { - if (this.proxyKind == ProxyKind.ProxyAutoConfigure) - { - return "pac"; - } - - return this.proxyKind.ToString().ToLowerInvariant(); - } + this.SslProxy = sslProxy; } - /// - /// Gets or sets a value indicating whether the proxy uses automatic detection. - /// - [JsonIgnore] - public bool IsAutoDetect + if (settings.TryGetValue("socksProxy", out object? socksProxyObj) && socksProxyObj?.ToString() is string socksProxy) { - get => this.isAutoDetect; - - set - { - if (this.isAutoDetect == value) - { - return; - } - - this.VerifyProxyTypeCompatilibily(ProxyKind.AutoDetect); - this.proxyKind = ProxyKind.AutoDetect; - this.isAutoDetect = value; - } + this.SocksProxy = socksProxy; } - /// - /// Gets or sets the value of the proxy for the FTP protocol. - /// - [JsonPropertyName("ftpProxy")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? FtpProxy + if (settings.TryGetValue("socksUsername", out object? socksUsernameObj) && socksUsernameObj?.ToString() is string socksUsername) { - get => this.ftpProxyLocation; - - set - { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.ftpProxyLocation = value; - } + this.SocksUserName = socksUsername; } - /// - /// Gets or sets the value of the proxy for the HTTP protocol. - /// - [JsonPropertyName("httpProxy")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? HttpProxy + if (settings.TryGetValue("socksPassword", out object? socksPasswordObj) && socksPasswordObj?.ToString() is string socksPassword) { - get => this.httpProxyLocation; - - set - { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.httpProxyLocation = value; - } + this.SocksPassword = socksPassword; } - /// - /// Gets the list of address for which to bypass the proxy as an array. - /// - [JsonPropertyName("noProxy")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public ReadOnlyCollection? BypassProxyAddresses + if (settings.TryGetValue("socksVersion", out object? socksVersion) && socksVersion != null) { - get - { - if (this.noProxyAddresses.Count == 0) - { - return null; - } + this.SocksVersion = Convert.ToInt32(socksVersion); + } - return this.noProxyAddresses.AsReadOnly(); - } + if (settings.TryGetValue("autodetect", out object? autodetect) && autodetect != null) + { + this.IsAutoDetect = Convert.ToBoolean(autodetect); } + } + + /// + /// Gets or sets the type of proxy. + /// + [JsonIgnore] + public ProxyKind Kind + { + get => this.proxyKind; - /// - /// Gets or sets the URL used for proxy automatic configuration. - /// - [JsonPropertyName("proxyAutoconfigUrl")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? ProxyAutoConfigUrl + set { - get => this.proxyAutoConfigUrl; + this.VerifyProxyTypeCompatilibily(value); + this.proxyKind = value; + } + } - set + /// + /// Gets the type of proxy as a string for JSON serialization. + /// + [JsonPropertyName("proxyType")] + public string SerializableProxyKind + { + get + { + if (this.proxyKind == ProxyKind.ProxyAutoConfigure) { - this.VerifyProxyTypeCompatilibily(ProxyKind.ProxyAutoConfigure); - this.proxyKind = ProxyKind.ProxyAutoConfigure; - this.proxyAutoConfigUrl = value; + return "pac"; } + + return this.proxyKind.ToString().ToLowerInvariant(); } + } - /// - /// Gets or sets the value of the proxy for the SSL protocol. - /// - [JsonPropertyName("sslProxy")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? SslProxy - { - get => this.sslProxyLocation; + /// + /// Gets or sets a value indicating whether the proxy uses automatic detection. + /// + [JsonIgnore] + public bool IsAutoDetect + { + get => this.isAutoDetect; - set + set + { + if (this.isAutoDetect == value) { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.sslProxyLocation = value; + return; } + + this.VerifyProxyTypeCompatilibily(ProxyKind.AutoDetect); + this.proxyKind = ProxyKind.AutoDetect; + this.isAutoDetect = value; } + } - /// - /// Gets or sets the value of the proxy for the SOCKS protocol. - /// - [JsonPropertyName("socksProxy")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? SocksProxy - { - get => this.socksProxyLocation; + /// + /// Gets or sets the value of the proxy for the FTP protocol. + /// + [JsonPropertyName("ftpProxy")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? FtpProxy + { + get => this.ftpProxyLocation; - set - { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.socksProxyLocation = value; - } + set + { + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.ftpProxyLocation = value; } + } - /// - /// Gets or sets the value of username for the SOCKS proxy. - /// - [JsonPropertyName("socksUsername")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? SocksUserName + /// + /// Gets or sets the value of the proxy for the HTTP protocol. + /// + [JsonPropertyName("httpProxy")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? HttpProxy + { + get => this.httpProxyLocation; + + set { - get => this.socksUserName; + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.httpProxyLocation = value; + } + } - set + /// + /// Gets the list of address for which to bypass the proxy as an array. + /// + [JsonPropertyName("noProxy")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public ReadOnlyCollection? BypassProxyAddresses + { + get + { + if (this.noProxyAddresses.Count == 0) { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.socksUserName = value; + return null; } + + return this.noProxyAddresses.AsReadOnly(); } + } - /// - /// Gets or sets the value of the protocol version for the SOCKS proxy. - /// Value can be if not set. - /// - [JsonPropertyName("socksVersion")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int? SocksVersion + /// + /// Gets or sets the URL used for proxy automatic configuration. + /// + [JsonPropertyName("proxyAutoconfigUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? ProxyAutoConfigUrl + { + get => this.proxyAutoConfigUrl; + + set { - get => this.socksVersion; + this.VerifyProxyTypeCompatilibily(ProxyKind.ProxyAutoConfigure); + this.proxyKind = ProxyKind.ProxyAutoConfigure; + this.proxyAutoConfigUrl = value; + } + } - set - { - if (value == null) - { - this.socksVersion = value; - } - else - { - if (value.Value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "SocksVersion must be a positive integer"); - } + /// + /// Gets or sets the value of the proxy for the SSL protocol. + /// + [JsonPropertyName("sslProxy")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? SslProxy + { + get => this.sslProxyLocation; - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.socksVersion = value; - } - } + set + { + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.sslProxyLocation = value; } + } - /// - /// Gets or sets the value of password for the SOCKS proxy. - /// - [JsonPropertyName("socksPassword")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string? SocksPassword + /// + /// Gets or sets the value of the proxy for the SOCKS protocol. + /// + [JsonPropertyName("socksProxy")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? SocksProxy + { + get => this.socksProxyLocation; + + set { - get => this.socksPassword; + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.socksProxyLocation = value; + } + } - set - { - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); - this.proxyKind = ProxyKind.Manual; - this.socksPassword = value; - } + /// + /// Gets or sets the value of username for the SOCKS proxy. + /// + [JsonPropertyName("socksUsername")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? SocksUserName + { + get => this.socksUserName; + + set + { + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.socksUserName = value; } + } - /// - /// Adds a single address to the list of addresses against which the proxy will not be used. - /// - /// The address to add. - public void AddBypassAddress(string address) + /// + /// Gets or sets the value of the protocol version for the SOCKS proxy. + /// Value can be if not set. + /// + [JsonPropertyName("socksVersion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int? SocksVersion + { + get => this.socksVersion; + + set { - if (string.IsNullOrEmpty(address)) + if (value == null) { - throw new ArgumentException("address must not be null or empty", nameof(address)); + this.socksVersion = value; } + else + { + if (value.Value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "SocksVersion must be a positive integer"); + } - this.AddBypassAddresses(address); + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.socksVersion = value; + } } + } - /// - /// Adds addresses to the list of addresses against which the proxy will not be used. - /// - /// An array of addresses to add. - public void AddBypassAddresses(params string[] addressesToAdd) - { - this.AddBypassAddresses((IEnumerable)addressesToAdd); - } + /// + /// Gets or sets the value of password for the SOCKS proxy. + /// + [JsonPropertyName("socksPassword")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string? SocksPassword + { + get => this.socksPassword; - /// - /// Adds addresses to the list of addresses against which the proxy will not be used. - /// - /// An object of arguments to add. - public void AddBypassAddresses(IEnumerable addressesToAdd) + set { - if (addressesToAdd == null) - { - throw new ArgumentNullException(nameof(addressesToAdd), "addressesToAdd must not be null"); - } - this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); this.proxyKind = ProxyKind.Manual; - this.noProxyAddresses.AddRange(addressesToAdd); + this.socksPassword = value; } + } - /// - /// Returns a dictionary suitable for serializing to the W3C Specification - /// dialect of the wire protocol. - /// - /// A dictionary suitable for serializing to the W3C Specification - /// dialect of the wire protocol. - internal Dictionary? ToCapability() + /// + /// Adds a single address to the list of addresses against which the proxy will not be used. + /// + /// The address to add. + public void AddBypassAddress(string address) + { + if (string.IsNullOrEmpty(address)) { - return this.AsDictionary(true); + throw new ArgumentException("address must not be null or empty", nameof(address)); } - /// - /// Returns a dictionary suitable for serializing to the OSS dialect of the - /// wire protocol. - /// - /// A dictionary suitable for serializing to the OSS dialect of the - /// wire protocol. - internal Dictionary? ToLegacyCapability() + this.AddBypassAddresses(address); + } + + /// + /// Adds addresses to the list of addresses against which the proxy will not be used. + /// + /// An array of addresses to add. + public void AddBypassAddresses(params string[] addressesToAdd) + { + this.AddBypassAddresses((IEnumerable)addressesToAdd); + } + + /// + /// Adds addresses to the list of addresses against which the proxy will not be used. + /// + /// An object of arguments to add. + public void AddBypassAddresses(IEnumerable addressesToAdd) + { + if (addressesToAdd == null) { - return this.AsDictionary(false); + throw new ArgumentNullException(nameof(addressesToAdd), "addressesToAdd must not be null"); } - private Dictionary? AsDictionary(bool isSpecCompliant) + this.VerifyProxyTypeCompatilibily(ProxyKind.Manual); + this.proxyKind = ProxyKind.Manual; + this.noProxyAddresses.AddRange(addressesToAdd); + } + + /// + /// Returns a dictionary suitable for serializing to the W3C Specification + /// dialect of the wire protocol. + /// + /// A dictionary suitable for serializing to the W3C Specification + /// dialect of the wire protocol. + internal Dictionary? ToCapability() + { + return this.AsDictionary(true); + } + + /// + /// Returns a dictionary suitable for serializing to the OSS dialect of the + /// wire protocol. + /// + /// A dictionary suitable for serializing to the OSS dialect of the + /// wire protocol. + internal Dictionary? ToLegacyCapability() + { + return this.AsDictionary(false); + } + + private Dictionary? AsDictionary(bool isSpecCompliant) + { + Dictionary? serializedDictionary = null; + if (this.proxyKind != ProxyKind.Unspecified) { - Dictionary? serializedDictionary = null; - if (this.proxyKind != ProxyKind.Unspecified) + serializedDictionary = new Dictionary(); + if (this.proxyKind == ProxyKind.ProxyAutoConfigure) { - serializedDictionary = new Dictionary(); - if (this.proxyKind == ProxyKind.ProxyAutoConfigure) - { - serializedDictionary["proxyType"] = "pac"; - if (!string.IsNullOrEmpty(this.proxyAutoConfigUrl)) - { - serializedDictionary["proxyAutoconfigUrl"] = this.proxyAutoConfigUrl; - } - } - else + serializedDictionary["proxyType"] = "pac"; + if (!string.IsNullOrEmpty(this.proxyAutoConfigUrl)) { - serializedDictionary["proxyType"] = this.proxyKind.ToString().ToLowerInvariant(); + serializedDictionary["proxyAutoconfigUrl"] = this.proxyAutoConfigUrl; } + } + else + { + serializedDictionary["proxyType"] = this.proxyKind.ToString().ToLowerInvariant(); + } - if (!string.IsNullOrEmpty(this.httpProxyLocation)) - { - serializedDictionary["httpProxy"] = this.httpProxyLocation; - } + if (!string.IsNullOrEmpty(this.httpProxyLocation)) + { + serializedDictionary["httpProxy"] = this.httpProxyLocation; + } - if (!string.IsNullOrEmpty(this.sslProxyLocation)) - { - serializedDictionary["sslProxy"] = this.sslProxyLocation; - } + if (!string.IsNullOrEmpty(this.sslProxyLocation)) + { + serializedDictionary["sslProxy"] = this.sslProxyLocation; + } - if (!string.IsNullOrEmpty(this.ftpProxyLocation)) - { - serializedDictionary["ftpProxy"] = this.ftpProxyLocation; - } + if (!string.IsNullOrEmpty(this.ftpProxyLocation)) + { + serializedDictionary["ftpProxy"] = this.ftpProxyLocation; + } - if (!string.IsNullOrEmpty(this.socksProxyLocation)) + if (!string.IsNullOrEmpty(this.socksProxyLocation)) + { + if (!this.socksVersion.HasValue) { - if (!this.socksVersion.HasValue) - { - throw new InvalidOperationException("Must have a version value set (usually 4 or 5) when specifying a SOCKS proxy"); - } - - string socksAuth = string.Empty; - if (!string.IsNullOrEmpty(this.socksUserName) && !string.IsNullOrEmpty(this.socksPassword)) - { - // TODO: this is probably inaccurate as to how this is supposed - // to look. - socksAuth = this.socksUserName + ":" + this.socksPassword + "@"; - } - - serializedDictionary["socksProxy"] = socksAuth + this.socksProxyLocation; - serializedDictionary["socksVersion"] = this.socksVersion.Value; + throw new InvalidOperationException("Must have a version value set (usually 4 or 5) when specifying a SOCKS proxy"); } - if (this.noProxyAddresses.Count > 0) + string socksAuth = string.Empty; + if (!string.IsNullOrEmpty(this.socksUserName) && !string.IsNullOrEmpty(this.socksPassword)) { - serializedDictionary["noProxy"] = this.GetNoProxyAddressList(isSpecCompliant); + // TODO: this is probably inaccurate as to how this is supposed + // to look. + socksAuth = this.socksUserName + ":" + this.socksPassword + "@"; } - } - - return serializedDictionary; - } - private object? GetNoProxyAddressList(bool isSpecCompliant) - { - object? addresses = null; - if (isSpecCompliant) - { - List addressList = [.. this.noProxyAddresses]; - addresses = addressList; + serializedDictionary["socksProxy"] = socksAuth + this.socksProxyLocation; + serializedDictionary["socksVersion"] = this.socksVersion.Value; } - else + + if (this.noProxyAddresses.Count > 0) { - addresses = this.BypassProxyAddresses; + serializedDictionary["noProxy"] = this.GetNoProxyAddressList(isSpecCompliant); } + } + + return serializedDictionary; + } - return addresses; + private object? GetNoProxyAddressList(bool isSpecCompliant) + { + object? addresses = null; + if (isSpecCompliant) + { + List addressList = [.. this.noProxyAddresses]; + addresses = addressList; + } + else + { + addresses = this.BypassProxyAddresses; } - private void VerifyProxyTypeCompatilibily(ProxyKind compatibleProxy) + return addresses; + } + + private void VerifyProxyTypeCompatilibily(ProxyKind compatibleProxy) + { + if (this.proxyKind != ProxyKind.Unspecified && this.proxyKind != compatibleProxy) { - if (this.proxyKind != ProxyKind.Unspecified && this.proxyKind != compatibleProxy) - { - string errorMessage = string.Format( - CultureInfo.InvariantCulture, - "Specified proxy type {0} is not compatible with current setting {1}", - compatibleProxy.ToString().ToUpperInvariant(), - this.proxyKind.ToString().ToUpperInvariant()); + string errorMessage = string.Format( + CultureInfo.InvariantCulture, + "Specified proxy type {0} is not compatible with current setting {1}", + compatibleProxy.ToString().ToUpperInvariant(), + this.proxyKind.ToString().ToUpperInvariant()); - throw new InvalidOperationException(errorMessage); - } + throw new InvalidOperationException(errorMessage); } } } diff --git a/dotnet/src/webdriver/RelativeBy.cs b/dotnet/src/webdriver/RelativeBy.cs index 57d9b7a3f844c..2823b1c6a7f71 100644 --- a/dotnet/src/webdriver/RelativeBy.cs +++ b/dotnet/src/webdriver/RelativeBy.cs @@ -24,404 +24,403 @@ using System.Globalization; using System.IO; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for finding elements spatially relative to other elements. +/// +public sealed class RelativeBy : By { - /// - /// Provides a mechanism for finding elements spatially relative to other elements. - /// - public sealed class RelativeBy : By - { - private readonly string wrappedAtom; - private readonly object root; - private readonly List filters = new List(); + private readonly string wrappedAtom; + private readonly object root; + private readonly List filters = new List(); - private static string GetWrappedAtom() + private static string GetWrappedAtom() + { + string atom; + using (Stream atomStream = ResourceUtilities.GetResourceStream("find-elements.js", "find-elements.js")) { - string atom; - using (Stream atomStream = ResourceUtilities.GetResourceStream("find-elements.js", "find-elements.js")) + using (StreamReader atomReader = new StreamReader(atomStream)) { - using (StreamReader atomReader = new StreamReader(atomStream)) - { - atom = atomReader.ReadToEnd(); - } + atom = atomReader.ReadToEnd(); } - - return string.Format(CultureInfo.InvariantCulture, "/* findElements */return ({0}).apply(null, arguments);", atom); } - private RelativeBy(object root, List? filters = null) - { - this.wrappedAtom = GetWrappedAtom(); - this.root = GetSerializableRoot(root); - if (filters != null) - { - this.filters.AddRange(filters); - } - } + return string.Format(CultureInfo.InvariantCulture, "/* findElements */return ({0}).apply(null, arguments);", atom); + } - /// - /// Creates a new for finding elements with the specified tag name. - /// - /// A By object that will be used to find the initial element. - /// A object to be used in finding the elements. - /// If is null. - public static RelativeBy WithLocator(By by) + private RelativeBy(object root, List? filters = null) + { + this.wrappedAtom = GetWrappedAtom(); + this.root = GetSerializableRoot(root); + if (filters != null) { - return new RelativeBy(by); + this.filters.AddRange(filters); } + } - /// - /// Finds the first element matching the criteria. - /// - /// An object to use to search for the elements. - /// The first matching on the current context. - /// If is not or wraps a driver that does. - public override IWebElement FindElement(ISearchContext context) - { - ReadOnlyCollection elements = FindElements(context); - if (elements.Count == 0) - { - throw new NoSuchElementException("Unable to find element"); - } + /// + /// Creates a new for finding elements with the specified tag name. + /// + /// A By object that will be used to find the initial element. + /// A object to be used in finding the elements. + /// If is null. + public static RelativeBy WithLocator(By by) + { + return new RelativeBy(by); + } - return elements[0]; + /// + /// Finds the first element matching the criteria. + /// + /// An object to use to search for the elements. + /// The first matching on the current context. + /// If is not or wraps a driver that does. + public override IWebElement FindElement(ISearchContext context) + { + ReadOnlyCollection elements = FindElements(context); + if (elements.Count == 0) + { + throw new NoSuchElementException("Unable to find element"); } - /// - /// Finds all elements matching the criteria. - /// - /// An object to use to search for the elements. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - /// If is not or wraps a driver that does. - public override ReadOnlyCollection FindElements(ISearchContext context) + return elements[0]; + } + + /// + /// Finds all elements matching the criteria. + /// + /// An object to use to search for the elements. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + /// If is not or wraps a driver that does. + public override ReadOnlyCollection FindElements(ISearchContext context) + { + IJavaScriptExecutor js = GetExecutor(context); + Dictionary parameters = new Dictionary(); + Dictionary filterParameters = new Dictionary(); + filterParameters["root"] = GetSerializableObject(this.root); + filterParameters["filters"] = this.filters; + parameters["relative"] = filterParameters; + object? rawElements = js.ExecuteScript(wrappedAtom, parameters); + + if (rawElements is ReadOnlyCollection elements) { - IJavaScriptExecutor js = GetExecutor(context); - Dictionary parameters = new Dictionary(); - Dictionary filterParameters = new Dictionary(); - filterParameters["root"] = GetSerializableObject(this.root); - filterParameters["filters"] = this.filters; - parameters["relative"] = filterParameters; - object? rawElements = js.ExecuteScript(wrappedAtom, parameters); - - if (rawElements is ReadOnlyCollection elements) - { - return elements; - } + return elements; + } - // De-serializer quirk - if the response is empty then the de-serializer will not know we're getting back elements - // We will have a ReadOnlyCollection + // De-serializer quirk - if the response is empty then the de-serializer will not know we're getting back elements + // We will have a ReadOnlyCollection - if (rawElements is ReadOnlyCollection elementsObj) + if (rawElements is ReadOnlyCollection elementsObj) + { + if (elementsObj.Count == 0) { - if (elementsObj.Count == 0) - { #if NET8_0_OR_GREATER - return ReadOnlyCollection.Empty; + return ReadOnlyCollection.Empty; #else - return new List().AsReadOnly(); + return new List().AsReadOnly(); #endif - } } - - throw new WebDriverException($"Could not de-serialize element list response{Environment.NewLine}{rawElements}"); } - /// - /// Locates an element above the specified element. - /// - /// The element to look above for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Above(IWebElement element) - { - if (element == null) - { - throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); - } - - return SimpleDirection("above", element); - } + throw new WebDriverException($"Could not de-serialize element list response{Environment.NewLine}{rawElements}"); + } - /// - /// Locates an element above the specified element. - /// - /// The locator describing the element to look above for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Above(By locator) + /// + /// Locates an element above the specified element. + /// + /// The element to look above for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Above(IWebElement element) + { + if (element == null) { - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); - } - - return SimpleDirection("above", locator); + throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); } - /// - /// Locates an element below the specified element. - /// - /// The element to look below for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Below(IWebElement element) - { - if (element == null) - { - throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); - } - - return SimpleDirection("below", element); - } + return SimpleDirection("above", element); + } - /// - /// Locates an element below the specified element. - /// - /// The locator describing the element to look below for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Below(By locator) + /// + /// Locates an element above the specified element. + /// + /// The locator describing the element to look above for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Above(By locator) + { + if (locator == null) { - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); - } - - return SimpleDirection("below", locator); + throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); } - /// - /// Locates an element to the left of the specified element. - /// - /// The element to look to the left of for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy LeftOf(IWebElement element) - { - if (element == null) - { - throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); - } - - return SimpleDirection("left", element); - } + return SimpleDirection("above", locator); + } - /// - /// Locates an element to the left of the specified element. - /// - /// The locator describing the element to look to the left of for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy LeftOf(By locator) + /// + /// Locates an element below the specified element. + /// + /// The element to look below for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Below(IWebElement element) + { + if (element == null) { - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); - } - - return SimpleDirection("left", locator); + throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); } - /// - /// Locates an element to the right of the specified element. - /// - /// The element to look to the right of for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy RightOf(IWebElement element) - { - if (element == null) - { - throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); - } + return SimpleDirection("below", element); + } - return SimpleDirection("right", element); + /// + /// Locates an element below the specified element. + /// + /// The locator describing the element to look below for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Below(By locator) + { + if (locator == null) + { + throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); } - /// - /// Locates an element to the right of the specified element. - /// - /// The locator describing the element to look to the right of for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy RightOf(By locator) - { - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); - } + return SimpleDirection("below", locator); + } - return SimpleDirection("right", locator); + /// + /// Locates an element to the left of the specified element. + /// + /// The element to look to the left of for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy LeftOf(IWebElement element) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); } - /// - /// Locates an element near the specified element. - /// - /// The element to look near for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Near(IWebElement element) + return SimpleDirection("left", element); + } + + /// + /// Locates an element to the left of the specified element. + /// + /// The locator describing the element to look to the left of for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy LeftOf(By locator) + { + if (locator == null) { - return Near(element, 50); + throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); } - /// - /// Locates an element near the specified element. - /// - /// The element to look near for elements. - /// The maximum distance from the element to be considered "near." - /// A object for use in finding the elements. - /// If is null. - /// If is not a positive value. - public RelativeBy Near(IWebElement element, int atMostDistanceInPixels) + return SimpleDirection("left", locator); + } + + /// + /// Locates an element to the right of the specified element. + /// + /// The element to look to the right of for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy RightOf(IWebElement element) + { + if (element == null) { - return Near((object)element, atMostDistanceInPixels); + throw new ArgumentNullException(nameof(element), "Element relative to cannot be null"); } - /// - /// Locates an element near the specified element. - /// - /// The locator describing the element to look near for elements. - /// A object for use in finding the elements. - /// If is null. - public RelativeBy Near(By locator) + return SimpleDirection("right", element); + } + + /// + /// Locates an element to the right of the specified element. + /// + /// The locator describing the element to look to the right of for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy RightOf(By locator) + { + if (locator == null) { - return Near(locator, 50); + throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); } - /// - /// Locates an element near the specified element. - /// - /// The locator describing the element to look near for elements. - /// The maximum distance from the element to be considered "near." - /// A object for use in finding the elements. - /// If is null. - /// If is not a positive value. - public RelativeBy Near(By locator, int atMostDistanceInPixels) + return SimpleDirection("right", locator); + } + + /// + /// Locates an element near the specified element. + /// + /// The element to look near for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Near(IWebElement element) + { + return Near(element, 50); + } + + /// + /// Locates an element near the specified element. + /// + /// The element to look near for elements. + /// The maximum distance from the element to be considered "near." + /// A object for use in finding the elements. + /// If is null. + /// If is not a positive value. + public RelativeBy Near(IWebElement element, int atMostDistanceInPixels) + { + return Near((object)element, atMostDistanceInPixels); + } + + /// + /// Locates an element near the specified element. + /// + /// The locator describing the element to look near for elements. + /// A object for use in finding the elements. + /// If is null. + public RelativeBy Near(By locator) + { + return Near(locator, 50); + } + + /// + /// Locates an element near the specified element. + /// + /// The locator describing the element to look near for elements. + /// The maximum distance from the element to be considered "near." + /// A object for use in finding the elements. + /// If is null. + /// If is not a positive value. + public RelativeBy Near(By locator, int atMostDistanceInPixels) + { + return Near((object)locator, atMostDistanceInPixels); + } + + private RelativeBy Near(object locator, int atMostDistanceInPixels) + { + if (locator == null) { - return Near((object)locator, atMostDistanceInPixels); + throw new ArgumentNullException(nameof(locator), "Locator to use to search must be set"); } - private RelativeBy Near(object locator, int atMostDistanceInPixels) + if (atMostDistanceInPixels <= 0) { - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Locator to use to search must be set"); - } + throw new ArgumentOutOfRangeException(nameof(atMostDistanceInPixels), "Distance must be greater than zero"); + } - if (atMostDistanceInPixels <= 0) - { - throw new ArgumentOutOfRangeException(nameof(atMostDistanceInPixels), "Distance must be greater than zero"); - } + Dictionary filter = new Dictionary(); + filter["kind"] = "near"; + filter["args"] = new List() { GetSerializableObject(locator), atMostDistanceInPixels }; + this.filters.Add(filter); - Dictionary filter = new Dictionary(); - filter["kind"] = "near"; - filter["args"] = new List() { GetSerializableObject(locator), atMostDistanceInPixels }; - this.filters.Add(filter); + return new RelativeBy(this.root, this.filters); + } - return new RelativeBy(this.root, this.filters); + private RelativeBy SimpleDirection(string direction, object locator) + { + if (string.IsNullOrEmpty(direction)) + { + throw new ArgumentNullException(nameof(direction), "Direction cannot be null or the empty string"); } - private RelativeBy SimpleDirection(string direction, object locator) + if (locator == null) { - if (string.IsNullOrEmpty(direction)) - { - throw new ArgumentNullException(nameof(direction), "Direction cannot be null or the empty string"); - } + throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); + } - if (locator == null) - { - throw new ArgumentNullException(nameof(locator), "Element locator to cannot be null"); - } + Dictionary filter = new Dictionary(); + filter["kind"] = direction; + filter["args"] = new List() { GetSerializableObject(locator) }; + this.filters.Add(filter); - Dictionary filter = new Dictionary(); - filter["kind"] = direction; - filter["args"] = new List() { GetSerializableObject(locator) }; - this.filters.Add(filter); + return new RelativeBy(this.root, this.filters); + } - return new RelativeBy(this.root, this.filters); + private static object GetSerializableRoot(object root) + { + if (root == null) + { + throw new ArgumentNullException(nameof(root), "object to serialize must not be null"); } - private static object GetSerializableRoot(object root) + if (root is By asBy) { - if (root == null) - { - throw new ArgumentNullException(nameof(root), "object to serialize must not be null"); - } + return asBy; + } - if (root is By asBy) - { - return asBy; - } + if (root is IWebElement element) + { + return element; + } - if (root is IWebElement element) - { - return element; - } + if (root is IWrapsElement wrapper) + { + return wrapper.WrappedElement; + } - if (root is IWrapsElement wrapper) - { - return wrapper.WrappedElement; - } + throw new WebDriverException("Serializable locator must be a By, an IWebElement, or a wrapped element using IWrapsElement"); + } - throw new WebDriverException("Serializable locator must be a By, an IWebElement, or a wrapped element using IWrapsElement"); + private static object GetSerializableObject(object root) + { + if (root == null) + { + throw new ArgumentNullException(nameof(root), "object to serialize must not be null"); } - private static object GetSerializableObject(object root) + if (root is By asBy) { - if (root == null) - { - throw new ArgumentNullException(nameof(root), "object to serialize must not be null"); - } + Dictionary serializedBy = new Dictionary(); + serializedBy[asBy.Mechanism] = asBy.Criteria; + return serializedBy; + } - if (root is By asBy) - { - Dictionary serializedBy = new Dictionary(); - serializedBy[asBy.Mechanism] = asBy.Criteria; - return serializedBy; - } + if (root is IWebElement element) + { + return element; + } - if (root is IWebElement element) - { - return element; - } + if (root is IWrapsElement wrapper) + { + return wrapper.WrappedElement; + } - if (root is IWrapsElement wrapper) - { - return wrapper.WrappedElement; - } + throw new WebDriverException("Serializable locator must be a By, an IWebElement, or a wrapped element using IWrapsElement"); + } - throw new WebDriverException("Serializable locator must be a By, an IWebElement, or a wrapped element using IWrapsElement"); + private static IJavaScriptExecutor GetExecutor(ISearchContext context) + { + IJavaScriptExecutor? executor = context as IJavaScriptExecutor; + if (executor != null) + { + return executor; } - private static IJavaScriptExecutor GetExecutor(ISearchContext context) + IWrapsDriver? current = context as IWrapsDriver; + while (current != null) { - IJavaScriptExecutor? executor = context as IJavaScriptExecutor; + IWebDriver driver = current.WrappedDriver; + executor = driver as IJavaScriptExecutor; if (executor != null) { - return executor; - } - - IWrapsDriver? current = context as IWrapsDriver; - while (current != null) - { - IWebDriver driver = current.WrappedDriver; - executor = driver as IJavaScriptExecutor; - if (executor != null) - { - break; - } - - current = driver as IWrapsDriver; + break; } - if (executor == null) - { - throw new ArgumentException("Search context must support JavaScript or IWrapsDriver where the wrapped driver supports JavaScript", nameof(context)); - } + current = driver as IWrapsDriver; + } - return executor; + if (executor == null) + { + throw new ArgumentException("Search context must support JavaScript or IWrapsDriver where the wrapped driver supports JavaScript", nameof(context)); } + + return executor; } } diff --git a/dotnet/src/webdriver/Remote/DesiredCapabilities.cs b/dotnet/src/webdriver/Remote/DesiredCapabilities.cs index 526ebd87344f0..e8990d39028c0 100644 --- a/dotnet/src/webdriver/Remote/DesiredCapabilities.cs +++ b/dotnet/src/webdriver/Remote/DesiredCapabilities.cs @@ -23,269 +23,268 @@ using System.Collections.ObjectModel; using System.Globalization; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Internal class to specify the requested capabilities of the browser for . +/// +internal class DesiredCapabilities : IWritableCapabilities, IHasCapabilitiesDictionary { + private readonly Dictionary capabilities = new Dictionary(); + /// - /// Internal class to specify the requested capabilities of the browser for . + /// Initializes a new instance of the class /// - internal class DesiredCapabilities : IWritableCapabilities, IHasCapabilitiesDictionary + /// Name of the browser e.g. firefox, internet explorer, safari + /// Version of the browser + /// The platform it works on + public DesiredCapabilities(string browser, string version, Platform platform) { - private readonly Dictionary capabilities = new Dictionary(); - - /// - /// Initializes a new instance of the class - /// - /// Name of the browser e.g. firefox, internet explorer, safari - /// Version of the browser - /// The platform it works on - public DesiredCapabilities(string browser, string version, Platform platform) - { - this.SetCapability(CapabilityType.BrowserName, browser); - this.SetCapability(CapabilityType.Version, version); - this.SetCapability(CapabilityType.Platform, platform); - } + this.SetCapability(CapabilityType.BrowserName, browser); + this.SetCapability(CapabilityType.Version, version); + this.SetCapability(CapabilityType.Platform, platform); + } - /// - /// Initializes a new instance of the class - /// - public DesiredCapabilities() - { - } + /// + /// Initializes a new instance of the class + /// + public DesiredCapabilities() + { + } - /// - /// Initializes a new instance of the class - /// - /// Dictionary of items for the remote driver - /// - /// - /// DesiredCapabilities capabilities = new DesiredCapabilities(new Dictionary]]>(){["browserName","firefox"],["version",string.Empty],["javaScript",true]}); - /// - /// - public DesiredCapabilities(Dictionary? rawMap) + /// + /// Initializes a new instance of the class + /// + /// Dictionary of items for the remote driver + /// + /// + /// DesiredCapabilities capabilities = new DesiredCapabilities(new Dictionary]]>(){["browserName","firefox"],["version",string.Empty],["javaScript",true]}); + /// + /// + public DesiredCapabilities(Dictionary? rawMap) + { + if (rawMap != null) { - if (rawMap != null) + foreach (KeyValuePair entry in rawMap) { - foreach (KeyValuePair entry in rawMap) + if (entry.Key == CapabilityType.Platform) { - if (entry.Key == CapabilityType.Platform) + if (entry.Value is string rawAsString) { - if (entry.Value is string rawAsString) - { - this.SetCapability(CapabilityType.Platform, Platform.FromString(rawAsString)); - } - else if (entry.Value is Platform rawAsPlatform) - { - this.SetCapability(CapabilityType.Platform, rawAsPlatform); - } + this.SetCapability(CapabilityType.Platform, Platform.FromString(rawAsString)); } - else + else if (entry.Value is Platform rawAsPlatform) { - this.SetCapability(entry.Key, entry.Value); + this.SetCapability(CapabilityType.Platform, rawAsPlatform); } } + else + { + this.SetCapability(entry.Key, entry.Value); + } } } + } - /// - /// Initializes a new instance of the class - /// - /// Name of the browser e.g. firefox, internet explorer, safari - /// Version of the browser - /// The platform it works on - /// Sets a value indicating whether the capabilities are - /// compliant with the W3C WebDriver specification. - internal DesiredCapabilities(string browser, string version, Platform platform, bool isSpecCompliant) - { - this.SetCapability(CapabilityType.BrowserName, browser); - this.SetCapability(CapabilityType.Version, version); - this.SetCapability(CapabilityType.Platform, platform); - } + /// + /// Initializes a new instance of the class + /// + /// Name of the browser e.g. firefox, internet explorer, safari + /// Version of the browser + /// The platform it works on + /// Sets a value indicating whether the capabilities are + /// compliant with the W3C WebDriver specification. + internal DesiredCapabilities(string browser, string version, Platform platform, bool isSpecCompliant) + { + this.SetCapability(CapabilityType.BrowserName, browser); + this.SetCapability(CapabilityType.Version, version); + this.SetCapability(CapabilityType.Platform, platform); + } - /// - /// Gets the browser name - /// - public string BrowserName => this.GetCapability(CapabilityType.BrowserName)?.ToString() ?? string.Empty; + /// + /// Gets the browser name + /// + public string BrowserName => this.GetCapability(CapabilityType.BrowserName)?.ToString() ?? string.Empty; - /// - /// Gets or sets the platform - /// - public Platform Platform - { - get => this.GetCapability(CapabilityType.Platform) as Platform ?? new Platform(PlatformType.Any); - set => this.SetCapability(CapabilityType.Platform, value); - } + /// + /// Gets or sets the platform + /// + public Platform Platform + { + get => this.GetCapability(CapabilityType.Platform) as Platform ?? new Platform(PlatformType.Any); + set => this.SetCapability(CapabilityType.Platform, value); + } - /// - /// Gets the browser version - /// - public string Version => this.GetCapability(CapabilityType.Version)?.ToString() ?? string.Empty; + /// + /// Gets the browser version + /// + public string Version => this.GetCapability(CapabilityType.Version)?.ToString() ?? string.Empty; - /// - /// Gets or sets a value indicating whether the browser accepts SSL certificates. - /// - public bool AcceptInsecureCerts + /// + /// Gets or sets a value indicating whether the browser accepts SSL certificates. + /// + public bool AcceptInsecureCerts + { + get { - get + bool acceptSSLCerts = false; + object? capabilityValue = this.GetCapability(CapabilityType.AcceptInsecureCertificates); + if (capabilityValue != null) { - bool acceptSSLCerts = false; - object? capabilityValue = this.GetCapability(CapabilityType.AcceptInsecureCertificates); - if (capabilityValue != null) - { - acceptSSLCerts = (bool)capabilityValue; - } - - return acceptSSLCerts; + acceptSSLCerts = (bool)capabilityValue; } - set => this.SetCapability(CapabilityType.AcceptInsecureCertificates, value); + return acceptSSLCerts; } - /// - /// Gets the underlying Dictionary for a given set of capabilities. - /// - IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; - - /// - /// Gets the underlying Dictionary for a given set of capabilities. - /// - internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); - - /// - /// Gets the capability value with the specified name. - /// - /// The name of the capability to get. - /// The value of the capability. - /// - /// The specified capability name is not in the set of capabilities. - /// - public object this[string capabilityName] - { - get - { - if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); - } + set => this.SetCapability(CapabilityType.AcceptInsecureCertificates, value); + } - return capabilityValue; - } - } + /// + /// Gets the underlying Dictionary for a given set of capabilities. + /// + IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; - /// - /// Gets a value indicating whether the browser has a given capability. - /// - /// The capability to get. - /// Returns if the browser has the capability; otherwise, . - public bool HasCapability(string capability) - { - return this.capabilities.ContainsKey(capability); - } + /// + /// Gets the underlying Dictionary for a given set of capabilities. + /// + internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); - /// - /// Gets a capability of the browser. - /// - /// The capability to get. - /// An object associated with the capability, or - /// if the capability is not set on the browser. - public object? GetCapability(string capability) + /// + /// Gets the capability value with the specified name. + /// + /// The name of the capability to get. + /// The value of the capability. + /// + /// The specified capability name is not in the set of capabilities. + /// + public object this[string capabilityName] + { + get { - object? capabilityValue = null; - if (this.capabilities.TryGetValue(capability, out object? value)) + if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) { - capabilityValue = value; - if (capability == CapabilityType.Platform && capabilityValue is string capabilityValueString) - { - capabilityValue = Platform.FromString(capabilityValueString); - } + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); } return capabilityValue; } + } + + /// + /// Gets a value indicating whether the browser has a given capability. + /// + /// The capability to get. + /// Returns if the browser has the capability; otherwise, . + public bool HasCapability(string capability) + { + return this.capabilities.ContainsKey(capability); + } - /// - /// Sets a capability of the browser. - /// - /// The capability to get. - /// The value for the capability. - public void SetCapability(string capability, object capabilityValue) + /// + /// Gets a capability of the browser. + /// + /// The capability to get. + /// An object associated with the capability, or + /// if the capability is not set on the browser. + public object? GetCapability(string capability) + { + object? capabilityValue = null; + if (this.capabilities.TryGetValue(capability, out object? value)) { - // Handle the special case of Platform objects. These should - // be stored in the underlying dictionary as their protocol - // string representation. - if (capabilityValue is Platform platformCapabilityValue) + capabilityValue = value; + if (capability == CapabilityType.Platform && capabilityValue is string capabilityValueString) { - this.capabilities[capability] = platformCapabilityValue.ProtocolPlatformType; - } - else - { - this.capabilities[capability] = capabilityValue; + capabilityValue = Platform.FromString(capabilityValueString); } } - /// - /// Return HashCode for the DesiredCapabilities that has been created - /// - /// Integer of HashCode generated - public override int GetHashCode() + return capabilityValue; + } + + /// + /// Sets a capability of the browser. + /// + /// The capability to get. + /// The value for the capability. + public void SetCapability(string capability, object capabilityValue) + { + // Handle the special case of Platform objects. These should + // be stored in the underlying dictionary as their protocol + // string representation. + if (capabilityValue is Platform platformCapabilityValue) { - int result; - result = this.BrowserName != null ? this.BrowserName.GetHashCode() : 0; - result = (31 * result) + (this.Version != null ? this.Version.GetHashCode() : 0); - result = (31 * result) + (this.Platform != null ? this.Platform.GetHashCode() : 0); - return result; + this.capabilities[capability] = platformCapabilityValue.ProtocolPlatformType; } - - /// - /// Return a string of capabilities being used - /// - /// String of capabilities being used - public override string ToString() + else { - return string.Format(CultureInfo.InvariantCulture, "Capabilities [BrowserName={0}, Platform={1}, Version={2}]", this.BrowserName, this.Platform.PlatformType.ToString(), this.Version); + this.capabilities[capability] = capabilityValue; } + } - /// - /// Compare two DesiredCapabilities and will return either true or false - /// - /// DesiredCapabilities you wish to compare - /// true if they are the same or false if they are not - public override bool Equals(object? obj) - { - if (this == obj) - { - return true; - } + /// + /// Return HashCode for the DesiredCapabilities that has been created + /// + /// Integer of HashCode generated + public override int GetHashCode() + { + int result; + result = this.BrowserName != null ? this.BrowserName.GetHashCode() : 0; + result = (31 * result) + (this.Version != null ? this.Version.GetHashCode() : 0); + result = (31 * result) + (this.Platform != null ? this.Platform.GetHashCode() : 0); + return result; + } - if (obj is not DesiredCapabilities other) - { - return false; - } + /// + /// Return a string of capabilities being used + /// + /// String of capabilities being used + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Capabilities [BrowserName={0}, Platform={1}, Version={2}]", this.BrowserName, this.Platform.PlatformType.ToString(), this.Version); + } - if (this.BrowserName != null ? this.BrowserName != other.BrowserName : other.BrowserName != null) - { - return false; - } + /// + /// Compare two DesiredCapabilities and will return either true or false + /// + /// DesiredCapabilities you wish to compare + /// true if they are the same or false if they are not + public override bool Equals(object? obj) + { + if (this == obj) + { + return true; + } - if (!this.Platform.IsPlatformType(other.Platform.PlatformType)) - { - return false; - } + if (obj is not DesiredCapabilities other) + { + return false; + } - if (this.Version != null ? this.Version != other.Version : other.Version != null) - { - return false; - } + if (this.BrowserName != null ? this.BrowserName != other.BrowserName : other.BrowserName != null) + { + return false; + } - return true; + if (!this.Platform.IsPlatformType(other.Platform.PlatformType)) + { + return false; } - /// - /// Returns a read-only version of this capabilities object. - /// - /// A read-only version of this capabilities object. - public ICapabilities AsReadOnly() + if (this.Version != null ? this.Version != other.Version : other.Version != null) { - return new ReadOnlyDesiredCapabilities(this); + return false; } + + return true; + } + + /// + /// Returns a read-only version of this capabilities object. + /// + /// A read-only version of this capabilities object. + public ICapabilities AsReadOnly() + { + return new ReadOnlyDesiredCapabilities(this); } } diff --git a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs index a83693d02c13e..095c69cb99ab3 100644 --- a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs +++ b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs @@ -21,147 +21,146 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Provides a mechanism to execute commands on the browser +/// +public class DriverServiceCommandExecutor : ICommandExecutor { + private readonly DriverService service; + private bool isDisposed; + /// - /// Provides a mechanism to execute commands on the browser + /// Initializes a new instance of the class. /// - public class DriverServiceCommandExecutor : ICommandExecutor + /// The that drives the browser. + /// The maximum amount of time to wait for each command. + /// If is . + public DriverServiceCommandExecutor(DriverService driverService, TimeSpan commandTimeout) + : this(driverService, commandTimeout, true) { - private readonly DriverService service; - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - /// The that drives the browser. - /// The maximum amount of time to wait for each command. - /// If is . - public DriverServiceCommandExecutor(DriverService driverService, TimeSpan commandTimeout) - : this(driverService, commandTimeout, true) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The that drives the browser. - /// The maximum amount of time to wait for each command. - /// if the KeepAlive header should be sent - /// with HTTP requests; otherwise, . - /// If is . - public DriverServiceCommandExecutor(DriverService driverService, TimeSpan commandTimeout, bool enableKeepAlive) - { - this.service = driverService ?? throw new ArgumentNullException(nameof(driverService)); - this.HttpExecutor = new HttpCommandExecutor(driverService.ServiceUrl, commandTimeout, enableKeepAlive); - } + /// + /// Initializes a new instance of the class. + /// + /// The that drives the browser. + /// The maximum amount of time to wait for each command. + /// if the KeepAlive header should be sent + /// with HTTP requests; otherwise, . + /// If is . + public DriverServiceCommandExecutor(DriverService driverService, TimeSpan commandTimeout, bool enableKeepAlive) + { + this.service = driverService ?? throw new ArgumentNullException(nameof(driverService)); + this.HttpExecutor = new HttpCommandExecutor(driverService.ServiceUrl, commandTimeout, enableKeepAlive); + } - /// - /// Initializes a new instance of the class. - /// - /// The that drives the browser. - /// The object used to execute commands, - /// communicating with the service via HTTP. - /// If or are . - public DriverServiceCommandExecutor(DriverService service, HttpCommandExecutor commandExecutor) - { - this.service = service ?? throw new ArgumentNullException(nameof(service)); - this.HttpExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor)); - } + /// + /// Initializes a new instance of the class. + /// + /// The that drives the browser. + /// The object used to execute commands, + /// communicating with the service via HTTP. + /// If or are . + public DriverServiceCommandExecutor(DriverService service, HttpCommandExecutor commandExecutor) + { + this.service = service ?? throw new ArgumentNullException(nameof(service)); + this.HttpExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor)); + } + + /// + /// Gets the object associated with this executor. + /// + //public CommandInfoRepository CommandInfoRepository + //{ + // get { return this.HttpExecutor.CommandInfoRepository; } + //} + + public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info) + { + return this.HttpExecutor.TryAddCommand(commandName, info); + } - /// - /// Gets the object associated with this executor. - /// - //public CommandInfoRepository CommandInfoRepository - //{ - // get { return this.HttpExecutor.CommandInfoRepository; } - //} + /// + /// Gets the that sends commands to the remote + /// end WebDriver implementation. + /// + public HttpCommandExecutor HttpExecutor { get; } - public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info) + /// + /// Executes a command + /// + /// The command you wish to execute + /// A response from the browser + /// If is . + public Response Execute(Command commandToExecute) + { + return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult(); + } + + /// + /// Executes a command as an asynchronous task. + /// + /// The command you wish to execute + /// A task object representing the asynchronous operation + /// If is . + public async Task ExecuteAsync(Command commandToExecute) + { + if (commandToExecute == null) { - return this.HttpExecutor.TryAddCommand(commandName, info); + throw new ArgumentNullException(nameof(commandToExecute), "Command to execute cannot be null"); } - /// - /// Gets the that sends commands to the remote - /// end WebDriver implementation. - /// - public HttpCommandExecutor HttpExecutor { get; } - - /// - /// Executes a command - /// - /// The command you wish to execute - /// A response from the browser - /// If is . - public Response Execute(Command commandToExecute) + Response toReturn; + if (commandToExecute.Name == DriverCommand.NewSession) { - return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult(); + this.service.Start(); } - /// - /// Executes a command as an asynchronous task. - /// - /// The command you wish to execute - /// A task object representing the asynchronous operation - /// If is . - public async Task ExecuteAsync(Command commandToExecute) + // Use a try-catch block to catch exceptions for the Quit + // command, so that we can get the finally block. + try { - if (commandToExecute == null) - { - throw new ArgumentNullException(nameof(commandToExecute), "Command to execute cannot be null"); - } - - Response toReturn; - if (commandToExecute.Name == DriverCommand.NewSession) - { - this.service.Start(); - } - - // Use a try-catch block to catch exceptions for the Quit - // command, so that we can get the finally block. - try - { - toReturn = await this.HttpExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false); - } - finally + toReturn = await this.HttpExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false); + } + finally + { + if (commandToExecute.Name == DriverCommand.Quit) { - if (commandToExecute.Name == DriverCommand.Quit) - { - this.Dispose(); - } + this.Dispose(); } - - return toReturn; } - /// - /// Releases all resources used by the . - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } + return toReturn; + } + + /// + /// Releases all resources used by the . + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// to release managed and resources; - /// to only release unmanaged resources. - protected virtual void Dispose(bool disposing) + /// + /// Releases the unmanaged resources used by the and + /// optionally releases the managed resources. + /// + /// to release managed and resources; + /// to only release unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) { - if (!this.isDisposed) + if (disposing) { - if (disposing) - { - this.HttpExecutor.Dispose(); - this.service.Dispose(); - } - - this.isDisposed = true; + this.HttpExecutor.Dispose(); + this.service.Dispose(); } + + this.isDisposed = true; } } } diff --git a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs index 3094c539d83fc..22d163641ff35 100644 --- a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs +++ b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs @@ -31,447 +31,446 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Provides a way of executing Commands over HTTP +/// +public class HttpCommandExecutor : ICommandExecutor { + private const string JsonMimeType = "application/json"; + private const string PngMimeType = "image/png"; + private const string Utf8CharsetType = "utf-8"; + private const string RequestAcceptHeader = JsonMimeType + ", " + PngMimeType; + private const string RequestContentTypeHeader = JsonMimeType + "; charset=" + Utf8CharsetType; + private const string UserAgentHeaderTemplate = "selenium/{0} (.net {1})"; + private readonly Uri remoteServerUri; + private readonly TimeSpan serverResponseTimeout; + private bool isDisposed; + private CommandInfoRepository commandInfoRepository = new W3CWireProtocolCommandInfoRepository(); + private readonly Lazy client; + + private static readonly ILogger _logger = Log.GetLogger(); + /// - /// Provides a way of executing Commands over HTTP + /// Initializes a new instance of the class /// - public class HttpCommandExecutor : ICommandExecutor + /// Address of the WebDriver Server + /// The timeout within which the server must respond. + /// If is . + public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout) + : this(addressOfRemoteServer, timeout, true) { - private const string JsonMimeType = "application/json"; - private const string PngMimeType = "image/png"; - private const string Utf8CharsetType = "utf-8"; - private const string RequestAcceptHeader = JsonMimeType + ", " + PngMimeType; - private const string RequestContentTypeHeader = JsonMimeType + "; charset=" + Utf8CharsetType; - private const string UserAgentHeaderTemplate = "selenium/{0} (.net {1})"; - private readonly Uri remoteServerUri; - private readonly TimeSpan serverResponseTimeout; - private bool isDisposed; - private CommandInfoRepository commandInfoRepository = new W3CWireProtocolCommandInfoRepository(); - private readonly Lazy client; - - private static readonly ILogger _logger = Log.GetLogger(); + } - /// - /// Initializes a new instance of the class - /// - /// Address of the WebDriver Server - /// The timeout within which the server must respond. - /// If is . - public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout) - : this(addressOfRemoteServer, timeout, true) + /// + /// Initializes a new instance of the class + /// + /// Address of the WebDriver Server + /// The timeout within which the server must respond. + /// if the KeepAlive header should be sent + /// with HTTP requests; otherwise, . + /// If is . + public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool enableKeepAlive) + { + if (addressOfRemoteServer == null) { + throw new ArgumentNullException(nameof(addressOfRemoteServer), "You must specify a remote address to connect to"); } - /// - /// Initializes a new instance of the class - /// - /// Address of the WebDriver Server - /// The timeout within which the server must respond. - /// if the KeepAlive header should be sent - /// with HTTP requests; otherwise, . - /// If is . - public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool enableKeepAlive) + if (!addressOfRemoteServer.AbsoluteUri.EndsWith("/", StringComparison.OrdinalIgnoreCase)) { - if (addressOfRemoteServer == null) - { - throw new ArgumentNullException(nameof(addressOfRemoteServer), "You must specify a remote address to connect to"); - } + addressOfRemoteServer = new Uri(addressOfRemoteServer.ToString() + "/"); + } - if (!addressOfRemoteServer.AbsoluteUri.EndsWith("/", StringComparison.OrdinalIgnoreCase)) - { - addressOfRemoteServer = new Uri(addressOfRemoteServer.ToString() + "/"); - } + this.UserAgent = string.Format(CultureInfo.InvariantCulture, UserAgentHeaderTemplate, ResourceUtilities.ProductVersion, ResourceUtilities.PlatformFamily); + this.remoteServerUri = addressOfRemoteServer; + this.serverResponseTimeout = timeout; + this.IsKeepAliveEnabled = enableKeepAlive; + this.client = new Lazy(CreateHttpClient); + } - this.UserAgent = string.Format(CultureInfo.InvariantCulture, UserAgentHeaderTemplate, ResourceUtilities.ProductVersion, ResourceUtilities.PlatformFamily); - this.remoteServerUri = addressOfRemoteServer; - this.serverResponseTimeout = timeout; - this.IsKeepAliveEnabled = enableKeepAlive; - this.client = new Lazy(CreateHttpClient); - } + /// + /// Occurs when the is sending an HTTP + /// request to the remote end WebDriver implementation. + /// + public event EventHandler? SendingRemoteHttpRequest; - /// - /// Occurs when the is sending an HTTP - /// request to the remote end WebDriver implementation. - /// - public event EventHandler? SendingRemoteHttpRequest; + /// + /// Gets or sets an object to be used to proxy requests + /// between this and the remote end WebDriver + /// implementation. + /// + public IWebProxy? Proxy { get; set; } - /// - /// Gets or sets an object to be used to proxy requests - /// between this and the remote end WebDriver - /// implementation. - /// - public IWebProxy? Proxy { get; set; } + /// + /// Gets or sets a value indicating whether keep-alive is enabled for HTTP + /// communication between this and the + /// remote end WebDriver implementation. + /// + public bool IsKeepAliveEnabled { get; set; } - /// - /// Gets or sets a value indicating whether keep-alive is enabled for HTTP - /// communication between this and the - /// remote end WebDriver implementation. - /// - public bool IsKeepAliveEnabled { get; set; } + /// + /// Gets or sets the user agent string used for HTTP communication + /// between this and the remote end + /// WebDriver implementation + /// + public string UserAgent { get; set; } - /// - /// Gets or sets the user agent string used for HTTP communication - /// between this and the remote end - /// WebDriver implementation - /// - public string UserAgent { get; set; } + /// + /// Gets the repository of objects containing information about commands. + /// + /// If the value is set to . + protected CommandInfoRepository CommandInfoRepository + { + get => this.commandInfoRepository; + set => this.commandInfoRepository = value ?? throw new ArgumentNullException(nameof(value), "CommandInfoRepository cannot be null"); + } - /// - /// Gets the repository of objects containing information about commands. - /// - /// If the value is set to . - protected CommandInfoRepository CommandInfoRepository + /// + /// Attempts to add a command to the repository of commands known to this executor. + /// + /// The name of the command to attempt to add. + /// The describing the command to add. + /// if the new command has been added successfully; otherwise, . + public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info) + { + if (info is not HttpCommandInfo commandInfo) { - get => this.commandInfoRepository; - set => this.commandInfoRepository = value ?? throw new ArgumentNullException(nameof(value), "CommandInfoRepository cannot be null"); + return false; } - /// - /// Attempts to add a command to the repository of commands known to this executor. - /// - /// The name of the command to attempt to add. - /// The describing the command to add. - /// if the new command has been added successfully; otherwise, . - public bool TryAddCommand(string commandName, [NotNullWhen(true)] CommandInfo? info) + return this.commandInfoRepository.TryAddCommand(commandName, commandInfo); + } + + /// + /// Executes a command. + /// + /// The command you wish to execute. + /// A response from the browser. + /// If is . + public virtual Response Execute(Command commandToExecute) + { + return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult(); + } + + /// + /// Executes a command as an asynchronous task. + /// + /// The command you wish to execute. + /// A task object representing the asynchronous operation. + /// If is . + public virtual async Task ExecuteAsync(Command commandToExecute) + { + if (commandToExecute == null) { - if (info is not HttpCommandInfo commandInfo) - { - return false; - } + throw new ArgumentNullException(nameof(commandToExecute), "commandToExecute cannot be null"); + } - return this.commandInfoRepository.TryAddCommand(commandName, commandInfo); + if (_logger.IsEnabled(LogEventLevel.Debug)) + { + _logger.Debug($"Executing command: [{commandToExecute.SessionId}]: {commandToExecute.Name}"); } - /// - /// Executes a command. - /// - /// The command you wish to execute. - /// A response from the browser. - /// If is . - public virtual Response Execute(Command commandToExecute) + HttpCommandInfo? info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name); + if (info == null) { - return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult(); + throw new NotImplementedException(string.Format("The command you are attempting to execute, {0}, does not exist in the protocol dialect used by the remote end.", commandToExecute.Name)); } - /// - /// Executes a command as an asynchronous task. - /// - /// The command you wish to execute. - /// A task object representing the asynchronous operation. - /// If is . - public virtual async Task ExecuteAsync(Command commandToExecute) + HttpRequestInfo requestInfo = new HttpRequestInfo(this.remoteServerUri, commandToExecute, info); + HttpResponseInfo responseInfo; + try { - if (commandToExecute == null) - { - throw new ArgumentNullException(nameof(commandToExecute), "commandToExecute cannot be null"); - } + responseInfo = await this.MakeHttpRequest(requestInfo).ConfigureAwait(false); + } + catch (HttpRequestException ex) + { + string unknownErrorMessage = "An unknown exception was encountered sending an HTTP request to the remote WebDriver server for URL {0}. The exception message was: {1}"; + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, unknownErrorMessage, requestInfo.FullUri.AbsoluteUri, ex.Message), ex); + } + catch (TaskCanceledException ex) + { + string timeoutMessage = "The HTTP request to the remote WebDriver server for URL {0} timed out after {1} seconds."; + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, timeoutMessage, requestInfo.FullUri.AbsoluteUri, this.serverResponseTimeout.TotalSeconds), ex); + } - if (_logger.IsEnabled(LogEventLevel.Debug)) - { - _logger.Debug($"Executing command: [{commandToExecute.SessionId}]: {commandToExecute.Name}"); - } + Response toReturn = this.CreateResponse(responseInfo); - HttpCommandInfo? info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name); - if (info == null) - { - throw new NotImplementedException(string.Format("The command you are attempting to execute, {0}, does not exist in the protocol dialect used by the remote end.", commandToExecute.Name)); - } + if (_logger.IsEnabled(LogEventLevel.Debug)) + { + _logger.Debug($"Response: {toReturn}"); + } - HttpRequestInfo requestInfo = new HttpRequestInfo(this.remoteServerUri, commandToExecute, info); - HttpResponseInfo responseInfo; - try - { - responseInfo = await this.MakeHttpRequest(requestInfo).ConfigureAwait(false); - } - catch (HttpRequestException ex) - { - string unknownErrorMessage = "An unknown exception was encountered sending an HTTP request to the remote WebDriver server for URL {0}. The exception message was: {1}"; - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, unknownErrorMessage, requestInfo.FullUri.AbsoluteUri, ex.Message), ex); - } - catch (TaskCanceledException ex) - { - string timeoutMessage = "The HTTP request to the remote WebDriver server for URL {0} timed out after {1} seconds."; - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, timeoutMessage, requestInfo.FullUri.AbsoluteUri, this.serverResponseTimeout.TotalSeconds), ex); - } + return toReturn; + } - Response toReturn = this.CreateResponse(responseInfo); + /// + /// Raises the event. + /// + /// A that contains the event data. + protected virtual void OnSendingRemoteHttpRequest(SendingRemoteHttpRequestEventArgs eventArgs) + { + if (eventArgs == null) + { + throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); + } - if (_logger.IsEnabled(LogEventLevel.Debug)) - { - _logger.Debug($"Response: {toReturn}"); - } + this.SendingRemoteHttpRequest?.Invoke(this, eventArgs); + } - return toReturn; - } + /// + /// Creates an instance of as underlying handler, + /// used by . Invoked only once when required. + /// + /// An instance of . + protected virtual HttpClientHandler CreateHttpClientHandler() + { + HttpClientHandler httpClientHandler = new(); - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnSendingRemoteHttpRequest(SendingRemoteHttpRequestEventArgs eventArgs) - { - if (eventArgs == null) - { - throw new ArgumentNullException(nameof(eventArgs), "eventArgs must not be null"); - } + string userInfo = this.remoteServerUri.UserInfo; - this.SendingRemoteHttpRequest?.Invoke(this, eventArgs); + if (!string.IsNullOrEmpty(userInfo) && userInfo.Contains(':')) + { + string[] userInfoComponents = this.remoteServerUri.UserInfo.Split([':'], 2); + httpClientHandler.Credentials = new NetworkCredential(userInfoComponents[0], userInfoComponents[1]); + httpClientHandler.PreAuthenticate = true; } - /// - /// Creates an instance of as underlying handler, - /// used by . Invoked only once when required. - /// - /// An instance of . - protected virtual HttpClientHandler CreateHttpClientHandler() - { - HttpClientHandler httpClientHandler = new(); + httpClientHandler.Proxy = this.Proxy; - string userInfo = this.remoteServerUri.UserInfo; + return httpClientHandler; + } - if (!string.IsNullOrEmpty(userInfo) && userInfo.Contains(':')) - { - string[] userInfoComponents = this.remoteServerUri.UserInfo.Split([':'], 2); - httpClientHandler.Credentials = new NetworkCredential(userInfoComponents[0], userInfoComponents[1]); - httpClientHandler.PreAuthenticate = true; - } + /// + /// Creates an instance of used by making all HTTP calls to remote end. + /// Invoked only once when required. + /// + /// An instance of . + protected virtual HttpClient CreateHttpClient() + { + var httpClientHandler = CreateHttpClientHandler() + ?? throw new InvalidOperationException($"{nameof(CreateHttpClientHandler)} method returned null"); - httpClientHandler.Proxy = this.Proxy; + HttpMessageHandler handler = httpClientHandler; - return httpClientHandler; + if (_logger.IsEnabled(LogEventLevel.Trace)) + { + handler = new DiagnosticsHttpHandler(httpClientHandler, _logger); } - /// - /// Creates an instance of used by making all HTTP calls to remote end. - /// Invoked only once when required. - /// - /// An instance of . - protected virtual HttpClient CreateHttpClient() + var client = new HttpClient(handler); + + client.DefaultRequestHeaders.UserAgent.ParseAdd(this.UserAgent); + client.DefaultRequestHeaders.Accept.ParseAdd(RequestAcceptHeader); + client.DefaultRequestHeaders.ExpectContinue = false; + + if (!this.IsKeepAliveEnabled) { - var httpClientHandler = CreateHttpClientHandler() - ?? throw new InvalidOperationException($"{nameof(CreateHttpClientHandler)} method returned null"); + client.DefaultRequestHeaders.Connection.ParseAdd("close"); + } - HttpMessageHandler handler = httpClientHandler; + client.Timeout = this.serverResponseTimeout; - if (_logger.IsEnabled(LogEventLevel.Trace)) - { - handler = new DiagnosticsHttpHandler(httpClientHandler, _logger); - } + return client; + } - var client = new HttpClient(handler); + private async Task MakeHttpRequest(HttpRequestInfo requestInfo) + { + SendingRemoteHttpRequestEventArgs eventArgs = new SendingRemoteHttpRequestEventArgs(requestInfo.HttpMethod, requestInfo.FullUri.ToString(), requestInfo.RequestBody); + this.OnSendingRemoteHttpRequest(eventArgs); - client.DefaultRequestHeaders.UserAgent.ParseAdd(this.UserAgent); - client.DefaultRequestHeaders.Accept.ParseAdd(RequestAcceptHeader); - client.DefaultRequestHeaders.ExpectContinue = false; + HttpMethod method = new HttpMethod(requestInfo.HttpMethod); + using (HttpRequestMessage requestMessage = new HttpRequestMessage(method, requestInfo.FullUri)) + { + foreach (KeyValuePair header in eventArgs.Headers) + { + requestMessage.Headers.Add(header.Key, header.Value); + } - if (!this.IsKeepAliveEnabled) + if (requestInfo.HttpMethod == HttpCommandInfo.GetCommand) { - client.DefaultRequestHeaders.Connection.ParseAdd("close"); + CacheControlHeaderValue cacheControlHeader = new CacheControlHeaderValue(); + cacheControlHeader.NoCache = true; + requestMessage.Headers.CacheControl = cacheControlHeader; } - client.Timeout = this.serverResponseTimeout; + if (requestInfo.HttpMethod == HttpCommandInfo.PostCommand) + { + MediaTypeWithQualityHeaderValue acceptHeader = new MediaTypeWithQualityHeaderValue(JsonMimeType); + acceptHeader.CharSet = Utf8CharsetType; + requestMessage.Headers.Accept.Add(acceptHeader); - return client; - } + byte[] bytes = Encoding.UTF8.GetBytes(requestInfo.RequestBody); + requestMessage.Content = new ByteArrayContent(bytes, 0, bytes.Length); - private async Task MakeHttpRequest(HttpRequestInfo requestInfo) - { - SendingRemoteHttpRequestEventArgs eventArgs = new SendingRemoteHttpRequestEventArgs(requestInfo.HttpMethod, requestInfo.FullUri.ToString(), requestInfo.RequestBody); - this.OnSendingRemoteHttpRequest(eventArgs); + MediaTypeHeaderValue contentTypeHeader = new MediaTypeHeaderValue(JsonMimeType); + contentTypeHeader.CharSet = Utf8CharsetType; + requestMessage.Content.Headers.ContentType = contentTypeHeader; + } - HttpMethod method = new HttpMethod(requestInfo.HttpMethod); - using (HttpRequestMessage requestMessage = new HttpRequestMessage(method, requestInfo.FullUri)) + using (HttpResponseMessage responseMessage = await this.client.Value.SendAsync(requestMessage).ConfigureAwait(false)) { - foreach (KeyValuePair header in eventArgs.Headers) - { - requestMessage.Headers.Add(header.Key, header.Value); - } - - if (requestInfo.HttpMethod == HttpCommandInfo.GetCommand) - { - CacheControlHeaderValue cacheControlHeader = new CacheControlHeaderValue(); - cacheControlHeader.NoCache = true; - requestMessage.Headers.CacheControl = cacheControlHeader; - } - - if (requestInfo.HttpMethod == HttpCommandInfo.PostCommand) - { - MediaTypeWithQualityHeaderValue acceptHeader = new MediaTypeWithQualityHeaderValue(JsonMimeType); - acceptHeader.CharSet = Utf8CharsetType; - requestMessage.Headers.Accept.Add(acceptHeader); - - byte[] bytes = Encoding.UTF8.GetBytes(requestInfo.RequestBody); - requestMessage.Content = new ByteArrayContent(bytes, 0, bytes.Length); - - MediaTypeHeaderValue contentTypeHeader = new MediaTypeHeaderValue(JsonMimeType); - contentTypeHeader.CharSet = Utf8CharsetType; - requestMessage.Content.Headers.ContentType = contentTypeHeader; - } - - using (HttpResponseMessage responseMessage = await this.client.Value.SendAsync(requestMessage).ConfigureAwait(false)) - { - var responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); - var responseContentType = responseMessage.Content.Headers.ContentType?.ToString(); - var responseStatusCode = responseMessage.StatusCode; - - return new HttpResponseInfo(responseBody, responseContentType, responseStatusCode); - } + var responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); + var responseContentType = responseMessage.Content.Headers.ContentType?.ToString(); + var responseStatusCode = responseMessage.StatusCode; + + return new HttpResponseInfo(responseBody, responseContentType, responseStatusCode); } } + } - private Response CreateResponse(HttpResponseInfo responseInfo) + private Response CreateResponse(HttpResponseInfo responseInfo) + { + Response response; + string body = responseInfo.Body; + if ((int)responseInfo.StatusCode < 200 || (int)responseInfo.StatusCode > 299) { - Response response; - string body = responseInfo.Body; - if ((int)responseInfo.StatusCode < 200 || (int)responseInfo.StatusCode > 299) + if (responseInfo.ContentType != null && responseInfo.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase)) { - if (responseInfo.ContentType != null && responseInfo.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase)) - { - response = Response.FromErrorJson(body); - } - else - { - response = new Response(sessionId: null, body, WebDriverResult.UnknownError); - } - } - else if (responseInfo.ContentType != null && responseInfo.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase)) - { - response = Response.FromJson(body); + response = Response.FromErrorJson(body); } else { - response = new Response(sessionId: null, body, WebDriverResult.Success); + response = new Response(sessionId: null, body, WebDriverResult.UnknownError); } - - if (response.Value is string valueString) - { - valueString = valueString.Replace("\r\n", "\n").Replace("\n", Environment.NewLine); - response = new Response(response.SessionId, valueString, response.Status); - } - - return response; + } + else if (responseInfo.ContentType != null && responseInfo.ContentType.StartsWith(JsonMimeType, StringComparison.OrdinalIgnoreCase)) + { + response = Response.FromJson(body); + } + else + { + response = new Response(sessionId: null, body, WebDriverResult.Success); } - /// - /// Releases all resources used by the . - /// - public void Dispose() + if (response.Value is string valueString) { - this.Dispose(true); - GC.SuppressFinalize(this); + valueString = valueString.Replace("\r\n", "\n").Replace("\n", Environment.NewLine); + response = new Response(response.SessionId, valueString, response.Status); } - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// to release managed and resources; - /// to only release unmanaged resources. - protected virtual void Dispose(bool disposing) + return response; + } + + /// + /// Releases all resources used by the . + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the and + /// optionally releases the managed resources. + /// + /// to release managed and resources; + /// to only release unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) { - if (!this.isDisposed) + if (this.client.IsValueCreated) { - if (this.client.IsValueCreated) - { - this.client.Value.Dispose(); - } - - this.isDisposed = true; + this.client.Value.Dispose(); } + + this.isDisposed = true; } + } - private class HttpRequestInfo + private class HttpRequestInfo + { + public HttpRequestInfo(Uri serverUri, Command commandToExecute, HttpCommandInfo commandInfo) { - public HttpRequestInfo(Uri serverUri, Command commandToExecute, HttpCommandInfo commandInfo) + if (commandInfo is null) { - if (commandInfo is null) - { - throw new ArgumentNullException(nameof(commandInfo)); - } - - this.FullUri = commandInfo.CreateCommandUri(serverUri, commandToExecute); - this.HttpMethod = commandInfo.Method; - this.RequestBody = commandToExecute.ParametersAsJsonString; + throw new ArgumentNullException(nameof(commandInfo)); } - public Uri FullUri { get; set; } - public string HttpMethod { get; set; } - public string RequestBody { get; set; } + this.FullUri = commandInfo.CreateCommandUri(serverUri, commandToExecute); + this.HttpMethod = commandInfo.Method; + this.RequestBody = commandToExecute.ParametersAsJsonString; } - private class HttpResponseInfo + public Uri FullUri { get; set; } + public string HttpMethod { get; set; } + public string RequestBody { get; set; } + } + + private class HttpResponseInfo + { + public HttpResponseInfo(string body, string? contentType, HttpStatusCode statusCode) + { + this.Body = body ?? throw new ArgumentNullException(nameof(body)); + this.ContentType = contentType; + this.StatusCode = statusCode; + } + + public HttpStatusCode StatusCode { get; set; } + public string Body { get; set; } + public string? ContentType { get; set; } + } + + /// + /// Internal diagnostic handler to log http requests/responses. + /// + private class DiagnosticsHttpHandler : DelegatingHandler + { + private readonly ILogger _logger; + + public DiagnosticsHttpHandler(HttpMessageHandler messageHandler, ILogger logger) + : base(messageHandler) { - public HttpResponseInfo(string body, string? contentType, HttpStatusCode statusCode) + if (messageHandler is null) { - this.Body = body ?? throw new ArgumentNullException(nameof(body)); - this.ContentType = contentType; - this.StatusCode = statusCode; + throw new ArgumentNullException(nameof(messageHandler)); } - public HttpStatusCode StatusCode { get; set; } - public string Body { get; set; } - public string? ContentType { get; set; } + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// - /// Internal diagnostic handler to log http requests/responses. + /// Sends the specified request and returns the associated response. /// - private class DiagnosticsHttpHandler : DelegatingHandler + /// The request to be sent. + /// A CancellationToken object to allow for cancellation of the request. + /// The http response message content. + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - private readonly ILogger _logger; - - public DiagnosticsHttpHandler(HttpMessageHandler messageHandler, ILogger logger) - : base(messageHandler) - { - if (messageHandler is null) - { - throw new ArgumentNullException(nameof(messageHandler)); - } + var responseTask = base.SendAsync(request, cancellationToken); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + StringBuilder requestLogMessageBuilder = new(); + requestLogMessageBuilder.AppendFormat(">> {0} RequestUri: {1}, Content: {2}, Headers: {3}", + request.Method, + request.RequestUri?.ToString() ?? "null", + request.Content?.ToString() ?? "null", + request.Headers?.Count()); - /// - /// Sends the specified request and returns the associated response. - /// - /// The request to be sent. - /// A CancellationToken object to allow for cancellation of the request. - /// The http response message content. - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + if (request.Content != null) { - var responseTask = base.SendAsync(request, cancellationToken); - - StringBuilder requestLogMessageBuilder = new(); - requestLogMessageBuilder.AppendFormat(">> {0} RequestUri: {1}, Content: {2}, Headers: {3}", - request.Method, - request.RequestUri?.ToString() ?? "null", - request.Content?.ToString() ?? "null", - request.Headers?.Count()); - - if (request.Content != null) - { - var requestContent = await request.Content.ReadAsStringAsync().ConfigureAwait(false); - requestLogMessageBuilder.AppendFormat("{0}{1}", Environment.NewLine, requestContent); - } + var requestContent = await request.Content.ReadAsStringAsync().ConfigureAwait(false); + requestLogMessageBuilder.AppendFormat("{0}{1}", Environment.NewLine, requestContent); + } - _logger.Trace(requestLogMessageBuilder.ToString()); + _logger.Trace(requestLogMessageBuilder.ToString()); - var response = await responseTask.ConfigureAwait(false); + var response = await responseTask.ConfigureAwait(false); - StringBuilder responseLogMessageBuilder = new(); - responseLogMessageBuilder.AppendFormat("<< StatusCode: {0}, ReasonPhrase: {1}, Content: {2}, Headers: {3}", (int)response.StatusCode, response.ReasonPhrase, response.Content, response.Headers?.Count()); + StringBuilder responseLogMessageBuilder = new(); + responseLogMessageBuilder.AppendFormat("<< StatusCode: {0}, ReasonPhrase: {1}, Content: {2}, Headers: {3}", (int)response.StatusCode, response.ReasonPhrase, response.Content, response.Headers?.Count()); - if (!response.IsSuccessStatusCode && response.Content != null) - { - var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - responseLogMessageBuilder.AppendFormat("{0}{1}", Environment.NewLine, responseContent); - } + if (!response.IsSuccessStatusCode && response.Content != null) + { + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + responseLogMessageBuilder.AppendFormat("{0}{1}", Environment.NewLine, responseContent); + } - _logger.Trace(responseLogMessageBuilder.ToString()); + _logger.Trace(responseLogMessageBuilder.ToString()); - return response; - } + return response; } } } diff --git a/dotnet/src/webdriver/Remote/ICommandServer.cs b/dotnet/src/webdriver/Remote/ICommandServer.cs index 395616482f7d5..b76731de57a73 100644 --- a/dotnet/src/webdriver/Remote/ICommandServer.cs +++ b/dotnet/src/webdriver/Remote/ICommandServer.cs @@ -19,16 +19,15 @@ using System; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Provides a way to start a server that understands remote commands +/// +public interface ICommandServer : IDisposable { /// - /// Provides a way to start a server that understands remote commands + /// Starts the server. /// - public interface ICommandServer : IDisposable - { - /// - /// Starts the server. - /// - void Start(); - } + void Start(); } diff --git a/dotnet/src/webdriver/Remote/LocalFileDetector.cs b/dotnet/src/webdriver/Remote/LocalFileDetector.cs index 06b8510d676e6..58decf7851727 100644 --- a/dotnet/src/webdriver/Remote/LocalFileDetector.cs +++ b/dotnet/src/webdriver/Remote/LocalFileDetector.cs @@ -20,23 +20,22 @@ using System.Diagnostics.CodeAnalysis; using System.IO; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Represents a file detector for determining whether a file +/// must be uploaded to a remote server. +/// +public class LocalFileDetector : IFileDetector { /// - /// Represents a file detector for determining whether a file - /// must be uploaded to a remote server. + /// Returns a value indicating whether a specified key sequence represents + /// a file name and path. /// - public class LocalFileDetector : IFileDetector + /// The sequence to test for file existence. + /// if the key sequence represents a file; otherwise, . + public bool IsFile([NotNullWhen(true)] string? keySequence) { - /// - /// Returns a value indicating whether a specified key sequence represents - /// a file name and path. - /// - /// The sequence to test for file existence. - /// if the key sequence represents a file; otherwise, . - public bool IsFile([NotNullWhen(true)] string? keySequence) - { - return File.Exists(keySequence); - } + return File.Exists(keySequence); } } diff --git a/dotnet/src/webdriver/Remote/ReadOnlyDesiredCapabilities.cs b/dotnet/src/webdriver/Remote/ReadOnlyDesiredCapabilities.cs index 183b7efc193ac..40db2fb515b83 100644 --- a/dotnet/src/webdriver/Remote/ReadOnlyDesiredCapabilities.cs +++ b/dotnet/src/webdriver/Remote/ReadOnlyDesiredCapabilities.cs @@ -23,209 +23,208 @@ using System.Collections.ObjectModel; using System.Globalization; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Class to Create the capabilities of the browser you require for . +/// If you wish to use default values use the static methods +/// +public class ReadOnlyDesiredCapabilities : ICapabilities, IHasCapabilitiesDictionary { + private readonly Dictionary capabilities = new Dictionary(); + /// - /// Class to Create the capabilities of the browser you require for . - /// If you wish to use default values use the static methods + /// Prevents a default instance of the class. /// - public class ReadOnlyDesiredCapabilities : ICapabilities, IHasCapabilitiesDictionary + private ReadOnlyDesiredCapabilities() { - private readonly Dictionary capabilities = new Dictionary(); + } - /// - /// Prevents a default instance of the class. - /// - private ReadOnlyDesiredCapabilities() + internal ReadOnlyDesiredCapabilities(DesiredCapabilities desiredCapabilities) + { + IDictionary internalDictionary = desiredCapabilities.CapabilitiesDictionary; + foreach (KeyValuePair keyValuePair in internalDictionary) { + this.capabilities[keyValuePair.Key] = keyValuePair.Value; } + } - internal ReadOnlyDesiredCapabilities(DesiredCapabilities desiredCapabilities) + /// + /// Gets the browser name + /// + public string BrowserName + { + get { - IDictionary internalDictionary = desiredCapabilities.CapabilitiesDictionary; - foreach (KeyValuePair keyValuePair in internalDictionary) - { - this.capabilities[keyValuePair.Key] = keyValuePair.Value; - } + return this.GetCapability(CapabilityType.BrowserName)?.ToString() ?? string.Empty; } + } - /// - /// Gets the browser name - /// - public string BrowserName + /// + /// Gets or sets the platform + /// + public Platform Platform + { + get { - get - { - return this.GetCapability(CapabilityType.BrowserName)?.ToString() ?? string.Empty; - } + return this.GetCapability(CapabilityType.Platform) as Platform ?? new Platform(PlatformType.Any); } + } - /// - /// Gets or sets the platform - /// - public Platform Platform + /// + /// Gets the browser version + /// + public string Version + { + get { - get - { - return this.GetCapability(CapabilityType.Platform) as Platform ?? new Platform(PlatformType.Any); - } + return this.GetCapability(CapabilityType.Version)?.ToString() ?? string.Empty; } + } - /// - /// Gets the browser version - /// - public string Version + /// + /// Gets or sets a value indicating whether the browser accepts SSL certificates. + /// + public bool AcceptInsecureCerts + { + get { - get + bool acceptSSLCerts = false; + object? capabilityValue = this.GetCapability(CapabilityType.AcceptInsecureCertificates); + if (capabilityValue != null) { - return this.GetCapability(CapabilityType.Version)?.ToString() ?? string.Empty; + acceptSSLCerts = (bool)capabilityValue; } + + return acceptSSLCerts; } + } + + /// + /// Gets the underlying Dictionary for a given set of capabilities. + /// + IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; + + /// + /// Gets the underlying Dictionary for a given set of capabilities. + /// + internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); - /// - /// Gets or sets a value indicating whether the browser accepts SSL certificates. - /// - public bool AcceptInsecureCerts + /// + /// Gets the capability value with the specified name. + /// + /// The name of the capability to get. + /// The value of the capability. + /// + /// The specified capability name is not in the set of capabilities. + /// + public object this[string capabilityName] + { + get { - get + if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) { - bool acceptSSLCerts = false; - object? capabilityValue = this.GetCapability(CapabilityType.AcceptInsecureCertificates); - if (capabilityValue != null) - { - acceptSSLCerts = (bool)capabilityValue; - } - - return acceptSSLCerts; + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); } + + return capabilityValue; } + } - /// - /// Gets the underlying Dictionary for a given set of capabilities. - /// - IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; - - /// - /// Gets the underlying Dictionary for a given set of capabilities. - /// - internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); - - /// - /// Gets the capability value with the specified name. - /// - /// The name of the capability to get. - /// The value of the capability. - /// - /// The specified capability name is not in the set of capabilities. - /// - public object this[string capabilityName] + /// + /// Gets a value indicating whether the browser has a given capability. + /// + /// The capability to get. + /// Returns if the browser has the capability; otherwise, . + public bool HasCapability(string capability) + { + return this.capabilities.ContainsKey(capability); + } + + /// + /// Gets a capability of the browser. + /// + /// The capability to get. + /// An object associated with the capability, or + /// if the capability is not set on the browser. + public object? GetCapability(string capability) + { + if (this.capabilities.TryGetValue(capability, out object? capabilityValue)) { - get + if (capability == CapabilityType.Platform && capabilityValue is string capabilityValueString) { - if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); - } - - return capabilityValue; + capabilityValue = Platform.FromString(capabilityValueString); } - } - /// - /// Gets a value indicating whether the browser has a given capability. - /// - /// The capability to get. - /// Returns if the browser has the capability; otherwise, . - public bool HasCapability(string capability) - { - return this.capabilities.ContainsKey(capability); + return capabilityValue; } - /// - /// Gets a capability of the browser. - /// - /// The capability to get. - /// An object associated with the capability, or - /// if the capability is not set on the browser. - public object? GetCapability(string capability) - { - if (this.capabilities.TryGetValue(capability, out object? capabilityValue)) - { - if (capability == CapabilityType.Platform && capabilityValue is string capabilityValueString) - { - capabilityValue = Platform.FromString(capabilityValueString); - } + return null; + } - return capabilityValue; - } + /// + /// Converts the object to a read-only . + /// + /// A read-only containing the capabilities. + public IDictionary ToDictionary() + { + return new ReadOnlyDictionary(this.capabilities); + } - return null; - } + /// + /// Return HashCode for the DesiredCapabilities that has been created + /// + /// Integer of HashCode generated + public override int GetHashCode() + { + int result; + result = this.BrowserName != null ? this.BrowserName.GetHashCode() : 0; + result = (31 * result) + (this.Version != null ? this.Version.GetHashCode() : 0); + result = (31 * result) + (this.Platform != null ? this.Platform.GetHashCode() : 0); + return result; + } + + /// + /// Return a string of capabilities being used + /// + /// String of capabilities being used + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Capabilities [BrowserName={0}, Platform={1}, Version={2}]", this.BrowserName, this.Platform.PlatformType.ToString(), this.Version); + } - /// - /// Converts the object to a read-only . - /// - /// A read-only containing the capabilities. - public IDictionary ToDictionary() + /// + /// Compare two DesiredCapabilities and will return either true or false + /// + /// DesiredCapabilities you wish to compare + /// true if they are the same or false if they are not + public override bool Equals(object? obj) + { + if (this == obj) { - return new ReadOnlyDictionary(this.capabilities); + return true; } - /// - /// Return HashCode for the DesiredCapabilities that has been created - /// - /// Integer of HashCode generated - public override int GetHashCode() + if (obj is not DesiredCapabilities other) { - int result; - result = this.BrowserName != null ? this.BrowserName.GetHashCode() : 0; - result = (31 * result) + (this.Version != null ? this.Version.GetHashCode() : 0); - result = (31 * result) + (this.Platform != null ? this.Platform.GetHashCode() : 0); - return result; + return false; } - /// - /// Return a string of capabilities being used - /// - /// String of capabilities being used - public override string ToString() + if (this.BrowserName != other.BrowserName) { - return string.Format(CultureInfo.InvariantCulture, "Capabilities [BrowserName={0}, Platform={1}, Version={2}]", this.BrowserName, this.Platform.PlatformType.ToString(), this.Version); + return false; } - /// - /// Compare two DesiredCapabilities and will return either true or false - /// - /// DesiredCapabilities you wish to compare - /// true if they are the same or false if they are not - public override bool Equals(object? obj) + if (!this.Platform.IsPlatformType(other.Platform.PlatformType)) { - if (this == obj) - { - return true; - } - - if (obj is not DesiredCapabilities other) - { - return false; - } - - if (this.BrowserName != other.BrowserName) - { - return false; - } - - if (!this.Platform.IsPlatformType(other.Platform.PlatformType)) - { - return false; - } - - if (this.Version != other.Version) - { - return false; - } + return false; + } - return true; + if (this.Version != other.Version) + { + return false; } + + return true; } } diff --git a/dotnet/src/webdriver/Remote/RemoteSessionSettings.cs b/dotnet/src/webdriver/Remote/RemoteSessionSettings.cs index 57c3b87bbbd8b..ddb5f84c0373f 100644 --- a/dotnet/src/webdriver/Remote/RemoteSessionSettings.cs +++ b/dotnet/src/webdriver/Remote/RemoteSessionSettings.cs @@ -22,260 +22,259 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Base class for managing options specific to a browser driver. +/// +public class RemoteSessionSettings : ICapabilities { + private const string FirstMatchCapabilityName = "firstMatch"; + private const string AlwaysMatchCapabilityName = "alwaysMatch"; + + private readonly HashSet reservedSettingNames = new HashSet() { FirstMatchCapabilityName, AlwaysMatchCapabilityName }; + private DriverOptions? mustMatchDriverOptions; + private readonly List firstMatchOptions = new List(); + private readonly Dictionary remoteMetadataSettings = new Dictionary(); + + /// + /// Creates a new instance of the class. + /// + public RemoteSessionSettings() + { + } + /// - /// Base class for managing options specific to a browser driver. + /// Creates a new instance of the class, + /// containing the specified to use in the remote + /// session. /// - public class RemoteSessionSettings : ICapabilities + /// + /// A object that contains values that must be matched + /// by the remote end to create the remote session. + /// + /// + /// A list of objects that contain values that may be matched + /// by the remote end to create the remote session. + /// + public RemoteSessionSettings(DriverOptions mustMatchDriverOptions, params DriverOptions[] firstMatchDriverOptions) { - private const string FirstMatchCapabilityName = "firstMatch"; - private const string AlwaysMatchCapabilityName = "alwaysMatch"; - - private readonly HashSet reservedSettingNames = new HashSet() { FirstMatchCapabilityName, AlwaysMatchCapabilityName }; - private DriverOptions? mustMatchDriverOptions; - private readonly List firstMatchOptions = new List(); - private readonly Dictionary remoteMetadataSettings = new Dictionary(); - - /// - /// Creates a new instance of the class. - /// - public RemoteSessionSettings() + this.mustMatchDriverOptions = mustMatchDriverOptions; + foreach (DriverOptions firstMatchOption in firstMatchDriverOptions) { + this.AddFirstMatchDriverOption(firstMatchOption); } + } + + /// + /// Gets a value indicating the options that must be matched by the remote end to create a session. + /// + internal DriverOptions? MustMatchDriverOptions => this.mustMatchDriverOptions; + + /// + /// Gets a value indicating the number of options that may be matched by the remote end to create a session. + /// + internal int FirstMatchOptionsCount => this.firstMatchOptions.Count; - /// - /// Creates a new instance of the class, - /// containing the specified to use in the remote - /// session. - /// - /// - /// A object that contains values that must be matched - /// by the remote end to create the remote session. - /// - /// - /// A list of objects that contain values that may be matched - /// by the remote end to create the remote session. - /// - public RemoteSessionSettings(DriverOptions mustMatchDriverOptions, params DriverOptions[] firstMatchDriverOptions) + /// + /// Gets the capability value with the specified name. + /// + /// The name of the capability to get. + /// The value of the capability. + /// + /// The specified capability name is not in the set of capabilities. + /// + /// If is null. + public object this[string capabilityName] + { + get { - this.mustMatchDriverOptions = mustMatchDriverOptions; - foreach (DriverOptions firstMatchOption in firstMatchDriverOptions) + if (capabilityName == AlwaysMatchCapabilityName) { - this.AddFirstMatchDriverOption(firstMatchOption); + return this.GetAlwaysMatchOptionsAsSerializableDictionary() + ?? throw new ArgumentException("The \"alwaysMatch\" value has not been set", nameof(capabilityName)); } - } - /// - /// Gets a value indicating the options that must be matched by the remote end to create a session. - /// - internal DriverOptions? MustMatchDriverOptions => this.mustMatchDriverOptions; - - /// - /// Gets a value indicating the number of options that may be matched by the remote end to create a session. - /// - internal int FirstMatchOptionsCount => this.firstMatchOptions.Count; - - /// - /// Gets the capability value with the specified name. - /// - /// The name of the capability to get. - /// The value of the capability. - /// - /// The specified capability name is not in the set of capabilities. - /// - /// If is null. - public object this[string capabilityName] - { - get + if (capabilityName == FirstMatchCapabilityName) { - if (capabilityName == AlwaysMatchCapabilityName) - { - return this.GetAlwaysMatchOptionsAsSerializableDictionary() - ?? throw new ArgumentException("The \"alwaysMatch\" value has not been set", nameof(capabilityName)); - } + return this.GetFirstMatchOptionsAsSerializableList(); + } - if (capabilityName == FirstMatchCapabilityName) - { - return this.GetFirstMatchOptionsAsSerializableList(); - } + if (!this.remoteMetadataSettings.ContainsKey(capabilityName)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); + } - if (!this.remoteMetadataSettings.ContainsKey(capabilityName)) - { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); - } + return this.remoteMetadataSettings[capabilityName]; + } - return this.remoteMetadataSettings[capabilityName]; - } + } + /// + /// Add a metadata setting to this set of remote session settings. + /// + /// The name of the setting to set. + /// The value of the setting. + /// + /// If the setting name is null or empty. + /// -or- + /// If one of the reserved names of metadata settings. + /// + public void AddMetadataSetting(string settingName, object settingValue) + { + if (string.IsNullOrEmpty(settingName)) + { + throw new ArgumentException("Metadata setting name cannot be null or empty", nameof(settingName)); } - /// - /// Add a metadata setting to this set of remote session settings. - /// - /// The name of the setting to set. - /// The value of the setting. - /// - /// If the setting name is null or empty. - /// -or- - /// If one of the reserved names of metadata settings. - /// - public void AddMetadataSetting(string settingName, object settingValue) + if (this.reservedSettingNames.Contains(settingName)) { - if (string.IsNullOrEmpty(settingName)) - { - throw new ArgumentException("Metadata setting name cannot be null or empty", nameof(settingName)); - } + throw new ArgumentException(string.Format("'{0}' is a reserved name for a metadata setting, and cannot be used as a name.", settingName), nameof(settingName)); + } + + this.remoteMetadataSettings[settingName] = settingValue; + } - if (this.reservedSettingNames.Contains(settingName)) + /// + /// Adds a object to the list of options containing values to be + /// "first matched" by the remote end. + /// + /// The to add to the list of "first matched" options. + public void AddFirstMatchDriverOption(DriverOptions options) + { + if (this.mustMatchDriverOptions != null) + { + DriverOptionsMergeResult mergeResult = this.mustMatchDriverOptions.GetMergeResult(options); + if (mergeResult.IsMergeConflict) { - throw new ArgumentException(string.Format("'{0}' is a reserved name for a metadata setting, and cannot be used as a name.", settingName), nameof(settingName)); + string msg = string.Format(CultureInfo.InvariantCulture, "You cannot request the same capability in both must-match and first-match capabilities. You are attempting to add a first-match driver options object that defines a capability, '{0}', that is already defined in the must-match driver options.", mergeResult.MergeConflictOptionName); + throw new ArgumentException(msg, nameof(options)); } - - this.remoteMetadataSettings[settingName] = settingValue; } - /// - /// Adds a object to the list of options containing values to be - /// "first matched" by the remote end. - /// - /// The to add to the list of "first matched" options. - public void AddFirstMatchDriverOption(DriverOptions options) + firstMatchOptions.Add(options); + } + + /// + /// Adds a object containing values that must be matched + /// by the remote end to successfully create a session. + /// + /// The that must be matched by + /// the remote end to successfully create a session. + public void SetMustMatchDriverOptions(DriverOptions options) + { + if (this.firstMatchOptions.Count > 0) { - if (this.mustMatchDriverOptions != null) + int driverOptionIndex = 0; + foreach (DriverOptions firstMatchOption in this.firstMatchOptions) { - DriverOptionsMergeResult mergeResult = this.mustMatchDriverOptions.GetMergeResult(options); + DriverOptionsMergeResult mergeResult = firstMatchOption.GetMergeResult(options); if (mergeResult.IsMergeConflict) { - string msg = string.Format(CultureInfo.InvariantCulture, "You cannot request the same capability in both must-match and first-match capabilities. You are attempting to add a first-match driver options object that defines a capability, '{0}', that is already defined in the must-match driver options.", mergeResult.MergeConflictOptionName); + string msg = string.Format(CultureInfo.InvariantCulture, "You cannot request the same capability in both must-match and first-match capabilities. You are attempting to add a must-match driver options object that defines a capability, '{0}', that is already defined in the first-match driver options with index {1}.", mergeResult.MergeConflictOptionName, driverOptionIndex); throw new ArgumentException(msg, nameof(options)); } - } - firstMatchOptions.Add(options); + driverOptionIndex++; + } } - /// - /// Adds a object containing values that must be matched - /// by the remote end to successfully create a session. - /// - /// The that must be matched by - /// the remote end to successfully create a session. - public void SetMustMatchDriverOptions(DriverOptions options) - { - if (this.firstMatchOptions.Count > 0) - { - int driverOptionIndex = 0; - foreach (DriverOptions firstMatchOption in this.firstMatchOptions) - { - DriverOptionsMergeResult mergeResult = firstMatchOption.GetMergeResult(options); - if (mergeResult.IsMergeConflict) - { - string msg = string.Format(CultureInfo.InvariantCulture, "You cannot request the same capability in both must-match and first-match capabilities. You are attempting to add a must-match driver options object that defines a capability, '{0}', that is already defined in the first-match driver options with index {1}.", mergeResult.MergeConflictOptionName, driverOptionIndex); - throw new ArgumentException(msg, nameof(options)); - } - - driverOptionIndex++; - } - } + this.mustMatchDriverOptions = options; + } - this.mustMatchDriverOptions = options; + /// + /// Gets a value indicating whether the browser has a given capability. + /// + /// The capability to get. + /// Returns if this set of capabilities has the capability; + /// otherwise, . + public bool HasCapability(string capability) + { + if (capability == AlwaysMatchCapabilityName || capability == FirstMatchCapabilityName) + { + return true; } - /// - /// Gets a value indicating whether the browser has a given capability. - /// - /// The capability to get. - /// Returns if this set of capabilities has the capability; - /// otherwise, . - public bool HasCapability(string capability) - { - if (capability == AlwaysMatchCapabilityName || capability == FirstMatchCapabilityName) - { - return true; - } + return this.remoteMetadataSettings.ContainsKey(capability); + } - return this.remoteMetadataSettings.ContainsKey(capability); + /// + /// Gets a capability of the browser. + /// + /// The capability to get. + /// An object associated with the capability, or + /// if the capability is not set in this set of capabilities. + public object? GetCapability(string capability) + { + if (capability == AlwaysMatchCapabilityName) + { + return this.GetAlwaysMatchOptionsAsSerializableDictionary(); } - /// - /// Gets a capability of the browser. - /// - /// The capability to get. - /// An object associated with the capability, or - /// if the capability is not set in this set of capabilities. - public object? GetCapability(string capability) + if (capability == FirstMatchCapabilityName) { - if (capability == AlwaysMatchCapabilityName) - { - return this.GetAlwaysMatchOptionsAsSerializableDictionary(); - } - - if (capability == FirstMatchCapabilityName) - { - return this.GetFirstMatchOptionsAsSerializableList(); - } - - if (this.remoteMetadataSettings.ContainsKey(capability)) - { - return this.remoteMetadataSettings[capability]; - } - - return null; + return this.GetFirstMatchOptionsAsSerializableList(); } - /// - /// Return a dictionary representation of this . - /// - /// A representation of this . - public Dictionary ToDictionary() + if (this.remoteMetadataSettings.ContainsKey(capability)) { - Dictionary capabilitiesDictionary = new Dictionary(); - - foreach (KeyValuePair remoteMetadataSetting in this.remoteMetadataSettings) - { - capabilitiesDictionary[remoteMetadataSetting.Key] = remoteMetadataSetting.Value; - } + return this.remoteMetadataSettings[capability]; + } - if (this.mustMatchDriverOptions != null) - { - capabilitiesDictionary["alwaysMatch"] = GetAlwaysMatchOptionsAsSerializableDictionary(); - } + return null; + } - if (this.firstMatchOptions.Count > 0) - { - List optionsMatches = GetFirstMatchOptionsAsSerializableList(); + /// + /// Return a dictionary representation of this . + /// + /// A representation of this . + public Dictionary ToDictionary() + { + Dictionary capabilitiesDictionary = new Dictionary(); - capabilitiesDictionary["firstMatch"] = optionsMatches; - } + foreach (KeyValuePair remoteMetadataSetting in this.remoteMetadataSettings) + { + capabilitiesDictionary[remoteMetadataSetting.Key] = remoteMetadataSetting.Value; + } - return capabilitiesDictionary; + if (this.mustMatchDriverOptions != null) + { + capabilitiesDictionary["alwaysMatch"] = GetAlwaysMatchOptionsAsSerializableDictionary(); } - internal DriverOptions GetFirstMatchDriverOptions(int firstMatchIndex) + if (this.firstMatchOptions.Count > 0) { - if (firstMatchIndex < 0 || firstMatchIndex >= this.firstMatchOptions.Count) - { - throw new ArgumentException("Requested index must be greater than zero and less than the count of firstMatch options added."); - } + List optionsMatches = GetFirstMatchOptionsAsSerializableList(); - return this.firstMatchOptions[firstMatchIndex]; + capabilitiesDictionary["firstMatch"] = optionsMatches; } - private IDictionary? GetAlwaysMatchOptionsAsSerializableDictionary() + return capabilitiesDictionary; + } + + internal DriverOptions GetFirstMatchDriverOptions(int firstMatchIndex) + { + if (firstMatchIndex < 0 || firstMatchIndex >= this.firstMatchOptions.Count) { - return this.mustMatchDriverOptions?.ToDictionary(); + throw new ArgumentException("Requested index must be greater than zero and less than the count of firstMatch options added."); } - private List GetFirstMatchOptionsAsSerializableList() - { - List optionsMatches = new List(this.firstMatchOptions.Count); - foreach (DriverOptions options in this.firstMatchOptions) - { - optionsMatches.Add(options.ToDictionary()); - } + return this.firstMatchOptions[firstMatchIndex]; + } + + private IDictionary? GetAlwaysMatchOptionsAsSerializableDictionary() + { + return this.mustMatchDriverOptions?.ToDictionary(); + } - return optionsMatches; + private List GetFirstMatchOptionsAsSerializableList() + { + List optionsMatches = new List(this.firstMatchOptions.Count); + foreach (DriverOptions options in this.firstMatchOptions) + { + optionsMatches.Add(options.ToDictionary()); } + + return optionsMatches; } } diff --git a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs index 3905f4ca87a54..e7cdf4216ca25 100644 --- a/dotnet/src/webdriver/Remote/RemoteWebDriver.cs +++ b/dotnet/src/webdriver/Remote/RemoteWebDriver.cs @@ -28,607 +28,606 @@ using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Provides a way to use the driver through +/// +/// /// +/// +/// [TestFixture] +/// public class Testing +/// { +/// private IWebDriver driver; +/// +/// [SetUp] +/// public void SetUp() +/// { +/// driver = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"),new FirefoxOptions()); +/// } +/// +/// [Test] +/// public void TestGoogle() +/// { +/// driver.Navigate().GoToUrl("http://www.google.co.uk/"); +/// /* +/// * Rest of the test +/// */ +/// } +/// +/// [TearDown] +/// public void TearDown() +/// { +/// driver.Quit(); +/// } +/// } +/// +/// +public class RemoteWebDriver : WebDriver, IDevTools, IHasDownloads { + private static readonly ILogger _logger = OpenQA.Selenium.Internal.Logging.Log.GetLogger(typeof(RemoteWebDriver)); + /// - /// Provides a way to use the driver through + /// The name of the Selenium grid remote DevTools end point capability. /// - /// /// - /// - /// [TestFixture] - /// public class Testing - /// { - /// private IWebDriver driver; - /// - /// [SetUp] - /// public void SetUp() - /// { - /// driver = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"),new FirefoxOptions()); - /// } - /// - /// [Test] - /// public void TestGoogle() - /// { - /// driver.Navigate().GoToUrl("http://www.google.co.uk/"); - /// /* - /// * Rest of the test - /// */ - /// } - /// - /// [TearDown] - /// public void TearDown() - /// { - /// driver.Quit(); - /// } - /// } - /// - /// - public class RemoteWebDriver : WebDriver, IDevTools, IHasDownloads + public readonly string RemoteDevToolsEndPointCapabilityName = "se:cdp"; + + /// + /// The name of the Selenium remote DevTools version capability. + /// + public readonly string RemoteDevToolsVersionCapabilityName = "se:cdpVersion"; + + private const string DefaultRemoteServerUrl = "http://127.0.0.1:4444/wd/hub"; + + private DevToolsSession? devToolsSession; + + /// + /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub + /// + /// An object containing the desired capabilities of the browser. + public RemoteWebDriver(DriverOptions options) + : this(ConvertOptionsToCapabilities(options)) { - private static readonly ILogger _logger = OpenQA.Selenium.Internal.Logging.Log.GetLogger(typeof(RemoteWebDriver)); + } - /// - /// The name of the Selenium grid remote DevTools end point capability. - /// - public readonly string RemoteDevToolsEndPointCapabilityName = "se:cdp"; + /// + /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub + /// + /// An object containing the desired capabilities of the browser. + public RemoteWebDriver(ICapabilities capabilities) + : this(new Uri(DefaultRemoteServerUrl), capabilities) + { + } - /// - /// The name of the Selenium remote DevTools version capability. - /// - public readonly string RemoteDevToolsVersionCapabilityName = "se:cdpVersion"; + /// + /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub + /// + /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). + /// An object containing the desired capabilities of the browser. + public RemoteWebDriver(Uri remoteAddress, DriverOptions options) + : this(remoteAddress, ConvertOptionsToCapabilities(options)) + { + } - private const string DefaultRemoteServerUrl = "http://127.0.0.1:4444/wd/hub"; + /// + /// Initializes a new instance of the class + /// + /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). + /// An object containing the desired capabilities of the browser. + public RemoteWebDriver(Uri remoteAddress, ICapabilities capabilities) + : this(remoteAddress, capabilities, RemoteWebDriver.DefaultCommandTimeout) + { + } - private DevToolsSession? devToolsSession; + /// + /// Initializes a new instance of the class using the specified remote address, desired capabilities, and command timeout. + /// + /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). + /// An object containing the desired capabilities of the browser. + /// The maximum amount of time to wait for each command. + public RemoteWebDriver(Uri remoteAddress, ICapabilities capabilities, TimeSpan commandTimeout) + : this(new HttpCommandExecutor(remoteAddress, commandTimeout), capabilities) + { + } - /// - /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub - /// - /// An object containing the desired capabilities of the browser. - public RemoteWebDriver(DriverOptions options) - : this(ConvertOptionsToCapabilities(options)) - { - } + /// + /// Initializes a new instance of the class + /// + /// An object which executes commands for the driver. + /// An object containing the desired capabilities of the browser. + public RemoteWebDriver(ICommandExecutor commandExecutor, ICapabilities capabilities) + : base(commandExecutor, capabilities) + { + } - /// - /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub - /// - /// An object containing the desired capabilities of the browser. - public RemoteWebDriver(ICapabilities capabilities) - : this(new Uri(DefaultRemoteServerUrl), capabilities) - { - } + /// + /// Gets a value indicating whether a DevTools session is active. + /// + [MemberNotNullWhen(true, nameof(devToolsSession))] + public bool HasActiveDevToolsSession => this.devToolsSession != null; - /// - /// Initializes a new instance of the class. This constructor defaults proxy to http://127.0.0.1:4444/wd/hub - /// - /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). - /// An object containing the desired capabilities of the browser. - public RemoteWebDriver(Uri remoteAddress, DriverOptions options) - : this(remoteAddress, ConvertOptionsToCapabilities(options)) - { - } + /// + /// Finds the first element in the page that matches the ID supplied + /// + /// ID of the element + /// IWebElement object so that you can interact with that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementById("id") + /// + /// + public IWebElement FindElementById(string id) + { + return this.FindElement("css selector", "#" + By.EscapeCssSelector(id)); + } - /// - /// Initializes a new instance of the class - /// - /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). - /// An object containing the desired capabilities of the browser. - public RemoteWebDriver(Uri remoteAddress, ICapabilities capabilities) - : this(remoteAddress, capabilities, RemoteWebDriver.DefaultCommandTimeout) + /// + /// Finds the first element in the page that matches the ID supplied + /// + /// ID of the Element + /// ReadOnlyCollection of Elements that match the object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsById("id") + /// + /// + public ReadOnlyCollection FindElementsById(string id) + { + string selector = By.EscapeCssSelector(id); + if (string.IsNullOrEmpty(selector)) { + // Finding multiple elements with an empty ID will return + // an empty list. However, finding by a CSS selector of '#' + // throws an exception, even in the multiple elements case, + // which means we need to short-circuit that behavior. + return new List().AsReadOnly(); } - /// - /// Initializes a new instance of the class using the specified remote address, desired capabilities, and command timeout. - /// - /// URI containing the address of the WebDriver remote server (e.g. http://127.0.0.1:4444/wd/hub). - /// An object containing the desired capabilities of the browser. - /// The maximum amount of time to wait for each command. - public RemoteWebDriver(Uri remoteAddress, ICapabilities capabilities, TimeSpan commandTimeout) - : this(new HttpCommandExecutor(remoteAddress, commandTimeout), capabilities) - { - } + return this.FindElements("css selector", "#" + selector); + } - /// - /// Initializes a new instance of the class - /// - /// An object which executes commands for the driver. - /// An object containing the desired capabilities of the browser. - public RemoteWebDriver(ICommandExecutor commandExecutor, ICapabilities capabilities) - : base(commandExecutor, capabilities) + /// + /// Finds the first element in the page that matches the CSS Class supplied + /// + /// className of the + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementByClassName("classname") + /// + /// + public IWebElement FindElementByClassName(string className) + { + string selector = By.EscapeCssSelector(className); + if (selector.Contains(" ")) { + // Finding elements by class name with whitespace is not allowed. + // However, converting the single class name to a valid CSS selector + // by prepending a '.' may result in a still-valid, but incorrect + // selector. Thus, we short-circuit that behavior here. + throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); } - /// - /// Gets a value indicating whether a DevTools session is active. - /// - [MemberNotNullWhen(true, nameof(devToolsSession))] - public bool HasActiveDevToolsSession => this.devToolsSession != null; - - /// - /// Finds the first element in the page that matches the ID supplied - /// - /// ID of the element - /// IWebElement object so that you can interact with that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementById("id") - /// - /// - public IWebElement FindElementById(string id) - { - return this.FindElement("css selector", "#" + By.EscapeCssSelector(id)); - } + return this.FindElement("css selector", "." + selector); + } - /// - /// Finds the first element in the page that matches the ID supplied - /// - /// ID of the Element - /// ReadOnlyCollection of Elements that match the object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsById("id") - /// - /// - public ReadOnlyCollection FindElementsById(string id) + /// + /// Finds a list of elements that match the class name supplied + /// + /// CSS class Name on the element + /// ReadOnlyCollection of IWebElement object so that you can interact with those objects + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByClassName("classname") + /// + /// + public ReadOnlyCollection FindElementsByClassName(string className) + { + string selector = By.EscapeCssSelector(className); + if (selector.Contains(" ")) { - string selector = By.EscapeCssSelector(id); - if (string.IsNullOrEmpty(selector)) - { - // Finding multiple elements with an empty ID will return - // an empty list. However, finding by a CSS selector of '#' - // throws an exception, even in the multiple elements case, - // which means we need to short-circuit that behavior. - return new List().AsReadOnly(); - } - - return this.FindElements("css selector", "#" + selector); + // Finding elements by class name with whitespace is not allowed. + // However, converting the single class name to a valid CSS selector + // by prepending a '.' may result in a still-valid, but incorrect + // selector. Thus, we short-circuit that behavior here. + throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); } - /// - /// Finds the first element in the page that matches the CSS Class supplied - /// - /// className of the - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementByClassName("classname") - /// - /// - public IWebElement FindElementByClassName(string className) - { - string selector = By.EscapeCssSelector(className); - if (selector.Contains(" ")) - { - // Finding elements by class name with whitespace is not allowed. - // However, converting the single class name to a valid CSS selector - // by prepending a '.' may result in a still-valid, but incorrect - // selector. Thus, we short-circuit that behavior here. - throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); - } + return this.FindElements("css selector", "." + selector); + } - return this.FindElement("css selector", "." + selector); - } + /// + /// Finds the first of elements that match the link text supplied + /// + /// Link text of element + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementsByLinkText("linktext") + /// + /// + public IWebElement FindElementByLinkText(string linkText) + { + return this.FindElement("link text", linkText); + } - /// - /// Finds a list of elements that match the class name supplied - /// - /// CSS class Name on the element - /// ReadOnlyCollection of IWebElement object so that you can interact with those objects - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByClassName("classname") - /// - /// - public ReadOnlyCollection FindElementsByClassName(string className) - { - string selector = By.EscapeCssSelector(className); - if (selector.Contains(" ")) - { - // Finding elements by class name with whitespace is not allowed. - // However, converting the single class name to a valid CSS selector - // by prepending a '.' may result in a still-valid, but incorrect - // selector. Thus, we short-circuit that behavior here. - throw new InvalidSelectorException("Compound class names not allowed. Cannot have whitespace in class name. Use CSS selectors instead."); - } + /// + /// Finds a list of elements that match the link text supplied + /// + /// Link text of element + /// ReadOnlyCollection]]> object so that you can interact with those objects + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByClassName("classname") + /// + /// + public ReadOnlyCollection FindElementsByLinkText(string linkText) + { + return this.FindElements("link text", linkText); + } - return this.FindElements("css selector", "." + selector); - } + /// + /// Finds the first of elements that match the part of the link text supplied + /// + /// part of the link text + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementsByPartialLinkText("partOfLink") + /// + /// + public IWebElement FindElementByPartialLinkText(string partialLinkText) + { + return this.FindElement("partial link text", partialLinkText); + } - /// - /// Finds the first of elements that match the link text supplied - /// - /// Link text of element - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementsByLinkText("linktext") - /// - /// - public IWebElement FindElementByLinkText(string linkText) - { - return this.FindElement("link text", linkText); - } + /// + /// Finds a list of elements that match the class name supplied + /// + /// part of the link text + /// ReadOnlyCollection]]> objects so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByPartialLinkText("partOfTheLink") + /// + /// + public ReadOnlyCollection FindElementsByPartialLinkText(string partialLinkText) + { + return this.FindElements("partial link text", partialLinkText); + } - /// - /// Finds a list of elements that match the link text supplied - /// - /// Link text of element - /// ReadOnlyCollection]]> object so that you can interact with those objects - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByClassName("classname") - /// - /// - public ReadOnlyCollection FindElementsByLinkText(string linkText) - { - return this.FindElements("link text", linkText); - } + /// + /// Finds the first of elements that match the name supplied + /// + /// Name of the element on the page + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// elem = driver.FindElementsByName("name") + /// + /// + public IWebElement FindElementByName(string name) + { + return this.FindElement("css selector", "*[name=\"" + name + "\"]"); + } - /// - /// Finds the first of elements that match the part of the link text supplied - /// - /// part of the link text - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementsByPartialLinkText("partOfLink") - /// - /// - public IWebElement FindElementByPartialLinkText(string partialLinkText) - { - return this.FindElement("partial link text", partialLinkText); - } + /// + /// Finds a list of elements that match the name supplied + /// + /// Name of element + /// ReadOnlyCollect of IWebElement objects so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByName("name") + /// + /// + public ReadOnlyCollection FindElementsByName(string name) + { + return this.FindElements("css selector", "*[name=\"" + name + "\"]"); + } - /// - /// Finds a list of elements that match the class name supplied - /// - /// part of the link text - /// ReadOnlyCollection]]> objects so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByPartialLinkText("partOfTheLink") - /// - /// - public ReadOnlyCollection FindElementsByPartialLinkText(string partialLinkText) - { - return this.FindElements("partial link text", partialLinkText); - } + /// + /// Finds the first of elements that match the DOM Tag supplied + /// + /// DOM tag Name of the element being searched + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementsByTagName("tag") + /// + /// + public IWebElement FindElementByTagName(string tagName) + { + return this.FindElement("css selector", tagName); + } - /// - /// Finds the first of elements that match the name supplied - /// - /// Name of the element on the page - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// elem = driver.FindElementsByName("name") - /// - /// - public IWebElement FindElementByName(string name) - { - return this.FindElement("css selector", "*[name=\"" + name + "\"]"); - } + /// + /// Finds a list of elements that match the DOM Tag supplied + /// + /// DOM tag Name of element being searched + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByTagName("tag") + /// + /// + public ReadOnlyCollection FindElementsByTagName(string tagName) + { + return this.FindElements("css selector", tagName); + } - /// - /// Finds a list of elements that match the name supplied - /// - /// Name of element - /// ReadOnlyCollect of IWebElement objects so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByName("name") - /// - /// - public ReadOnlyCollection FindElementsByName(string name) - { - return this.FindElements("css selector", "*[name=\"" + name + "\"]"); - } + /// + /// Finds the first of elements that match the XPath supplied + /// + /// xpath to the element + /// IWebElement object so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// IWebElement elem = driver.FindElementsByXPath("//table/tbody/tr/td/a"); + /// + /// + public IWebElement FindElementByXPath(string xpath) + { + return this.FindElement("xpath", xpath); + } - /// - /// Finds the first of elements that match the DOM Tag supplied - /// - /// DOM tag Name of the element being searched - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementsByTagName("tag") - /// - /// - public IWebElement FindElementByTagName(string tagName) - { - return this.FindElement("css selector", tagName); - } + /// + /// Finds a list of elements that match the XPath supplied + /// + /// xpath to the element + /// ReadOnlyCollection of IWebElement objects so that you can interact that object + /// + /// + /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); + /// ReadOnlyCollection]]> elem = driver.FindElementsByXpath("//tr/td/a") + /// + /// + public ReadOnlyCollection FindElementsByXPath(string xpath) + { + return this.FindElements("xpath", xpath); + } - /// - /// Finds a list of elements that match the DOM Tag supplied - /// - /// DOM tag Name of element being searched - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByTagName("tag") - /// - /// - public ReadOnlyCollection FindElementsByTagName(string tagName) - { - return this.FindElements("css selector", tagName); - } + /// + /// Finds the first element matching the specified CSS selector. + /// + /// The CSS selector to match. + /// The first matching the criteria. + public IWebElement FindElementByCssSelector(string cssSelector) + { + return this.FindElement("css selector", cssSelector); + } - /// - /// Finds the first of elements that match the XPath supplied - /// - /// xpath to the element - /// IWebElement object so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// IWebElement elem = driver.FindElementsByXPath("//table/tbody/tr/td/a"); - /// - /// - public IWebElement FindElementByXPath(string xpath) - { - return this.FindElement("xpath", xpath); - } + /// + /// Finds all elements matching the specified CSS selector. + /// + /// The CSS selector to match. + /// A containing all + /// IWebElements matching the criteria. + public ReadOnlyCollection FindElementsByCssSelector(string cssSelector) + { + return this.FindElements("css selector", cssSelector); + } - /// - /// Finds a list of elements that match the XPath supplied - /// - /// xpath to the element - /// ReadOnlyCollection of IWebElement objects so that you can interact that object - /// - /// - /// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions()); - /// ReadOnlyCollection]]> elem = driver.FindElementsByXpath("//tr/td/a") - /// - /// - public ReadOnlyCollection FindElementsByXPath(string xpath) + /// + /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. + /// + /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession() + { + if (this.Capabilities.GetCapability(CapabilityType.BrowserName) is "firefox") { - return this.FindElements("xpath", xpath); + if (_logger.IsEnabled(LogEventLevel.Warn)) + { + _logger.Warn("CDP support for Firefox is deprecated and will be removed in future versions. Please switch to WebDriver BiDi."); + } } - /// - /// Finds the first element matching the specified CSS selector. - /// - /// The CSS selector to match. - /// The first matching the criteria. - public IWebElement FindElementByCssSelector(string cssSelector) - { - return this.FindElement("css selector", cssSelector); - } + return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); + } - /// - /// Finds all elements matching the specified CSS selector. - /// - /// The CSS selector to match. - /// A containing all - /// IWebElements matching the criteria. - public ReadOnlyCollection FindElementsByCssSelector(string cssSelector) + /// + /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. + /// + /// The active session to use to communicate with the Developer Tools debugging protocol. + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession(DevToolsOptions options) + { + if (options is null) { - return this.FindElements("css selector", cssSelector); + throw new ArgumentNullException(nameof(options)); } - /// - /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. - /// - /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession() + if (this.devToolsSession == null) { - if (this.Capabilities.GetCapability(CapabilityType.BrowserName) is "firefox") + object? debuggerAddressObject = this.Capabilities.GetCapability(RemoteDevToolsEndPointCapabilityName); + if (debuggerAddressObject is null) { - if (_logger.IsEnabled(LogEventLevel.Warn)) - { - _logger.Warn("CDP support for Firefox is deprecated and will be removed in future versions. Please switch to WebDriver BiDi."); - } + throw new WebDriverException("Cannot find " + RemoteDevToolsEndPointCapabilityName + " capability for driver"); } - return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = DevToolsSession.AutoDetectDevToolsProtocolVersion }); - } + string debuggerAddress = debuggerAddressObject.ToString()!; - /// - /// Creates a session to communicate with a browser using a Developer Tools debugging protocol. - /// - /// The active session to use to communicate with the Developer Tools debugging protocol. - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession(DevToolsOptions options) - { - if (options is null) + if (!options.ProtocolVersion.HasValue || options.ProtocolVersion == DevToolsSession.AutoDetectDevToolsProtocolVersion) { - throw new ArgumentNullException(nameof(options)); - } - - if (this.devToolsSession == null) - { - object? debuggerAddressObject = this.Capabilities.GetCapability(RemoteDevToolsEndPointCapabilityName); - if (debuggerAddressObject is null) + object? versionObject = this.Capabilities.GetCapability(RemoteDevToolsVersionCapabilityName); + if (versionObject is null) { - throw new WebDriverException("Cannot find " + RemoteDevToolsEndPointCapabilityName + " capability for driver"); + throw new WebDriverException("Cannot find " + RemoteDevToolsVersionCapabilityName + " capability for driver"); } - string debuggerAddress = debuggerAddressObject.ToString()!; + string version = versionObject.ToString()!; - if (!options.ProtocolVersion.HasValue || options.ProtocolVersion == DevToolsSession.AutoDetectDevToolsProtocolVersion) + if (!int.TryParse(version.Substring(0, version.IndexOf(".")), out int devToolsProtocolVersion)) { - object? versionObject = this.Capabilities.GetCapability(RemoteDevToolsVersionCapabilityName); - if (versionObject is null) - { - throw new WebDriverException("Cannot find " + RemoteDevToolsVersionCapabilityName + " capability for driver"); - } - - string version = versionObject.ToString()!; - - if (!int.TryParse(version.Substring(0, version.IndexOf(".")), out int devToolsProtocolVersion)) - { - throw new WebDriverException("Cannot parse protocol version from reported version string: " + version); - } - - options.ProtocolVersion = devToolsProtocolVersion; + throw new WebDriverException("Cannot parse protocol version from reported version string: " + version); } - try - { - DevToolsSession session = new DevToolsSession(debuggerAddress, options); - Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult(); - this.devToolsSession = session; - } - catch (Exception e) - { - throw new WebDriverException("Unexpected error creating WebSocket DevTools session.", e); - } + options.ProtocolVersion = devToolsProtocolVersion; } - return this.devToolsSession; + try + { + DevToolsSession session = new DevToolsSession(debuggerAddress, options); + Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult(); + this.devToolsSession = session; + } + catch (Exception e) + { + throw new WebDriverException("Unexpected error creating WebSocket DevTools session.", e); + } } - /// - /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. - /// - /// The specific version of the Developer Tools debugging protocol to use. - /// The active session to use to communicate with the Developer Tools debugging protocol. - [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public DevToolsSession GetDevToolsSession(int protocolVersion) + return this.devToolsSession; + } + + /// + /// Creates a session to communicate with a browser using a specific version of the Developer Tools debugging protocol. + /// + /// The specific version of the Developer Tools debugging protocol to use. + /// The active session to use to communicate with the Developer Tools debugging protocol. + [Obsolete("Use GetDevToolsSession(DevToolsOptions options)")] + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public DevToolsSession GetDevToolsSession(int protocolVersion) + { + return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = protocolVersion }); + } + + /// + /// Retrieves the downloadable files. + /// + /// A read-only list of file names available for download. + public IReadOnlyList GetDownloadableFiles() + { + var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); + if (enableDownloads == null || !(bool)enableDownloads) { - return GetDevToolsSession(new DevToolsOptions() { ProtocolVersion = protocolVersion }); + throw new WebDriverException("You must enable downloads in order to work with downloadable files."); } - /// - /// Retrieves the downloadable files. - /// - /// A read-only list of file names available for download. - public IReadOnlyList GetDownloadableFiles() - { - var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); - if (enableDownloads == null || !(bool)enableDownloads) - { - throw new WebDriverException("You must enable downloads in order to work with downloadable files."); - } + Response commandResponse = this.Execute(DriverCommand.GetDownloadableFiles, null); - Response commandResponse = this.Execute(DriverCommand.GetDownloadableFiles, null); + if (commandResponse.Value is not Dictionary value) + { + throw new WebDriverException("GetDownloadableFiles returned successfully, but response content was not an object: " + commandResponse.Value); + } - if (commandResponse.Value is not Dictionary value) - { - throw new WebDriverException("GetDownloadableFiles returned successfully, but response content was not an object: " + commandResponse.Value); - } + object?[] namesArray = (object?[])value["names"]!; + return namesArray.Select(obj => obj!.ToString()!).ToList(); + } - object?[] namesArray = (object?[])value["names"]!; - return namesArray.Select(obj => obj!.ToString()!).ToList(); + /// + /// Downloads a file with the specified file name. + /// + /// The name of the file to be downloaded. + /// The target directory where the file should be downloaded to. + /// If is null. + public void DownloadFile(string fileName, string targetDirectory) + { + var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); + if (enableDownloads == null || !(bool)enableDownloads) + { + throw new WebDriverException("You must enable downloads in order to work with downloadable files."); } - /// - /// Downloads a file with the specified file name. - /// - /// The name of the file to be downloaded. - /// The target directory where the file should be downloaded to. - /// If is null. - public void DownloadFile(string fileName, string targetDirectory) + Dictionary parameters = new Dictionary { - var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); - if (enableDownloads == null || !(bool)enableDownloads) - { - throw new WebDriverException("You must enable downloads in order to work with downloadable files."); - } + { "name", fileName } + }; - Dictionary parameters = new Dictionary - { - { "name", fileName } - }; - - Response commandResponse = this.Execute(DriverCommand.DownloadFile, parameters); - if (commandResponse.Value is not Dictionary value) - { - throw new WebDriverException("DownloadFile returned successfully, but response content was not an object: " + commandResponse.Value); - } + Response commandResponse = this.Execute(DriverCommand.DownloadFile, parameters); + if (commandResponse.Value is not Dictionary value) + { + throw new WebDriverException("DownloadFile returned successfully, but response content was not an object: " + commandResponse.Value); + } - string contents = value["contents"]!.ToString()!; - byte[] fileData = Convert.FromBase64String(contents); + string contents = value["contents"]!.ToString()!; + byte[] fileData = Convert.FromBase64String(contents); - Directory.CreateDirectory(targetDirectory); + Directory.CreateDirectory(targetDirectory); - using (var memoryReader = new MemoryStream(fileData)) + using (var memoryReader = new MemoryStream(fileData)) + { + using (var zipArchive = new ZipArchive(memoryReader, ZipArchiveMode.Read)) { - using (var zipArchive = new ZipArchive(memoryReader, ZipArchiveMode.Read)) + foreach (ZipArchiveEntry entry in zipArchive.Entries) { - foreach (ZipArchiveEntry entry in zipArchive.Entries) - { - string destinationPath = Path.Combine(targetDirectory, entry.FullName); + string destinationPath = Path.Combine(targetDirectory, entry.FullName); - entry.ExtractToFile(destinationPath); - } + entry.ExtractToFile(destinationPath); } } } + } - /// - /// Deletes all downloadable files. - /// - public void DeleteDownloadableFiles() + /// + /// Deletes all downloadable files. + /// + public void DeleteDownloadableFiles() + { + var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); + if (enableDownloads == null || !(bool)enableDownloads) { - var enableDownloads = this.Capabilities.GetCapability(CapabilityType.EnableDownloads); - if (enableDownloads == null || !(bool)enableDownloads) - { - throw new WebDriverException("You must enable downloads in order to work with downloadable files."); - } - - this.Execute(DriverCommand.DeleteDownloadableFiles, null); + throw new WebDriverException("You must enable downloads in order to work with downloadable files."); } - /// - /// Closes a DevTools session. - /// - [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] - public void CloseDevToolsSession() + this.Execute(DriverCommand.DeleteDownloadableFiles, null); + } + + /// + /// Closes a DevTools session. + /// + [RequiresUnreferencedCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + [RequiresDynamicCode(DevToolsSession.CDP_AOTIncompatibilityMessage)] + public void CloseDevToolsSession() + { + if (this.devToolsSession != null) { - if (this.devToolsSession != null) - { - Task.Run(async () => await this.devToolsSession.StopSession(true)).GetAwaiter().GetResult(); - } + Task.Run(async () => await this.devToolsSession.StopSession(true)).GetAwaiter().GetResult(); } + } - /// - /// Releases all resources associated with this . - /// - /// if the Dispose method was explicitly called; otherwise, . - protected override void Dispose(bool disposing) + /// + /// Releases all resources associated with this . + /// + /// if the Dispose method was explicitly called; otherwise, . + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) + if (this.devToolsSession != null) { - if (this.devToolsSession != null) - { - this.devToolsSession.Dispose(); - this.devToolsSession = null; - } + this.devToolsSession.Dispose(); + this.devToolsSession = null; } - - base.Dispose(disposing); } - private static ICapabilities ConvertOptionsToCapabilities(DriverOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options), "Driver options must not be null"); - } + base.Dispose(disposing); + } - return options.ToCapabilities(); + private static ICapabilities ConvertOptionsToCapabilities(DriverOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options), "Driver options must not be null"); } + + return options.ToCapabilities(); } } diff --git a/dotnet/src/webdriver/Remote/SendingRemoteHttpRequestEventArgs.cs b/dotnet/src/webdriver/Remote/SendingRemoteHttpRequestEventArgs.cs index 5337ccfe857fb..5883f7346d56f 100644 --- a/dotnet/src/webdriver/Remote/SendingRemoteHttpRequestEventArgs.cs +++ b/dotnet/src/webdriver/Remote/SendingRemoteHttpRequestEventArgs.cs @@ -20,75 +20,74 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Provides data for the SendingRemoteHttpRequest event of a object. +/// +public class SendingRemoteHttpRequestEventArgs : EventArgs { + private readonly Dictionary headers = new Dictionary(); + /// - /// Provides data for the SendingRemoteHttpRequest event of a object. + /// Initializes a new instance of the class. /// - public class SendingRemoteHttpRequestEventArgs : EventArgs + /// The HTTP method of the request being sent. + /// The full URL of the request being sent. + /// The body of the request. + /// If , are null. + public SendingRemoteHttpRequestEventArgs(string method, string fullUrl, string? requestBody) { - private readonly Dictionary headers = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The HTTP method of the request being sent. - /// The full URL of the request being sent. - /// The body of the request. - /// If , are null. - public SendingRemoteHttpRequestEventArgs(string method, string fullUrl, string? requestBody) - { - this.Method = method ?? throw new ArgumentNullException(nameof(method)); - this.FullUrl = fullUrl ?? throw new ArgumentNullException(nameof(fullUrl)); - this.RequestBody = requestBody; - } + this.Method = method ?? throw new ArgumentNullException(nameof(method)); + this.FullUrl = fullUrl ?? throw new ArgumentNullException(nameof(fullUrl)); + this.RequestBody = requestBody; + } - /// - /// Gets the HTTP method for the HTTP request. - /// - public string Method { get; } + /// + /// Gets the HTTP method for the HTTP request. + /// + public string Method { get; } - /// - /// Gets the full URL of the HTTP request. - /// - public string FullUrl { get; } + /// + /// Gets the full URL of the HTTP request. + /// + public string FullUrl { get; } - /// - /// Gets the body of the HTTP request as a string. - /// - public string? RequestBody { get; } + /// + /// Gets the body of the HTTP request as a string. + /// + public string? RequestBody { get; } - /// - /// Gets a read-only dictionary of the headers of the HTTP request. - /// Does not include default headers of the web client making the request. - /// - public IReadOnlyDictionary Headers => this.headers; + /// + /// Gets a read-only dictionary of the headers of the HTTP request. + /// Does not include default headers of the web client making the request. + /// + public IReadOnlyDictionary Headers => this.headers; - /// - /// Adds a header to the HTTP request. - /// - /// The name of the header to add. - /// The value of the header to add. - /// - /// Adding headers here will attempt to add them to the headers for the - /// HTTP request being sent; however, be aware they may be overwritten by - /// the client raising the event. - /// - /// If is or . - /// If is . - public void AddHeader(string headerName, string headerValue) + /// + /// Adds a header to the HTTP request. + /// + /// The name of the header to add. + /// The value of the header to add. + /// + /// Adding headers here will attempt to add them to the headers for the + /// HTTP request being sent; however, be aware they may be overwritten by + /// the client raising the event. + /// + /// If is or . + /// If is . + public void AddHeader(string headerName, string headerValue) + { + if (string.IsNullOrEmpty(headerName)) { - if (string.IsNullOrEmpty(headerName)) - { - throw new ArgumentException("Header name may not be null or the empty string.", nameof(headerName)); - } - - if (headerValue == null) - { - throw new ArgumentNullException(nameof(headerValue), "Header value may not be null."); - } + throw new ArgumentException("Header name may not be null or the empty string.", nameof(headerName)); + } - this.headers[headerName] = headerValue; + if (headerValue == null) + { + throw new ArgumentNullException(nameof(headerValue), "Header value may not be null."); } + + this.headers[headerName] = headerValue; } } diff --git a/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs b/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs index 3df1370fa57a7..80feb6d6fa6f1 100644 --- a/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs +++ b/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs @@ -19,121 +19,120 @@ using System; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +/// +/// Holds the information about all commands specified by the JSON wire protocol. +/// This class cannot be inherited, as it is intended to be a singleton, and +/// allowing subclasses introduces the possibility of multiple instances. +/// +public sealed class W3CWireProtocolCommandInfoRepository : CommandInfoRepository { /// - /// Holds the information about all commands specified by the JSON wire protocol. - /// This class cannot be inherited, as it is intended to be a singleton, and - /// allowing subclasses introduces the possibility of multiple instances. + /// Initializes a new instance of the class. /// - public sealed class W3CWireProtocolCommandInfoRepository : CommandInfoRepository + public W3CWireProtocolCommandInfoRepository() + : base() { - /// - /// Initializes a new instance of the class. - /// - public W3CWireProtocolCommandInfoRepository() - : base() - { - this.InitializeCommandDictionary(); - } + this.InitializeCommandDictionary(); + } - /// - /// Gets the level of the W3C WebDriver specification that this repository supports. - /// - public override int SpecificationLevel => 1; + /// + /// Gets the level of the W3C WebDriver specification that this repository supports. + /// + public override int SpecificationLevel => 1; - /// - /// Gets the that is valid for this - /// - protected override Type RepositoryCommandInfoType => typeof(HttpCommandInfo); + /// + /// Gets the that is valid for this + /// + protected override Type RepositoryCommandInfoType => typeof(HttpCommandInfo); - /// - /// Initializes the dictionary of commands for the CommandInfoRepository - /// - protected override void InitializeCommandDictionary() - { - this.TryAddCommand(DriverCommand.Status, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/status")); - this.TryAddCommand(DriverCommand.NewSession, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session")); - this.TryAddCommand(DriverCommand.Quit, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}")); - this.TryAddCommand(DriverCommand.GetTimeouts, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/timeouts")); - this.TryAddCommand(DriverCommand.SetTimeouts, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/timeouts")); - this.TryAddCommand(DriverCommand.Get, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/url")); - this.TryAddCommand(DriverCommand.GetCurrentUrl, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/url")); - this.TryAddCommand(DriverCommand.GoBack, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/back")); - this.TryAddCommand(DriverCommand.GoForward, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/forward")); - this.TryAddCommand(DriverCommand.Refresh, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/refresh")); - this.TryAddCommand(DriverCommand.GetTitle, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/title")); - this.TryAddCommand(DriverCommand.GetCurrentWindowHandle, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window")); - this.TryAddCommand(DriverCommand.Close, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/window")); - this.TryAddCommand(DriverCommand.SwitchToWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window")); - this.TryAddCommand(DriverCommand.GetWindowHandles, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window/handles")); - this.TryAddCommand(DriverCommand.NewWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/new")); - this.TryAddCommand(DriverCommand.SwitchToFrame, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/frame")); - this.TryAddCommand(DriverCommand.SwitchToParentFrame, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/frame/parent")); + /// + /// Initializes the dictionary of commands for the CommandInfoRepository + /// + protected override void InitializeCommandDictionary() + { + this.TryAddCommand(DriverCommand.Status, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/status")); + this.TryAddCommand(DriverCommand.NewSession, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session")); + this.TryAddCommand(DriverCommand.Quit, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}")); + this.TryAddCommand(DriverCommand.GetTimeouts, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/timeouts")); + this.TryAddCommand(DriverCommand.SetTimeouts, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/timeouts")); + this.TryAddCommand(DriverCommand.Get, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/url")); + this.TryAddCommand(DriverCommand.GetCurrentUrl, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/url")); + this.TryAddCommand(DriverCommand.GoBack, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/back")); + this.TryAddCommand(DriverCommand.GoForward, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/forward")); + this.TryAddCommand(DriverCommand.Refresh, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/refresh")); + this.TryAddCommand(DriverCommand.GetTitle, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/title")); + this.TryAddCommand(DriverCommand.GetCurrentWindowHandle, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window")); + this.TryAddCommand(DriverCommand.Close, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/window")); + this.TryAddCommand(DriverCommand.SwitchToWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window")); + this.TryAddCommand(DriverCommand.GetWindowHandles, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window/handles")); + this.TryAddCommand(DriverCommand.NewWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/new")); + this.TryAddCommand(DriverCommand.SwitchToFrame, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/frame")); + this.TryAddCommand(DriverCommand.SwitchToParentFrame, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/frame/parent")); - this.TryAddCommand(DriverCommand.GetWindowRect, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window/rect")); - this.TryAddCommand(DriverCommand.SetWindowRect, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/rect")); + this.TryAddCommand(DriverCommand.GetWindowRect, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/window/rect")); + this.TryAddCommand(DriverCommand.SetWindowRect, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/rect")); - this.TryAddCommand(DriverCommand.MaximizeWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/maximize")); - this.TryAddCommand(DriverCommand.MinimizeWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/minimize")); - this.TryAddCommand(DriverCommand.FullScreenWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/fullscreen")); - this.TryAddCommand(DriverCommand.GetActiveElement, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/active")); - this.TryAddCommand(DriverCommand.GetElementShadowRoot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/shadow")); - this.TryAddCommand(DriverCommand.FindElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element")); - this.TryAddCommand(DriverCommand.FindElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/elements")); - this.TryAddCommand(DriverCommand.FindChildElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/element")); - this.TryAddCommand(DriverCommand.FindChildElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/elements")); - this.TryAddCommand(DriverCommand.FindShadowChildElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/shadow/{id}/element")); - this.TryAddCommand(DriverCommand.FindShadowChildElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/shadow/{id}/elements")); - this.TryAddCommand(DriverCommand.IsElementSelected, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/selected")); - this.TryAddCommand(DriverCommand.ClickElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/click")); - this.TryAddCommand(DriverCommand.ClearElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/clear")); - this.TryAddCommand(DriverCommand.SendKeysToElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/value")); - this.TryAddCommand(DriverCommand.GetElementAttribute, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/attribute/{name}")); - this.TryAddCommand(DriverCommand.GetElementProperty, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/property/{name}")); - this.TryAddCommand(DriverCommand.GetElementValueOfCssProperty, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/css/{name}")); - this.TryAddCommand(DriverCommand.GetComputedAccessibleLabel, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/computedlabel")); - this.TryAddCommand(DriverCommand.GetComputedAccessibleRole, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/computedrole")); - this.TryAddCommand(DriverCommand.GetElementText, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/text")); - this.TryAddCommand(DriverCommand.GetElementTagName, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/name")); - this.TryAddCommand(DriverCommand.GetElementRect, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/rect")); - this.TryAddCommand(DriverCommand.IsElementEnabled, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/enabled")); - this.TryAddCommand(DriverCommand.GetPageSource, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/source")); - this.TryAddCommand(DriverCommand.ExecuteScript, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/execute/sync")); - this.TryAddCommand(DriverCommand.ExecuteAsyncScript, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/execute/async")); - this.TryAddCommand(DriverCommand.GetAllCookies, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/cookie")); - this.TryAddCommand(DriverCommand.GetCookie, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/cookie/{name}")); - this.TryAddCommand(DriverCommand.AddCookie, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/cookie")); - this.TryAddCommand(DriverCommand.DeleteCookie, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/cookie/{name}")); - this.TryAddCommand(DriverCommand.DeleteAllCookies, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/cookie")); - this.TryAddCommand(DriverCommand.Actions, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/actions")); - this.TryAddCommand(DriverCommand.CancelActions, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/actions")); - this.TryAddCommand(DriverCommand.DismissAlert, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/dismiss")); - this.TryAddCommand(DriverCommand.AcceptAlert, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/accept")); - this.TryAddCommand(DriverCommand.GetAlertText, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/alert/text")); - this.TryAddCommand(DriverCommand.SetAlertValue, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/text")); - this.TryAddCommand(DriverCommand.Screenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/screenshot")); - this.TryAddCommand(DriverCommand.ElementScreenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/screenshot")); - this.TryAddCommand(DriverCommand.Print, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/print")); - this.TryAddCommand(DriverCommand.AddVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator")); - this.TryAddCommand(DriverCommand.RemoveVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}")); - this.TryAddCommand(DriverCommand.AddCredential, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credential")); - this.TryAddCommand(DriverCommand.GetCredentials, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials")); - this.TryAddCommand(DriverCommand.RemoveCredential, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials/{credentialId}")); - this.TryAddCommand(DriverCommand.RemoveAllCredentials, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials")); - this.TryAddCommand(DriverCommand.SetUserVerified, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/uv")); + this.TryAddCommand(DriverCommand.MaximizeWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/maximize")); + this.TryAddCommand(DriverCommand.MinimizeWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/minimize")); + this.TryAddCommand(DriverCommand.FullScreenWindow, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/window/fullscreen")); + this.TryAddCommand(DriverCommand.GetActiveElement, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/active")); + this.TryAddCommand(DriverCommand.GetElementShadowRoot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/shadow")); + this.TryAddCommand(DriverCommand.FindElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element")); + this.TryAddCommand(DriverCommand.FindElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/elements")); + this.TryAddCommand(DriverCommand.FindChildElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/element")); + this.TryAddCommand(DriverCommand.FindChildElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/elements")); + this.TryAddCommand(DriverCommand.FindShadowChildElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/shadow/{id}/element")); + this.TryAddCommand(DriverCommand.FindShadowChildElements, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/shadow/{id}/elements")); + this.TryAddCommand(DriverCommand.IsElementSelected, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/selected")); + this.TryAddCommand(DriverCommand.ClickElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/click")); + this.TryAddCommand(DriverCommand.ClearElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/clear")); + this.TryAddCommand(DriverCommand.SendKeysToElement, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/element/{id}/value")); + this.TryAddCommand(DriverCommand.GetElementAttribute, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/attribute/{name}")); + this.TryAddCommand(DriverCommand.GetElementProperty, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/property/{name}")); + this.TryAddCommand(DriverCommand.GetElementValueOfCssProperty, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/css/{name}")); + this.TryAddCommand(DriverCommand.GetComputedAccessibleLabel, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/computedlabel")); + this.TryAddCommand(DriverCommand.GetComputedAccessibleRole, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/computedrole")); + this.TryAddCommand(DriverCommand.GetElementText, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/text")); + this.TryAddCommand(DriverCommand.GetElementTagName, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/name")); + this.TryAddCommand(DriverCommand.GetElementRect, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/rect")); + this.TryAddCommand(DriverCommand.IsElementEnabled, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/enabled")); + this.TryAddCommand(DriverCommand.GetPageSource, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/source")); + this.TryAddCommand(DriverCommand.ExecuteScript, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/execute/sync")); + this.TryAddCommand(DriverCommand.ExecuteAsyncScript, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/execute/async")); + this.TryAddCommand(DriverCommand.GetAllCookies, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/cookie")); + this.TryAddCommand(DriverCommand.GetCookie, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/cookie/{name}")); + this.TryAddCommand(DriverCommand.AddCookie, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/cookie")); + this.TryAddCommand(DriverCommand.DeleteCookie, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/cookie/{name}")); + this.TryAddCommand(DriverCommand.DeleteAllCookies, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/cookie")); + this.TryAddCommand(DriverCommand.Actions, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/actions")); + this.TryAddCommand(DriverCommand.CancelActions, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/actions")); + this.TryAddCommand(DriverCommand.DismissAlert, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/dismiss")); + this.TryAddCommand(DriverCommand.AcceptAlert, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/accept")); + this.TryAddCommand(DriverCommand.GetAlertText, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/alert/text")); + this.TryAddCommand(DriverCommand.SetAlertValue, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/alert/text")); + this.TryAddCommand(DriverCommand.Screenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/screenshot")); + this.TryAddCommand(DriverCommand.ElementScreenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/screenshot")); + this.TryAddCommand(DriverCommand.Print, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/print")); + this.TryAddCommand(DriverCommand.AddVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator")); + this.TryAddCommand(DriverCommand.RemoveVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}")); + this.TryAddCommand(DriverCommand.AddCredential, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credential")); + this.TryAddCommand(DriverCommand.GetCredentials, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials")); + this.TryAddCommand(DriverCommand.RemoveCredential, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials/{credentialId}")); + this.TryAddCommand(DriverCommand.RemoveAllCredentials, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials")); + this.TryAddCommand(DriverCommand.SetUserVerified, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/uv")); - // Commands below here are not included in the W3C specification, - // but are required for full fidelity of execution with Selenium's - // local-end implementation of WebDriver. - this.TryAddCommand(DriverCommand.IsElementDisplayed, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/displayed")); - this.TryAddCommand(DriverCommand.ElementEquals, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/equals/{other}")); - this.TryAddCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log")); - this.TryAddCommand(DriverCommand.GetAvailableLogTypes, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/log/types")); - this.TryAddCommand(DriverCommand.UploadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/file")); - this.TryAddCommand(DriverCommand.GetDownloadableFiles, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/files")); - this.TryAddCommand(DriverCommand.DownloadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/files")); - this.TryAddCommand(DriverCommand.DeleteDownloadableFiles, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/se/files")); - } + // Commands below here are not included in the W3C specification, + // but are required for full fidelity of execution with Selenium's + // local-end implementation of WebDriver. + this.TryAddCommand(DriverCommand.IsElementDisplayed, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/displayed")); + this.TryAddCommand(DriverCommand.ElementEquals, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/equals/{other}")); + this.TryAddCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log")); + this.TryAddCommand(DriverCommand.GetAvailableLogTypes, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/log/types")); + this.TryAddCommand(DriverCommand.UploadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/file")); + this.TryAddCommand(DriverCommand.GetDownloadableFiles, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/files")); + this.TryAddCommand(DriverCommand.DownloadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/files")); + this.TryAddCommand(DriverCommand.DeleteDownloadableFiles, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/se/files")); } } diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index 8b3979601d465..e1967421c0d2d 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -25,185 +25,184 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Handles responses from the browser +/// +public class Response { /// - /// Handles responses from the browser + /// Initializes a new instance of the class /// - public class Response + /// The Session ID in use, if any. + /// The JSON payload of the response. + /// The WebDriver result status of the response. + public Response(string? sessionId, object? value, WebDriverResult status) { - /// - /// Initializes a new instance of the class - /// - /// The Session ID in use, if any. - /// The JSON payload of the response. - /// The WebDriver result status of the response. - public Response(string? sessionId, object? value, WebDriverResult status) + this.SessionId = sessionId; + this.Value = value; + this.Status = status; + } + + /// + /// Returns a new from a JSON-encoded string. + /// + /// The JSON string to deserialize into a . + /// A object described by the JSON string. + /// If is . + /// If is not a valid JSON object. + public static Response FromJson(string value) + { + JsonObject rawResponse = JsonNode.Parse(value) as JsonObject + ?? throw new WebDriverException($"JSON success response did not return a dictionary{Environment.NewLine}{value}"); + + JsonNode? contents; + string? sessionId = null; + + if (rawResponse.TryGetPropertyValue("sessionId", out JsonNode? s) && s is not null) { - this.SessionId = sessionId; - this.Value = value; - this.Status = status; + sessionId = s.ToString(); } - /// - /// Returns a new from a JSON-encoded string. - /// - /// The JSON string to deserialize into a . - /// A object described by the JSON string. - /// If is . - /// If is not a valid JSON object. - public static Response FromJson(string value) + if (rawResponse.TryGetPropertyValue("value", out JsonNode? valueObj)) { - JsonObject rawResponse = JsonNode.Parse(value) as JsonObject - ?? throw new WebDriverException($"JSON success response did not return a dictionary{Environment.NewLine}{value}"); - - JsonNode? contents; - string? sessionId = null; - - if (rawResponse.TryGetPropertyValue("sessionId", out JsonNode? s) && s is not null) + contents = valueObj; + } + else + { + // If the returned object does *not* have a "value" property + // the response value should be the entirety of the response. + // TODO: Remove this if statement altogether; there should + // never be a spec-compliant response that does not contain a + // value property. + + // Special-case for the new session command, where the "capabilities" + // property of the response is the actual value we're interested in. + if (rawResponse.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) { - sessionId = s.ToString(); + contents = capabilities; } - - if (rawResponse.TryGetPropertyValue("value", out JsonNode? valueObj)) + else { - contents = valueObj; + contents = rawResponse; } - else + } + + if (contents is JsonObject valueDictionary) + { + // Special case code for the new session command. If the response contains + // sessionId and capabilities properties, fix up the session ID and value members. + if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session)) { - // If the returned object does *not* have a "value" property - // the response value should be the entirety of the response. - // TODO: Remove this if statement altogether; there should - // never be a spec-compliant response that does not contain a - // value property. - - // Special-case for the new session command, where the "capabilities" - // property of the response is the actual value we're interested in. - if (rawResponse.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) + sessionId = session?.ToString(); + if (valueDictionary.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) { contents = capabilities; } else { - contents = rawResponse; + contents = valueDictionary["value"]; } } - - if (contents is JsonObject valueDictionary) - { - // Special case code for the new session command. If the response contains - // sessionId and capabilities properties, fix up the session ID and value members. - if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session)) - { - sessionId = session?.ToString(); - if (valueDictionary.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) - { - contents = capabilities; - } - else - { - contents = valueDictionary["value"]; - } - } - } - - var contentsDictionary = JsonSerializer.Deserialize(contents, ResponseJsonSerializerContext.Default.Object); - - return new Response(sessionId, contentsDictionary, WebDriverResult.Success); } - /// - /// Gets or sets the value from JSON. - /// - public object? Value { get; } - - /// - /// Gets or sets the session ID. - /// - public string? SessionId { get; } - - /// - /// Gets or sets the status value of the response. - /// - public WebDriverResult Status { get; } - - /// - /// Returns a new from a JSON-encoded string. - /// - /// The JSON string to deserialize into a . - /// A object described by the JSON string. - /// If is . - /// If is not a valid JSON object. - /// If the JSON dictionary is not in the expected state, per spec. - public static Response FromErrorJson(string value) - { - JsonObject responseObject = JsonNode.Parse(value) as JsonObject - ?? throw new WebDriverException($"JSON error response did not return an object{Environment.NewLine}{value}"); + var contentsDictionary = JsonSerializer.Deserialize(contents, ResponseJsonSerializerContext.Default.Object); - if (!responseObject.TryGetPropertyValue("value", out JsonNode? valueNode)) - { - throw new WebDriverException($"The 'value' property was not found in the response{Environment.NewLine}{value}"); - } + return new Response(sessionId, contentsDictionary, WebDriverResult.Success); + } - if (valueNode is not JsonObject valueObject) - { - throw new WebDriverException($"The 'value' property is not a dictionary{Environment.NewLine}{value}"); - } + /// + /// Gets or sets the value from JSON. + /// + public object? Value { get; } - if (!valueObject.TryGetPropertyValue("error", out JsonNode? errorObject)) - { - throw new WebDriverException($"The 'value > error' property was not found in the response{Environment.NewLine}{value}"); - } + /// + /// Gets or sets the session ID. + /// + public string? SessionId { get; } - if (errorObject is not JsonValue errorValue || !errorValue.TryGetValue(out string? errorString)) - { - throw new WebDriverException($"The 'value > error' property is not a string{Environment.NewLine}{value}"); - } + /// + /// Gets or sets the status value of the response. + /// + public WebDriverResult Status { get; } - var valueDictionary = JsonSerializer.Deserialize(valueObject, ResponseJsonSerializerContext.Default.Object); - WebDriverResult status = WebDriverError.ResultFromError(errorString); + /// + /// Returns a new from a JSON-encoded string. + /// + /// The JSON string to deserialize into a . + /// A object described by the JSON string. + /// If is . + /// If is not a valid JSON object. + /// If the JSON dictionary is not in the expected state, per spec. + public static Response FromErrorJson(string value) + { + JsonObject responseObject = JsonNode.Parse(value) as JsonObject + ?? throw new WebDriverException($"JSON error response did not return an object{Environment.NewLine}{value}"); - return new Response(sessionId: null, valueDictionary, status); + if (!responseObject.TryGetPropertyValue("value", out JsonNode? valueNode)) + { + throw new WebDriverException($"The 'value' property was not found in the response{Environment.NewLine}{value}"); } - /// - /// Returns this object as a JSON-encoded string. - /// - /// A JSON-encoded string representing this object. - - [RequiresUnreferencedCode("Untyped JSON serialization is not trim- or AOT- safe.")] - [RequiresDynamicCode("Untyped JSON serialization is not trim- or AOT- safe.")] - public string ToJson() + if (valueNode is not JsonObject valueObject) { - return JsonSerializer.Serialize(this); + throw new WebDriverException($"The 'value' property is not a dictionary{Environment.NewLine}{value}"); } - /// - /// Throws if is . - /// - /// If is . - [MemberNotNull(nameof(Value))] - internal Response EnsureValueIsNotNull() + if (!valueObject.TryGetPropertyValue("error", out JsonNode? errorObject)) { - if (Value is null) - { - throw new WebDriverException("Response from remote end doesn't have $.Value property"); - } + throw new WebDriverException($"The 'value > error' property was not found in the response{Environment.NewLine}{value}"); + } - return this; + if (errorObject is not JsonValue errorValue || !errorValue.TryGetValue(out string? errorString)) + { + throw new WebDriverException($"The 'value > error' property is not a string{Environment.NewLine}{value}"); } - /// - /// Returns the object as a string. - /// - /// A string with the Session ID, status value, and the value from JSON. - public override string ToString() + var valueDictionary = JsonSerializer.Deserialize(valueObject, ResponseJsonSerializerContext.Default.Object); + WebDriverResult status = WebDriverError.ResultFromError(errorString); + + return new Response(sessionId: null, valueDictionary, status); + } + + /// + /// Returns this object as a JSON-encoded string. + /// + /// A JSON-encoded string representing this object. + + [RequiresUnreferencedCode("Untyped JSON serialization is not trim- or AOT- safe.")] + [RequiresDynamicCode("Untyped JSON serialization is not trim- or AOT- safe.")] + public string ToJson() + { + return JsonSerializer.Serialize(this); + } + + /// + /// Throws if is . + /// + /// If is . + [MemberNotNull(nameof(Value))] + internal Response EnsureValueIsNotNull() + { + if (Value is null) { - return string.Format(CultureInfo.InvariantCulture, "({0} {1}: {2})", this.SessionId, this.Status, this.Value); + throw new WebDriverException("Response from remote end doesn't have $.Value property"); } + + return this; } - [JsonSerializable(typeof(object))] - [JsonSourceGenerationOptions(Converters = [typeof(ResponseValueJsonConverter)])] // we still need it to make `Object` as `Dictionary` - internal sealed partial class ResponseJsonSerializerContext : JsonSerializerContext; + /// + /// Returns the object as a string. + /// + /// A string with the Session ID, status value, and the value from JSON. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "({0} {1}: {2})", this.SessionId, this.Status, this.Value); + } } + +[JsonSerializable(typeof(object))] +[JsonSourceGenerationOptions(Converters = [typeof(ResponseValueJsonConverter)])] // we still need it to make `Object` as `Dictionary` +internal sealed partial class ResponseJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/src/webdriver/Safari/SafariDriver.cs b/dotnet/src/webdriver/Safari/SafariDriver.cs index a4fb92c5588ed..8b84b657072f9 100644 --- a/dotnet/src/webdriver/Safari/SafariDriver.cs +++ b/dotnet/src/webdriver/Safari/SafariDriver.cs @@ -22,243 +22,242 @@ using System.Collections.Generic; using System.IO; -namespace OpenQA.Selenium.Safari +namespace OpenQA.Selenium.Safari; + +/// +/// Provides a way to access Safari to run your tests by creating a SafariDriver instance +/// +/// +/// When the WebDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and +/// start your test. +/// +/// +/// +/// [TestFixture] +/// public class Testing +/// { +/// private IWebDriver driver; +/// +/// [SetUp] +/// public void SetUp() +/// { +/// driver = new SafariDriver(); +/// } +/// +/// [Test] +/// public void TestGoogle() +/// { +/// driver.Navigate().GoToUrl("http://www.google.co.uk/"); +/// /* +/// * Rest of the test +/// */ +/// } +/// +/// [TearDown] +/// public void TearDown() +/// { +/// driver.Quit(); +/// driver.Dispose(); +/// } +/// } +/// +/// +public class SafariDriver : WebDriver { + private const string AttachDebuggerCommand = "attachDebugger"; + private const string GetPermissionsCommand = "getPermissions"; + private const string SetPermissionsCommand = "setPermissions"; + /// - /// Provides a way to access Safari to run your tests by creating a SafariDriver instance + /// Initializes a new instance of the class. /// - /// - /// When the WebDriver object has been instantiated the browser will load. The test can then navigate to the URL under test and - /// start your test. - /// - /// - /// - /// [TestFixture] - /// public class Testing - /// { - /// private IWebDriver driver; - /// - /// [SetUp] - /// public void SetUp() - /// { - /// driver = new SafariDriver(); - /// } - /// - /// [Test] - /// public void TestGoogle() - /// { - /// driver.Navigate().GoToUrl("http://www.google.co.uk/"); - /// /* - /// * Rest of the test - /// */ - /// } - /// - /// [TearDown] - /// public void TearDown() - /// { - /// driver.Quit(); - /// driver.Dispose(); - /// } - /// } - /// - /// - public class SafariDriver : WebDriver + public SafariDriver() + : this(new SafariOptions()) { - private const string AttachDebuggerCommand = "attachDebugger"; - private const string GetPermissionsCommand = "getPermissions"; - private const string SetPermissionsCommand = "setPermissions"; + } - /// - /// Initializes a new instance of the class. - /// - public SafariDriver() - : this(new SafariOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified . + /// + /// The to use for this instance. + /// If is . + public SafariDriver(SafariOptions options) + : this(SafariDriverService.CreateDefaultService(), options) + { + } - /// - /// Initializes a new instance of the class using the specified . - /// - /// The to use for this instance. - /// If is . - public SafariDriver(SafariOptions options) - : this(SafariDriverService.CreateDefaultService(), options) - { - } + /// + /// Initializes a new instance of the class using the specified driver service. + /// + /// The used to initialize the driver. + /// If is . + public SafariDriver(SafariDriverService service) + : this(service, new SafariOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified driver service. - /// - /// The used to initialize the driver. - /// If is . - public SafariDriver(SafariDriverService service) - : this(service, new SafariOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing safaridriver. + /// + /// The full path to the directory containing SafariDriver executable. + public SafariDriver(string safariDriverDirectory) + : this(safariDriverDirectory, new SafariOptions()) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing safaridriver. - /// - /// The full path to the directory containing SafariDriver executable. - public SafariDriver(string safariDriverDirectory) - : this(safariDriverDirectory, new SafariOptions()) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing safaridriver and options. + /// + /// The full path to the directory containing SafariDriver executable. + /// The to be used with the Safari driver. + /// If is . + public SafariDriver(string safariDriverDirectory, SafariOptions options) + : this(safariDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing safaridriver and options. - /// - /// The full path to the directory containing SafariDriver executable. - /// The to be used with the Safari driver. - /// If is . - public SafariDriver(string safariDriverDirectory, SafariOptions options) - : this(safariDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified path + /// to the directory containing safaridriver, options, and command timeout. + /// + /// The full path to the directory containing SafariDriver executable. + /// The to be used with the Safari driver. + /// The maximum amount of time to wait for each command. + /// If is . + public SafariDriver(string safariDriverDirectory, SafariOptions options, TimeSpan commandTimeout) + : this(SafariDriverService.CreateDefaultService(safariDriverDirectory), options, commandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified path - /// to the directory containing safaridriver, options, and command timeout. - /// - /// The full path to the directory containing SafariDriver executable. - /// The to be used with the Safari driver. - /// The maximum amount of time to wait for each command. - /// If is . - public SafariDriver(string safariDriverDirectory, SafariOptions options, TimeSpan commandTimeout) - : this(SafariDriverService.CreateDefaultService(safariDriverDirectory), options, commandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified + /// and options. + /// + /// The to use. + /// The used to initialize the driver. + /// If or are . + public SafariDriver(SafariDriverService service, SafariOptions options) + : this(service, options, RemoteWebDriver.DefaultCommandTimeout) + { + } - /// - /// Initializes a new instance of the class using the specified - /// and options. - /// - /// The to use. - /// The used to initialize the driver. - /// If or are . - public SafariDriver(SafariDriverService service, SafariOptions options) - : this(service, options, RemoteWebDriver.DefaultCommandTimeout) - { - } + /// + /// Initializes a new instance of the class using the specified . + /// + /// The to use. + /// The to be used with the Safari driver. + /// The maximum amount of time to wait for each command. + /// If or are . + public SafariDriver(SafariDriverService service, SafariOptions options, TimeSpan commandTimeout) + : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + { + this.AddCustomSafariCommand(AttachDebuggerCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/apple/attach_debugger"); + this.AddCustomSafariCommand(GetPermissionsCommand, HttpCommandInfo.GetCommand, "/session/{sessionId}/apple/permissions"); + this.AddCustomSafariCommand(SetPermissionsCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/apple/permissions"); + } - /// - /// Initializes a new instance of the class using the specified . - /// - /// The to use. - /// The to be used with the Safari driver. - /// The maximum amount of time to wait for each command. - /// If or are . - public SafariDriver(SafariDriverService service, SafariOptions options, TimeSpan commandTimeout) - : base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options)) + /// + /// Uses DriverFinder to set Service attributes if necessary when creating the command executor + /// + /// + /// + /// + /// + private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + { + if (service is null) { - this.AddCustomSafariCommand(AttachDebuggerCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/apple/attach_debugger"); - this.AddCustomSafariCommand(GetPermissionsCommand, HttpCommandInfo.GetCommand, "/session/{sessionId}/apple/permissions"); - this.AddCustomSafariCommand(SetPermissionsCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/apple/permissions"); + throw new ArgumentNullException(nameof(service)); } - /// - /// Uses DriverFinder to set Service attributes if necessary when creating the command executor - /// - /// - /// - /// - /// - private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout) + if (options is null) { - if (service is null) - { - throw new ArgumentNullException(nameof(service)); - } - - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (service.DriverServicePath == null) - { - DriverFinder finder = new DriverFinder(options); - string fullServicePath = finder.GetDriverPath(); - service.DriverServicePath = Path.GetDirectoryName(fullServicePath); - service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); - } - return new DriverServiceCommandExecutor(service, commandTimeout); + throw new ArgumentNullException(nameof(options)); } - /// - /// This opens Safari's Web Inspector. - /// If driver subsequently executes script of "debugger;" - /// the execution will pause, no additional commands will be processed, and the code will time out. - /// - public void AttachDebugger() + if (service.DriverServicePath == null) { - Dictionary parameters = new Dictionary(); - parameters["attachDebugger"] = null; - this.Execute(AttachDebuggerCommand, parameters); + DriverFinder finder = new DriverFinder(options); + string fullServicePath = finder.GetDriverPath(); + service.DriverServicePath = Path.GetDirectoryName(fullServicePath); + service.DriverServiceExecutableName = Path.GetFileName(fullServicePath); } + return new DriverServiceCommandExecutor(service, commandTimeout); + } - /// - /// Set permission of an item on the browser. The only supported permission at this time is "getUserMedia". - /// - /// The name of the item to set permission on. - /// Whether the permission has been granted. - /// If is or . - public void SetPermission(string permissionName, bool permissionValue) - { - if (string.IsNullOrEmpty(permissionName)) - { - throw new ArgumentNullException(nameof(permissionName), "permission must not be null or the empty string"); - } - - Dictionary permissions = new Dictionary(); - permissions[permissionName] = permissionValue; - Dictionary parameters = new Dictionary(); - parameters["permissions"] = permissions; - this.Execute(SetPermissionsCommand, parameters); - } + /// + /// This opens Safari's Web Inspector. + /// If driver subsequently executes script of "debugger;" + /// the execution will pause, no additional commands will be processed, and the code will time out. + /// + public void AttachDebugger() + { + Dictionary parameters = new Dictionary(); + parameters["attachDebugger"] = null; + this.Execute(AttachDebuggerCommand, parameters); + } - /// - /// Returns Each available permission item and whether it is allowed or not. - /// - /// whether the item is allowed or not. - public object? GetPermissions() + /// + /// Set permission of an item on the browser. The only supported permission at this time is "getUserMedia". + /// + /// The name of the item to set permission on. + /// Whether the permission has been granted. + /// If is or . + public void SetPermission(string permissionName, bool permissionValue) + { + if (string.IsNullOrEmpty(permissionName)) { - Response response = this.Execute(GetPermissionsCommand, null); - return response.Value; + throw new ArgumentNullException(nameof(permissionName), "permission must not be null or the empty string"); } - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// The Safari driver does not allow a file detector to be set, - /// as the server component of the Safari driver (the Safari extension) only - /// allows uploads from the local computer environment. Attempting to set - /// this property has no effect, but does not throw an exception. If you - /// are attempting to run the Safari driver remotely, use - /// in conjunction with a standalone WebDriver server. - public override IFileDetector FileDetector - { - get => base.FileDetector; - set { } - } + Dictionary permissions = new Dictionary(); + permissions[permissionName] = permissionValue; + Dictionary parameters = new Dictionary(); + parameters["permissions"] = permissions; + this.Execute(SetPermissionsCommand, parameters); + } - private static ICapabilities ConvertOptionsToCapabilities(SafariOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options), "options must not be null"); - } + /// + /// Returns Each available permission item and whether it is allowed or not. + /// + /// whether the item is allowed or not. + public object? GetPermissions() + { + Response response = this.Execute(GetPermissionsCommand, null); + return response.Value; + } - return options.ToCapabilities(); - } + /// + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. + /// + /// The Safari driver does not allow a file detector to be set, + /// as the server component of the Safari driver (the Safari extension) only + /// allows uploads from the local computer environment. Attempting to set + /// this property has no effect, but does not throw an exception. If you + /// are attempting to run the Safari driver remotely, use + /// in conjunction with a standalone WebDriver server. + public override IFileDetector FileDetector + { + get => base.FileDetector; + set { } + } - private void AddCustomSafariCommand(string commandName, string method, string resourcePath) + private static ICapabilities ConvertOptionsToCapabilities(SafariOptions options) + { + if (options == null) { - HttpCommandInfo commandInfoToAdd = new HttpCommandInfo(method, resourcePath); - this.CommandExecutor.TryAddCommand(commandName, commandInfoToAdd); + throw new ArgumentNullException(nameof(options), "options must not be null"); } + + return options.ToCapabilities(); + } + + private void AddCustomSafariCommand(string commandName, string method, string resourcePath) + { + HttpCommandInfo commandInfoToAdd = new HttpCommandInfo(method, resourcePath); + this.CommandExecutor.TryAddCommand(commandName, commandInfoToAdd); } } diff --git a/dotnet/src/webdriver/Safari/SafariDriverService.cs b/dotnet/src/webdriver/Safari/SafariDriverService.cs index a3de41bf901fe..30f53d1336be7 100644 --- a/dotnet/src/webdriver/Safari/SafariDriverService.cs +++ b/dotnet/src/webdriver/Safari/SafariDriverService.cs @@ -25,109 +25,108 @@ using System.Text; using System.Threading.Tasks; -namespace OpenQA.Selenium.Safari +namespace OpenQA.Selenium.Safari; + +/// +/// Exposes the service provided by the native SafariDriver executable. +/// +public sealed class SafariDriverService : DriverService { + private const string DefaultSafariDriverServiceExecutableName = "safaridriver"; + /// - /// Exposes the service provided by the native SafariDriver executable. + /// Initializes a new instance of the class. /// - public sealed class SafariDriverService : DriverService + /// The directory of the SafariDriver executable. + /// The file name of the SafariDriver executable. + /// The port on which the SafariDriver executable should listen. + private SafariDriverService(string? executablePath, string? executableFileName, int port) + : base(executablePath, port, executableFileName) { - private const string DefaultSafariDriverServiceExecutableName = "safaridriver"; + } - /// - /// Initializes a new instance of the class. - /// - /// The directory of the SafariDriver executable. - /// The file name of the SafariDriver executable. - /// The port on which the SafariDriver executable should listen. - private SafariDriverService(string? executablePath, string? executableFileName, int port) - : base(executablePath, port, executableFileName) - { - } + /// + protected override DriverOptions GetDefaultDriverOptions() + { + return new SafariOptions(); + } - /// - protected override DriverOptions GetDefaultDriverOptions() + /// + /// Gets the command-line arguments for the driver service. + /// + protected override string CommandLineArguments + { + get { - return new SafariOptions(); + StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); + return argsBuilder.ToString(); } + } - /// - /// Gets the command-line arguments for the driver service. - /// - protected override string CommandLineArguments - { - get - { - StringBuilder argsBuilder = new StringBuilder(base.CommandLineArguments); - return argsBuilder.ToString(); - } - } + /// + /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. + /// For the Safari driver, there is no time for termination + /// + protected override TimeSpan TerminationTimeout + { + // Use a very small timeout for terminating the Safari driver, + // because the executable does not have a clean shutdown command, + // which means we have to kill the process. Using a short timeout + // gets us to the termination point much faster. + get => TimeSpan.FromMilliseconds(100); + } - /// - /// Gets a value indicating the time to wait for the service to terminate before forcing it to terminate. - /// For the Safari driver, there is no time for termination - /// - protected override TimeSpan TerminationTimeout - { - // Use a very small timeout for terminating the Safari driver, - // because the executable does not have a clean shutdown command, - // which means we have to kill the process. Using a short timeout - // gets us to the termination point much faster. - get => TimeSpan.FromMilliseconds(100); - } + /// + /// Gets a value indicating whether the service has a shutdown API that can be called to terminate + /// it gracefully before forcing a termination. + /// + protected override bool HasShutdown + { + // The Safari driver executable does not have a clean shutdown command, + // which means we have to kill the process. + get => false; + } - /// - /// Gets a value indicating whether the service has a shutdown API that can be called to terminate - /// it gracefully before forcing a termination. - /// - protected override bool HasShutdown - { - // The Safari driver executable does not have a clean shutdown command, - // which means we have to kill the process. - get => false; - } + /// + /// Creates a default instance of the SafariDriverService. + /// + /// A SafariDriverService that implements default settings. + public static SafariDriverService CreateDefaultService() + { + return new SafariDriverService(null, null, PortUtilities.FindFreePort()); + } - /// - /// Creates a default instance of the SafariDriverService. - /// - /// A SafariDriverService that implements default settings. - public static SafariDriverService CreateDefaultService() + /// + /// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable. + /// + /// The path to the executable or the directory containing the SafariDriver executable. + /// A SafariDriverService using a random port. + public static SafariDriverService CreateDefaultService(string? driverPath) + { + if (File.Exists(driverPath)) { - return new SafariDriverService(null, null, PortUtilities.FindFreePort()); - } + string fileName = Path.GetFileName(driverPath); + string driverFolder = Path.GetDirectoryName(driverPath)!; - /// - /// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable. - /// - /// The path to the executable or the directory containing the SafariDriver executable. - /// A SafariDriverService using a random port. - public static SafariDriverService CreateDefaultService(string? driverPath) + return CreateDefaultService(driverFolder, fileName); + } + else { - if (File.Exists(driverPath)) - { - string fileName = Path.GetFileName(driverPath); - string driverFolder = Path.GetDirectoryName(driverPath)!; + string fileName = DefaultSafariDriverServiceExecutableName; + string? driverFolder = driverPath; - return CreateDefaultService(driverFolder, fileName); - } - else - { - string fileName = DefaultSafariDriverServiceExecutableName; - string? driverFolder = driverPath; - - return CreateDefaultService(driverFolder, fileName); - } + return CreateDefaultService(driverFolder, fileName); } + } - /// - /// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable with the given name. - /// - /// The directory containing the SafariDriver executable. - /// The name of the SafariDriver executable file. - /// A SafariDriverService using a random port. - public static SafariDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) - { - return new SafariDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); - } + /// + /// Creates a default instance of the SafariDriverService using a specified path to the SafariDriver executable with the given name. + /// + /// The directory containing the SafariDriver executable. + /// The name of the SafariDriver executable file. + /// A SafariDriverService using a random port. + public static SafariDriverService CreateDefaultService(string? driverPath, string? driverExecutableFileName) + { + return new SafariDriverService(driverPath, driverExecutableFileName, PortUtilities.FindFreePort()); } } diff --git a/dotnet/src/webdriver/Safari/SafariOptions.cs b/dotnet/src/webdriver/Safari/SafariOptions.cs index b2d6307dc48fa..cad0132eb5236 100644 --- a/dotnet/src/webdriver/Safari/SafariOptions.cs +++ b/dotnet/src/webdriver/Safari/SafariOptions.cs @@ -17,93 +17,92 @@ // under the License. // -namespace OpenQA.Selenium.Safari +namespace OpenQA.Selenium.Safari; + +/// +/// Class to manage options specific to +/// +/// +/// +/// SafariOptions options = new SafariOptions(); +/// options.SkipExtensionInstallation = true; +/// +/// +/// For use with SafariDriver: +/// +/// +/// SafariDriver driver = new SafariDriver(options); +/// +/// +/// For use with RemoteWebDriver: +/// +/// +/// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); +/// +/// +public class SafariOptions : DriverOptions { + private const string BrowserNameValue = "safari"; + private const string EnableAutomaticInspectionSafariOption = "safari:automaticInspection"; + private const string EnableAutomaticProfilingSafariOption = "safari:automaticProfiling"; + /// - /// Class to manage options specific to + /// Initializes a new instance of the class. /// - /// - /// - /// SafariOptions options = new SafariOptions(); - /// options.SkipExtensionInstallation = true; - /// - /// - /// For use with SafariDriver: - /// - /// - /// SafariDriver driver = new SafariDriver(options); - /// - /// - /// For use with RemoteWebDriver: - /// - /// - /// RemoteWebDriver driver = new RemoteWebDriver(new Uri("http://localhost:4444/wd/hub"), options.ToCapabilities()); - /// - /// - public class SafariOptions : DriverOptions + public SafariOptions() : base() { - private const string BrowserNameValue = "safari"; - private const string EnableAutomaticInspectionSafariOption = "safari:automaticInspection"; - private const string EnableAutomaticProfilingSafariOption = "safari:automaticProfiling"; - - /// - /// Initializes a new instance of the class. - /// - public SafariOptions() : base() - { - this.BrowserName = BrowserNameValue; - this.TechnologyPreview = false; - this.AddKnownCapabilityName(SafariOptions.EnableAutomaticInspectionSafariOption, "EnableAutomaticInspection property"); - this.AddKnownCapabilityName(SafariOptions.EnableAutomaticProfilingSafariOption, "EnableAutomaticProfiling property"); - } + this.BrowserName = BrowserNameValue; + this.TechnologyPreview = false; + this.AddKnownCapabilityName(SafariOptions.EnableAutomaticInspectionSafariOption, "EnableAutomaticInspection property"); + this.AddKnownCapabilityName(SafariOptions.EnableAutomaticProfilingSafariOption, "EnableAutomaticProfiling property"); + } - /// - /// Allows the Options class to be used with a Safari Technology Preview driver - /// - public void UseTechnologyPreview() - { - this.TechnologyPreview = true; - this.BrowserName = "Safari Technology Preview"; - } + /// + /// Allows the Options class to be used with a Safari Technology Preview driver + /// + public void UseTechnologyPreview() + { + this.TechnologyPreview = true; + this.BrowserName = "Safari Technology Preview"; + } - /// - /// Gets or sets a value indicating whether to have the driver preload the - /// Web Inspector and JavaScript debugger in the background. - /// - public bool TechnologyPreview { get; private set; } = false; + /// + /// Gets or sets a value indicating whether to have the driver preload the + /// Web Inspector and JavaScript debugger in the background. + /// + public bool TechnologyPreview { get; private set; } = false; - /// - /// Gets or sets a value indicating whether to have the driver preload the - /// Web Inspector and JavaScript debugger in the background. - /// - public bool EnableAutomaticInspection { get; set; } = false; + /// + /// Gets or sets a value indicating whether to have the driver preload the + /// Web Inspector and JavaScript debugger in the background. + /// + public bool EnableAutomaticInspection { get; set; } = false; - /// - /// Gets or sets a value indicating whether to have the driver preload the - /// Web Inspector and start a timeline recording in the background. - /// - public bool EnableAutomaticProfiling { get; set; } = false; + /// + /// Gets or sets a value indicating whether to have the driver preload the + /// Web Inspector and start a timeline recording in the background. + /// + public bool EnableAutomaticProfiling { get; set; } = false; - /// - /// Returns ICapabilities for Safari with these options included as - /// capabilities. This copies the options. Further changes will not be - /// reflected in the returned capabilities. - /// - /// The ICapabilities for Safari with these options. - public override ICapabilities ToCapabilities() + /// + /// Returns ICapabilities for Safari with these options included as + /// capabilities. This copies the options. Further changes will not be + /// reflected in the returned capabilities. + /// + /// The ICapabilities for Safari with these options. + public override ICapabilities ToCapabilities() + { + IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(true); + if (this.EnableAutomaticInspection) { - IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(true); - if (this.EnableAutomaticInspection) - { - capabilities.SetCapability(EnableAutomaticInspectionSafariOption, true); - } - - if (this.EnableAutomaticProfiling) - { - capabilities.SetCapability(EnableAutomaticProfilingSafariOption, true); - } + capabilities.SetCapability(EnableAutomaticInspectionSafariOption, true); + } - return capabilities.AsReadOnly(); + if (this.EnableAutomaticProfiling) + { + capabilities.SetCapability(EnableAutomaticProfilingSafariOption, true); } + + return capabilities.AsReadOnly(); } } diff --git a/dotnet/src/webdriver/ScreenOrientation.cs b/dotnet/src/webdriver/ScreenOrientation.cs index fb496b4703c9b..2cd3f3db5fa63 100644 --- a/dotnet/src/webdriver/ScreenOrientation.cs +++ b/dotnet/src/webdriver/ScreenOrientation.cs @@ -17,21 +17,20 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents possible screen orientations. +/// +public enum ScreenOrientation { /// - /// Represents possible screen orientations. + /// Represents a portrait mode, where the screen is vertical. /// - public enum ScreenOrientation - { - /// - /// Represents a portrait mode, where the screen is vertical. - /// - Portrait, + Portrait, - /// - /// Represents Landscape mode, where the screen is horizontal. - /// - Landscape - } + /// + /// Represents Landscape mode, where the screen is horizontal. + /// + Landscape } diff --git a/dotnet/src/webdriver/Screenshot.cs b/dotnet/src/webdriver/Screenshot.cs index da17cf92e1601..3e87746e3e98c 100644 --- a/dotnet/src/webdriver/Screenshot.cs +++ b/dotnet/src/webdriver/Screenshot.cs @@ -20,37 +20,36 @@ using System; using System.IO; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents an image of the page currently loaded in the browser. +/// +[Serializable] +public class Screenshot : EncodedFile { /// - /// Represents an image of the page currently loaded in the browser. + /// Initializes a new instance of the class. /// - [Serializable] - public class Screenshot : EncodedFile + /// The image of the page as a Base64-encoded string. + /// If is . + /// + /// The length of , ignoring white-space characters, is not zero or a multiple of 4. + /// -or- + /// The format of is invalid. contains a non-base-64 character, + /// more than two padding characters, or a non-white space-character among the padding characters. + /// + public Screenshot(string base64EncodedScreenshot) : base(base64EncodedScreenshot) { - /// - /// Initializes a new instance of the class. - /// - /// The image of the page as a Base64-encoded string. - /// If is . - /// - /// The length of , ignoring white-space characters, is not zero or a multiple of 4. - /// -or- - /// The format of is invalid. contains a non-base-64 character, - /// more than two padding characters, or a non-white space-character among the padding characters. - /// - public Screenshot(string base64EncodedScreenshot) : base(base64EncodedScreenshot) - { - } + } - /// - /// Saves the screenshot to a Portable Network Graphics (PNG) file, overwriting the - /// file if it already exists. - /// - /// The full path and file name to save the screenshot to. - public override void SaveAsFile(string fileName) - { - File.WriteAllBytes(fileName, this.AsByteArray); - } + /// + /// Saves the screenshot to a Portable Network Graphics (PNG) file, overwriting the + /// file if it already exists. + /// + /// The full path and file name to save the screenshot to. + public override void SaveAsFile(string fileName) + { + File.WriteAllBytes(fileName, this.AsByteArray); } } diff --git a/dotnet/src/webdriver/SeleniumManager.cs b/dotnet/src/webdriver/SeleniumManager.cs index ad85409425ff6..3da1cd49cf937 100644 --- a/dotnet/src/webdriver/SeleniumManager.cs +++ b/dotnet/src/webdriver/SeleniumManager.cs @@ -28,230 +28,229 @@ using System.Text.Json.Serialization; using static OpenQA.Selenium.SeleniumManagerResponse; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Wrapper for the Selenium Manager binary. +/// This implementation is still in beta, and may change. +/// +public static class SeleniumManager { - /// - /// Wrapper for the Selenium Manager binary. - /// This implementation is still in beta, and may change. - /// - public static class SeleniumManager - { - internal const string DriverPathKey = "driver_path"; - internal const string BrowserPathKey = "browser_path"; + internal const string DriverPathKey = "driver_path"; + internal const string BrowserPathKey = "browser_path"; - private static readonly ILogger _logger = Log.GetLogger(typeof(SeleniumManager)); + private static readonly ILogger _logger = Log.GetLogger(typeof(SeleniumManager)); - private static readonly Lazy _lazyBinaryFullPath = new(() => + private static readonly Lazy _lazyBinaryFullPath = new(() => + { + string? binaryFullPath = Environment.GetEnvironmentVariable("SE_MANAGER_PATH"); + if (binaryFullPath == null) { - string? binaryFullPath = Environment.GetEnvironmentVariable("SE_MANAGER_PATH"); - if (binaryFullPath == null) - { - SupportedPlatform? platform = null; + SupportedPlatform? platform = null; #if NET8_0_OR_GREATER - if (OperatingSystem.IsWindows()) - { - platform = SupportedPlatform.Windows; - } - else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) - { - platform = SupportedPlatform.Linux; - } - else if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst()) - { - platform = SupportedPlatform.MacOS; - } + if (OperatingSystem.IsWindows()) + { + platform = SupportedPlatform.Windows; + } + else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) + { + platform = SupportedPlatform.Linux; + } + else if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst()) + { + platform = SupportedPlatform.MacOS; + } #else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - platform = SupportedPlatform.Windows; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - platform = SupportedPlatform.Linux; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - platform = SupportedPlatform.MacOS; - } -#endif - - var currentDirectory = AppContext.BaseDirectory; - - binaryFullPath = platform switch - { - SupportedPlatform.Windows => Path.Combine(currentDirectory, "selenium-manager", "windows", "selenium-manager.exe"), - SupportedPlatform.Linux => Path.Combine(currentDirectory, "selenium-manager", "linux", "selenium-manager"), - SupportedPlatform.MacOS => Path.Combine(currentDirectory, "selenium-manager", "macos", "selenium-manager"), - _ => throw new PlatformNotSupportedException( - $"Selenium Manager doesn't support your runtime platform: {RuntimeInformation.OSDescription}"), - }; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + platform = SupportedPlatform.Windows; } - - if (!File.Exists(binaryFullPath)) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - throw new WebDriverException($"Unable to locate or obtain Selenium Manager binary at {binaryFullPath}"); + platform = SupportedPlatform.Linux; } - - return binaryFullPath; - }); - - /// - /// Determines the location of the browser and driver binaries. - /// - /// List of arguments to use when invoking Selenium Manager. - /// - /// An array with two entries, one for the driver path, and another one for the browser path. - /// - public static Dictionary BinaryPaths(string arguments) - { - StringBuilder argsBuilder = new StringBuilder(arguments); - argsBuilder.Append(" --language-binding csharp"); - argsBuilder.Append(" --output json"); - if (_logger.IsEnabled(LogEventLevel.Debug)) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - argsBuilder.Append(" --debug"); + platform = SupportedPlatform.MacOS; } +#endif + + var currentDirectory = AppContext.BaseDirectory; - var smCommandResult = RunCommand(_lazyBinaryFullPath.Value, argsBuilder.ToString()); - Dictionary binaryPaths = new() + binaryFullPath = platform switch { - { BrowserPathKey, smCommandResult.BrowserPath }, - { DriverPathKey, smCommandResult.DriverPath } + SupportedPlatform.Windows => Path.Combine(currentDirectory, "selenium-manager", "windows", "selenium-manager.exe"), + SupportedPlatform.Linux => Path.Combine(currentDirectory, "selenium-manager", "linux", "selenium-manager"), + SupportedPlatform.MacOS => Path.Combine(currentDirectory, "selenium-manager", "macos", "selenium-manager"), + _ => throw new PlatformNotSupportedException( + $"Selenium Manager doesn't support your runtime platform: {RuntimeInformation.OSDescription}"), }; + } - if (_logger.IsEnabled(LogEventLevel.Trace)) - { - _logger.Trace($"Driver path: {binaryPaths[DriverPathKey]}"); - _logger.Trace($"Browser path: {binaryPaths[BrowserPathKey]}"); - } + if (!File.Exists(binaryFullPath)) + { + throw new WebDriverException($"Unable to locate or obtain Selenium Manager binary at {binaryFullPath}"); + } + + return binaryFullPath; + }); - return binaryPaths; + /// + /// Determines the location of the browser and driver binaries. + /// + /// List of arguments to use when invoking Selenium Manager. + /// + /// An array with two entries, one for the driver path, and another one for the browser path. + /// + public static Dictionary BinaryPaths(string arguments) + { + StringBuilder argsBuilder = new StringBuilder(arguments); + argsBuilder.Append(" --language-binding csharp"); + argsBuilder.Append(" --output json"); + if (_logger.IsEnabled(LogEventLevel.Debug)) + { + argsBuilder.Append(" --debug"); } - /// - /// Executes a process with the given arguments. - /// - /// The path to the Selenium Manager. - /// The switches to be used by Selenium Manager. - /// - /// the standard output of the execution. - /// - private static ResultResponse RunCommand(string fileName, string arguments) + var smCommandResult = RunCommand(_lazyBinaryFullPath.Value, argsBuilder.ToString()); + Dictionary binaryPaths = new() { - Process process = new Process(); - process.StartInfo.FileName = _lazyBinaryFullPath.Value; - process.StartInfo.Arguments = arguments; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.StandardErrorEncoding = Encoding.UTF8; - process.StartInfo.StandardOutputEncoding = Encoding.UTF8; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - - StringBuilder outputBuilder = new StringBuilder(); - StringBuilder errorOutputBuilder = new StringBuilder(); - - DataReceivedEventHandler outputHandler = (sender, e) => outputBuilder.AppendLine(e.Data); - DataReceivedEventHandler errorOutputHandler = (sender, e) => errorOutputBuilder.AppendLine(e.Data); - - try - { - process.OutputDataReceived += outputHandler; - process.ErrorDataReceived += errorOutputHandler; + { BrowserPathKey, smCommandResult.BrowserPath }, + { DriverPathKey, smCommandResult.DriverPath } + }; - process.Start(); + if (_logger.IsEnabled(LogEventLevel.Trace)) + { + _logger.Trace($"Driver path: {binaryPaths[DriverPathKey]}"); + _logger.Trace($"Browser path: {binaryPaths[BrowserPathKey]}"); + } + + return binaryPaths; + } + + /// + /// Executes a process with the given arguments. + /// + /// The path to the Selenium Manager. + /// The switches to be used by Selenium Manager. + /// + /// the standard output of the execution. + /// + private static ResultResponse RunCommand(string fileName, string arguments) + { + Process process = new Process(); + process.StartInfo.FileName = _lazyBinaryFullPath.Value; + process.StartInfo.Arguments = arguments; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.StandardErrorEncoding = Encoding.UTF8; + process.StartInfo.StandardOutputEncoding = Encoding.UTF8; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + StringBuilder outputBuilder = new StringBuilder(); + StringBuilder errorOutputBuilder = new StringBuilder(); + + DataReceivedEventHandler outputHandler = (sender, e) => outputBuilder.AppendLine(e.Data); + DataReceivedEventHandler errorOutputHandler = (sender, e) => errorOutputBuilder.AppendLine(e.Data); + + try + { + process.OutputDataReceived += outputHandler; + process.ErrorDataReceived += errorOutputHandler; - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + process.Start(); - process.WaitForExit(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + process.WaitForExit(); + + if (process.ExitCode != 0) + { + // We do not log any warnings coming from Selenium Manager like the other bindings, as we don't have any logging in the .NET bindings - if (process.ExitCode != 0) + var exceptionMessageBuilder = new StringBuilder($"Selenium Manager process exited abnormally with {process.ExitCode} code: {fileName} {arguments}"); + + if (!string.IsNullOrWhiteSpace(errorOutputBuilder.ToString())) { - // We do not log any warnings coming from Selenium Manager like the other bindings, as we don't have any logging in the .NET bindings - - var exceptionMessageBuilder = new StringBuilder($"Selenium Manager process exited abnormally with {process.ExitCode} code: {fileName} {arguments}"); - - if (!string.IsNullOrWhiteSpace(errorOutputBuilder.ToString())) - { - exceptionMessageBuilder.AppendLine(); - exceptionMessageBuilder.AppendLine("Error Output >>"); - exceptionMessageBuilder.Append(errorOutputBuilder); - exceptionMessageBuilder.AppendLine("<<"); - } - - if (!string.IsNullOrWhiteSpace(outputBuilder.ToString())) - { - exceptionMessageBuilder.AppendLine(); - exceptionMessageBuilder.AppendLine("Standard Output >>"); - exceptionMessageBuilder.Append(outputBuilder); - exceptionMessageBuilder.AppendLine("<<"); - } - - throw new WebDriverException(exceptionMessageBuilder.ToString()); + exceptionMessageBuilder.AppendLine(); + exceptionMessageBuilder.AppendLine("Error Output >>"); + exceptionMessageBuilder.Append(errorOutputBuilder); + exceptionMessageBuilder.AppendLine("<<"); } + + if (!string.IsNullOrWhiteSpace(outputBuilder.ToString())) + { + exceptionMessageBuilder.AppendLine(); + exceptionMessageBuilder.AppendLine("Standard Output >>"); + exceptionMessageBuilder.Append(outputBuilder); + exceptionMessageBuilder.AppendLine("<<"); + } + + throw new WebDriverException(exceptionMessageBuilder.ToString()); } - catch (Exception ex) - { - throw new WebDriverException($"Error starting process: {fileName} {arguments}", ex); - } - finally - { - process.OutputDataReceived -= outputHandler; - process.ErrorDataReceived -= errorOutputHandler; - } + } + catch (Exception ex) + { + throw new WebDriverException($"Error starting process: {fileName} {arguments}", ex); + } + finally + { + process.OutputDataReceived -= outputHandler; + process.ErrorDataReceived -= errorOutputHandler; + } - string output = outputBuilder.ToString().Trim(); + string output = outputBuilder.ToString().Trim(); - SeleniumManagerResponse jsonResponse; + SeleniumManagerResponse jsonResponse; - try - { - jsonResponse = JsonSerializer.Deserialize(output, SeleniumManagerSerializerContext.Default.SeleniumManagerResponse)!; - } - catch (Exception ex) - { - throw new WebDriverException($"Error deserializing Selenium Manager's response: {output}", ex); - } + try + { + jsonResponse = JsonSerializer.Deserialize(output, SeleniumManagerSerializerContext.Default.SeleniumManagerResponse)!; + } + catch (Exception ex) + { + throw new WebDriverException($"Error deserializing Selenium Manager's response: {output}", ex); + } - if (jsonResponse.Logs is not null) + if (jsonResponse.Logs is not null) + { + // Treat SM's logs always as Trace to avoid SM writing at Info level + if (_logger.IsEnabled(LogEventLevel.Trace)) { - // Treat SM's logs always as Trace to avoid SM writing at Info level - if (_logger.IsEnabled(LogEventLevel.Trace)) + foreach (var entry in jsonResponse.Logs) { - foreach (var entry in jsonResponse.Logs) - { - _logger.Trace($"{entry.Level} {entry.Message}"); - } + _logger.Trace($"{entry.Level} {entry.Message}"); } } - - return jsonResponse.Result; } - } - internal sealed record SeleniumManagerResponse(IReadOnlyList Logs, ResultResponse Result) - { - public sealed record LogEntryResponse(string Level, string Message); - - public sealed record ResultResponse - ( - [property: JsonPropertyName(SeleniumManager.DriverPathKey)] - string DriverPath, - [property: JsonPropertyName(SeleniumManager.BrowserPathKey)] - string BrowserPath - ); + return jsonResponse.Result; } +} - [JsonSerializable(typeof(SeleniumManagerResponse))] - [JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)] - internal sealed partial class SeleniumManagerSerializerContext : JsonSerializerContext; +internal sealed record SeleniumManagerResponse(IReadOnlyList Logs, ResultResponse Result) +{ + public sealed record LogEntryResponse(string Level, string Message); + + public sealed record ResultResponse + ( + [property: JsonPropertyName(SeleniumManager.DriverPathKey)] + string DriverPath, + [property: JsonPropertyName(SeleniumManager.BrowserPathKey)] + string BrowserPath + ); +} - internal enum SupportedPlatform - { - Windows, - Linux, - MacOS - } +[JsonSerializable(typeof(SeleniumManagerResponse))] +[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)] +internal sealed partial class SeleniumManagerSerializerContext : JsonSerializerContext; + +internal enum SupportedPlatform +{ + Windows, + Linux, + MacOS } diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index b6f999032966e..dc94a67823fc5 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -19,51 +19,50 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for maintaining a session for a test +/// +public class SessionId { + private readonly string sessionOpaqueKey; + /// - /// Provides a mechanism for maintaining a session for a test + /// Initializes a new instance of the class /// - public class SessionId + /// Key for the session in use + /// If is . + public SessionId(string opaqueKey) { - private readonly string sessionOpaqueKey; - - /// - /// Initializes a new instance of the class - /// - /// Key for the session in use - /// If is . - public SessionId(string opaqueKey) - { - this.sessionOpaqueKey = opaqueKey ?? throw new ArgumentNullException(nameof(opaqueKey)); - } + this.sessionOpaqueKey = opaqueKey ?? throw new ArgumentNullException(nameof(opaqueKey)); + } - /// - /// Get the value of the key - /// - /// The key in use - public override string ToString() - { - return this.sessionOpaqueKey; - } + /// + /// Get the value of the key + /// + /// The key in use + public override string ToString() + { + return this.sessionOpaqueKey; + } - /// - /// Get the hash code of the key - /// - /// The hash code of the key - public override int GetHashCode() - { - return this.sessionOpaqueKey.GetHashCode(); - } + /// + /// Get the hash code of the key + /// + /// The hash code of the key + public override int GetHashCode() + { + return this.sessionOpaqueKey.GetHashCode(); + } - /// - /// Indicates whether the current session ID value is the same as . - /// - /// The session to compare to. - /// if the values are equal; otherwise, . - public override bool Equals(object? obj) - { - return obj is SessionId otherSession && this.sessionOpaqueKey.Equals(otherSession.sessionOpaqueKey); - } + /// + /// Indicates whether the current session ID value is the same as . + /// + /// The session to compare to. + /// if the values are equal; otherwise, . + public override bool Equals(object? obj) + { + return obj is SessionId otherSession && this.sessionOpaqueKey.Equals(otherSession.sessionOpaqueKey); } } diff --git a/dotnet/src/webdriver/ShadowRoot.cs b/dotnet/src/webdriver/ShadowRoot.cs index 8e2e3ea90dc93..b40b3576fe659 100644 --- a/dotnet/src/webdriver/ShadowRoot.cs +++ b/dotnet/src/webdriver/ShadowRoot.cs @@ -23,113 +23,112 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a representation of an element's shadow root. +/// +public class ShadowRoot : ISearchContext, IWrapsDriver, IWebDriverObjectReference { /// - /// Provides a representation of an element's shadow root. + /// The property name that represents an element shadow root in the wire protocol. + /// + public const string ShadowRootReferencePropertyName = "shadow-6066-11e4-a52e-4f735466cecf"; + + private readonly WebDriver driver; + private readonly string shadowRootId; + + /// + /// Initializes a new instance of the class. /// - public class ShadowRoot : ISearchContext, IWrapsDriver, IWebDriverObjectReference + /// The instance that is driving this shadow root. + /// The ID value provided to identify the shadow root. + /// If or are . + public ShadowRoot(WebDriver parentDriver, string id) { - /// - /// The property name that represents an element shadow root in the wire protocol. - /// - public const string ShadowRootReferencePropertyName = "shadow-6066-11e4-a52e-4f735466cecf"; - - private readonly WebDriver driver; - private readonly string shadowRootId; - - /// - /// Initializes a new instance of the class. - /// - /// The instance that is driving this shadow root. - /// The ID value provided to identify the shadow root. - /// If or are . - public ShadowRoot(WebDriver parentDriver, string id) - { - this.driver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); - this.shadowRootId = id ?? throw new ArgumentNullException(nameof(id)); - } + this.driver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + this.shadowRootId = id ?? throw new ArgumentNullException(nameof(id)); + } - /// - /// Gets the driving this shadow root. - /// - public IWebDriver WrappedDriver => this.driver; + /// + /// Gets the driving this shadow root. + /// + public IWebDriver WrappedDriver => this.driver; - /// - /// Gets the internal ID for this ShadowRoot. - /// - string IWebDriverObjectReference.ObjectReferenceId => this.shadowRootId; + /// + /// Gets the internal ID for this ShadowRoot. + /// + string IWebDriverObjectReference.ObjectReferenceId => this.shadowRootId; - internal static bool TryCreate(WebDriver parentDriver, Dictionary shadowRootDictionary, [NotNullWhen(true)] out ShadowRoot? shadowRoot) + internal static bool TryCreate(WebDriver parentDriver, Dictionary shadowRootDictionary, [NotNullWhen(true)] out ShadowRoot? shadowRoot) + { + if (shadowRootDictionary is null) { - if (shadowRootDictionary is null) - { - throw new ArgumentNullException(nameof(shadowRootDictionary), "The dictionary containing the shadow root reference cannot be null"); - } - - if (shadowRootDictionary.TryGetValue(ShadowRootReferencePropertyName, out object? shadowRootValue)) - { - shadowRoot = new ShadowRoot(parentDriver, shadowRootValue?.ToString()!); - return true; - } - - shadowRoot = null; - return false; + throw new ArgumentNullException(nameof(shadowRootDictionary), "The dictionary containing the shadow root reference cannot be null"); } - /// - /// Finds the first using the given method. - /// - /// The locating mechanism to use. - /// The first matching on the current context. - /// If is . - /// If no element matches the criteria. - public IWebElement FindElement(By by) + if (shadowRootDictionary.TryGetValue(ShadowRootReferencePropertyName, out object? shadowRootValue)) { - if (by is null) - { - throw new ArgumentNullException(nameof(by), "by cannot be null"); - } - - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.shadowRootId); - parameters.Add("using", by.Mechanism); - parameters.Add("value", by.Criteria); - - Response commandResponse = this.driver.Execute(DriverCommand.FindShadowChildElement, parameters); - return this.driver.GetElementFromResponse(commandResponse)!; + shadowRoot = new ShadowRoot(parentDriver, shadowRootValue?.ToString()!); + return true; } - /// - /// Finds all IWebElements within the current context - /// using the given mechanism. - /// - /// The locating mechanism to use. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - /// If is . - public ReadOnlyCollection FindElements(By by) + shadowRoot = null; + return false; + } + + /// + /// Finds the first using the given method. + /// + /// The locating mechanism to use. + /// The first matching on the current context. + /// If is . + /// If no element matches the criteria. + public IWebElement FindElement(By by) + { + if (by is null) { - if (by is null) - { - throw new ArgumentNullException(nameof(by), "by cannot be null"); - } - - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.shadowRootId); - parameters.Add("using", by.Mechanism); - parameters.Add("value", by.Criteria); - - Response commandResponse = this.driver.Execute(DriverCommand.FindShadowChildElements, parameters); - return this.driver.GetElementsFromResponse(commandResponse); + throw new ArgumentNullException(nameof(by), "by cannot be null"); } - Dictionary IWebDriverObjectReference.ToDictionary() + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.shadowRootId); + parameters.Add("using", by.Mechanism); + parameters.Add("value", by.Criteria); + + Response commandResponse = this.driver.Execute(DriverCommand.FindShadowChildElement, parameters); + return this.driver.GetElementFromResponse(commandResponse)!; + } + + /// + /// Finds all IWebElements within the current context + /// using the given mechanism. + /// + /// The locating mechanism to use. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + /// If is . + public ReadOnlyCollection FindElements(By by) + { + if (by is null) { - return new Dictionary - { - [ShadowRootReferencePropertyName] = this.shadowRootId - }; + throw new ArgumentNullException(nameof(by), "by cannot be null"); } + + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.shadowRootId); + parameters.Add("using", by.Mechanism); + parameters.Add("value", by.Criteria); + + Response commandResponse = this.driver.Execute(DriverCommand.FindShadowChildElements, parameters); + return this.driver.GetElementsFromResponse(commandResponse); + } + + Dictionary IWebDriverObjectReference.ToDictionary() + { + return new Dictionary + { + [ShadowRootReferencePropertyName] = this.shadowRootId + }; } } diff --git a/dotnet/src/webdriver/StackTraceElement.cs b/dotnet/src/webdriver/StackTraceElement.cs index d4e4995dad46f..8c4899f8e0694 100644 --- a/dotnet/src/webdriver/StackTraceElement.cs +++ b/dotnet/src/webdriver/StackTraceElement.cs @@ -21,117 +21,116 @@ using System.Globalization; using System.Text.Json.Serialization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Gives properties to get a stack trace +/// +public class StackTraceElement { + private string fileName = string.Empty; + private string className = string.Empty; + private int lineNumber; + private string methodName = string.Empty; + /// - /// Gives properties to get a stack trace + /// Initializes a new instance of the class. /// - public class StackTraceElement + public StackTraceElement() { - private string fileName = string.Empty; - private string className = string.Empty; - private int lineNumber; - private string methodName = string.Empty; - - /// - /// Initializes a new instance of the class. - /// - public StackTraceElement() - { - } + } - /// - /// Initializes a new instance of the class using the given property values. - /// - /// A containing the names and values for the properties of this . - public StackTraceElement(Dictionary? elementAttributes) + /// + /// Initializes a new instance of the class using the given property values. + /// + /// A containing the names and values for the properties of this . + public StackTraceElement(Dictionary? elementAttributes) + { + if (elementAttributes != null) { - if (elementAttributes != null) + if (elementAttributes.TryGetValue("className", out object? classNameObj)) { - if (elementAttributes.TryGetValue("className", out object? classNameObj)) + string? className = classNameObj?.ToString(); + if (className is not null) { - string? className = classNameObj?.ToString(); - if (className is not null) - { - this.className = className; - } + this.className = className; } + } - if (elementAttributes.TryGetValue("methodName", out object? methodNameObj)) + if (elementAttributes.TryGetValue("methodName", out object? methodNameObj)) + { + string? methodName = methodNameObj?.ToString(); + if (methodName is not null) { - string? methodName = methodNameObj?.ToString(); - if (methodName is not null) - { - this.methodName = methodName; - } + this.methodName = methodName; } + } - if (elementAttributes.TryGetValue("lineNumber", out object? lineNumberObj)) + if (elementAttributes.TryGetValue("lineNumber", out object? lineNumberObj)) + { + if (int.TryParse(lineNumberObj?.ToString(), out int line)) { - if (int.TryParse(lineNumberObj?.ToString(), out int line)) - { - this.lineNumber = line; - } + this.lineNumber = line; } + } - if (elementAttributes.TryGetValue("fileName", out object? fileNameObj)) + if (elementAttributes.TryGetValue("fileName", out object? fileNameObj)) + { + string? fileName = fileNameObj?.ToString(); + if (fileName is not null) { - string? fileName = fileNameObj?.ToString(); - if (fileName is not null) - { - this.fileName = fileName; - } + this.fileName = fileName; } } } + } - /// - /// Gets or sets the value of the filename in the stack - /// - [JsonPropertyName("fileName")] - public string FileName - { - get { return this.fileName; } - set { this.fileName = value; } - } + /// + /// Gets or sets the value of the filename in the stack + /// + [JsonPropertyName("fileName")] + public string FileName + { + get { return this.fileName; } + set { this.fileName = value; } + } - /// - /// Gets or sets the value of the Class name in the stack trace - /// - [JsonPropertyName("className")] - public string ClassName - { - get { return this.className; } - set { this.className = value; } - } + /// + /// Gets or sets the value of the Class name in the stack trace + /// + [JsonPropertyName("className")] + public string ClassName + { + get { return this.className; } + set { this.className = value; } + } - /// - /// Gets or sets the line number - /// - [JsonPropertyName("lineNumber")] - public int LineNumber - { - get { return this.lineNumber; } - set { this.lineNumber = value; } - } + /// + /// Gets or sets the line number + /// + [JsonPropertyName("lineNumber")] + public int LineNumber + { + get { return this.lineNumber; } + set { this.lineNumber = value; } + } - /// - /// Gets or sets the Method name in the stack trace - /// - [JsonPropertyName("methodName")] - public string MethodName - { - get { return this.methodName; } - set { this.methodName = value; } - } + /// + /// Gets or sets the Method name in the stack trace + /// + [JsonPropertyName("methodName")] + public string MethodName + { + get { return this.methodName; } + set { this.methodName = value; } + } - /// - /// Gets a string representation of the object. - /// - /// A string representation of the object. - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "at {0}.{1} ({2}, {3})", this.className, this.methodName, this.fileName, this.lineNumber); - } + /// + /// Gets a string representation of the object. + /// + /// A string representation of the object. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "at {0}.{1} ({2}, {3})", this.className, this.methodName, this.fileName, this.lineNumber); } } diff --git a/dotnet/src/webdriver/StaleElementReferenceException.cs b/dotnet/src/webdriver/StaleElementReferenceException.cs index 41dc01c4f53e6..2cf7a4dd717a7 100644 --- a/dotnet/src/webdriver/StaleElementReferenceException.cs +++ b/dotnet/src/webdriver/StaleElementReferenceException.cs @@ -19,58 +19,57 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when a reference to an element is no longer valid. +/// +[Serializable] +public class StaleElementReferenceException : WebDriverException { /// - /// The exception that is thrown when a reference to an element is no longer valid. + /// Link to the documentation for this error /// - [Serializable] - public class StaleElementReferenceException : WebDriverException - { - /// - /// Link to the documentation for this error - /// - private static string supportUrl = baseSupportUrl + "#stale-element-reference-exception"; + private static string supportUrl = baseSupportUrl + "#stale-element-reference-exception"; - /// - /// Initializes a new instance of the class. - /// - public StaleElementReferenceException() - : base(GetMessage("")) - { - } + /// + /// Initializes a new instance of the class. + /// + public StaleElementReferenceException() + : base(GetMessage("")) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public StaleElementReferenceException(string? message) - : base(GetMessage(message)) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public StaleElementReferenceException(string? message) + : base(GetMessage(message)) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public StaleElementReferenceException(string? message, Exception? innerException) - : base(GetMessage(message), innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public StaleElementReferenceException(string? message, Exception? innerException) + : base(GetMessage(message), innerException) + { + } - /// - /// Add information about obtaining additional support from documentation to this exception. - /// - /// The original message for exception - /// The final message for exception - protected static string GetMessage(string? message) - { - return $"{message}; {supportMsg}{supportUrl}"; - } + /// + /// Add information about obtaining additional support from documentation to this exception. + /// + /// The original message for exception + /// The final message for exception + protected static string GetMessage(string? message) + { + return $"{message}; {supportMsg}{supportUrl}"; } } diff --git a/dotnet/src/webdriver/Support/DefaultWait{T}.cs b/dotnet/src/webdriver/Support/DefaultWait{T}.cs index 50242c4b5c580..2520980ddac56 100644 --- a/dotnet/src/webdriver/Support/DefaultWait{T}.cs +++ b/dotnet/src/webdriver/Support/DefaultWait{T}.cs @@ -24,195 +24,194 @@ using System.Linq; using System.Threading; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// An implementation of the interface that may have its timeout and polling interval +/// configured on the fly. +/// +/// The type of object on which the wait it to be applied. +public class DefaultWait : IWait { + private readonly T input; + private readonly IClock clock; + private readonly List ignoredExceptions = new List(); + /// - /// An implementation of the interface that may have its timeout and polling interval - /// configured on the fly. + /// Initializes a new instance of the class. /// - /// The type of object on which the wait it to be applied. - public class DefaultWait : IWait + /// The input value to pass to the evaluated conditions. + public DefaultWait(T input) + : this(input, SystemClock.Instance) { - private readonly T input; - private readonly IClock clock; - private readonly List ignoredExceptions = new List(); - - /// - /// Initializes a new instance of the class. - /// - /// The input value to pass to the evaluated conditions. - public DefaultWait(T input) - : this(input, SystemClock.Instance) - { - } + } + + /// + /// Initializes a new instance of the class. + /// + /// The input value to pass to the evaluated conditions. + /// The clock to use when measuring the timeout. + /// If or are . + public DefaultWait(T input, IClock clock) + { + this.input = input ?? throw new ArgumentNullException(nameof(input), "input cannot be null"); ; + this.clock = clock ?? throw new ArgumentNullException(nameof(clock), "clock cannot be null"); ; + } + + /// + /// Gets or sets how long to wait for the evaluated condition to be true. The default timeout is 500 milliseconds. + /// + public TimeSpan Timeout { get; set; } = DefaultSleepTimeout; + + /// + /// Gets or sets how often the condition should be evaluated. The default timeout is 500 milliseconds. + /// + public TimeSpan PollingInterval { get; set; } = DefaultSleepTimeout; + + /// + /// Gets or sets the message to be displayed when time expires. + /// + public string Message { get; set; } = string.Empty; + + private static TimeSpan DefaultSleepTimeout => TimeSpan.FromMilliseconds(500); - /// - /// Initializes a new instance of the class. - /// - /// The input value to pass to the evaluated conditions. - /// The clock to use when measuring the timeout. - /// If or are . - public DefaultWait(T input, IClock clock) + /// + /// Configures this instance to ignore specific types of exceptions while waiting for a condition. + /// Any exceptions not whitelisted will be allowed to propagate, terminating the wait. + /// + /// The types of exceptions to ignore. + public void IgnoreExceptionTypes(params Type[] exceptionTypes) + { + if (exceptionTypes == null) { - this.input = input ?? throw new ArgumentNullException(nameof(input), "input cannot be null"); ; - this.clock = clock ?? throw new ArgumentNullException(nameof(clock), "clock cannot be null"); ; + throw new ArgumentNullException(nameof(exceptionTypes), "exceptionTypes cannot be null"); } - /// - /// Gets or sets how long to wait for the evaluated condition to be true. The default timeout is 500 milliseconds. - /// - public TimeSpan Timeout { get; set; } = DefaultSleepTimeout; - - /// - /// Gets or sets how often the condition should be evaluated. The default timeout is 500 milliseconds. - /// - public TimeSpan PollingInterval { get; set; } = DefaultSleepTimeout; - - /// - /// Gets or sets the message to be displayed when time expires. - /// - public string Message { get; set; } = string.Empty; - - private static TimeSpan DefaultSleepTimeout => TimeSpan.FromMilliseconds(500); - - /// - /// Configures this instance to ignore specific types of exceptions while waiting for a condition. - /// Any exceptions not whitelisted will be allowed to propagate, terminating the wait. - /// - /// The types of exceptions to ignore. - public void IgnoreExceptionTypes(params Type[] exceptionTypes) + foreach (Type exceptionType in exceptionTypes) { - if (exceptionTypes == null) + if (!typeof(Exception).IsAssignableFrom(exceptionType)) { - throw new ArgumentNullException(nameof(exceptionTypes), "exceptionTypes cannot be null"); + throw new ArgumentException("All types to be ignored must derive from System.Exception", nameof(exceptionTypes)); } + } - foreach (Type exceptionType in exceptionTypes) - { - if (!typeof(Exception).IsAssignableFrom(exceptionType)) - { - throw new ArgumentException("All types to be ignored must derive from System.Exception", nameof(exceptionTypes)); - } - } + this.ignoredExceptions.AddRange(exceptionTypes); + } - this.ignoredExceptions.AddRange(exceptionTypes); - } + /// + /// Repeatedly applies this instance's input value to the given function until one of the following + /// occurs: + /// + /// + /// the function returns neither null nor false + /// the function throws an exception that is not in the list of ignored exception types + /// the timeout expires + /// + /// + /// + /// The delegate's expected return type. + /// A delegate taking an object of type T as its parameter, and returning a TResult. + /// The delegate's return value. + [return: NotNull] + public virtual TResult Until(Func condition) + { + return Until(condition, CancellationToken.None); + } - /// - /// Repeatedly applies this instance's input value to the given function until one of the following - /// occurs: - /// - /// - /// the function returns neither null nor false - /// the function throws an exception that is not in the list of ignored exception types - /// the timeout expires - /// - /// - /// - /// The delegate's expected return type. - /// A delegate taking an object of type T as its parameter, and returning a TResult. - /// The delegate's return value. - [return: NotNull] - public virtual TResult Until(Func condition) + /// + /// Repeatedly applies this instance's input value to the given function until one of the following + /// occurs: + /// + /// + /// the function returns neither null nor false + /// the function throws an exception that is not in the list of ignored exception types + /// the timeout expires + /// + /// + /// + /// The delegate's expected return type. + /// A delegate taking an object of type T as its parameter, and returning a TResult. + /// A cancellation token that can be used to cancel the wait. + /// The delegate's return value. + [return: NotNull] + public virtual TResult Until(Func condition, CancellationToken token) + { + if (condition == null) { - return Until(condition, CancellationToken.None); + throw new ArgumentNullException(nameof(condition), "condition cannot be null"); } - /// - /// Repeatedly applies this instance's input value to the given function until one of the following - /// occurs: - /// - /// - /// the function returns neither null nor false - /// the function throws an exception that is not in the list of ignored exception types - /// the timeout expires - /// - /// - /// - /// The delegate's expected return type. - /// A delegate taking an object of type T as its parameter, and returning a TResult. - /// A cancellation token that can be used to cancel the wait. - /// The delegate's return value. - [return: NotNull] - public virtual TResult Until(Func condition, CancellationToken token) + var resultType = typeof(TResult); + if ((resultType.IsValueType && resultType != typeof(bool)) || !typeof(object).IsAssignableFrom(resultType)) { - if (condition == null) - { - throw new ArgumentNullException(nameof(condition), "condition cannot be null"); - } + throw new ArgumentException($"Can only wait on an object or boolean response, tried to use type: {resultType}", nameof(condition)); + } - var resultType = typeof(TResult); - if ((resultType.IsValueType && resultType != typeof(bool)) || !typeof(object).IsAssignableFrom(resultType)) - { - throw new ArgumentException($"Can only wait on an object or boolean response, tried to use type: {resultType}", nameof(condition)); - } + Exception? lastException = null; + var endTime = this.clock.LaterBy(this.Timeout); + while (true) + { + token.ThrowIfCancellationRequested(); - Exception? lastException = null; - var endTime = this.clock.LaterBy(this.Timeout); - while (true) + try { - token.ThrowIfCancellationRequested(); - - try + var result = condition(this.input); + if (resultType == typeof(bool)) { - var result = condition(this.input); - if (resultType == typeof(bool)) + if (result is true) { - if (result is true) - { - return result; - } - } - else - { - if (result != null) - { - return result; - } + return result; } } - catch (Exception ex) + else { - if (!this.IsIgnoredException(ex)) + if (result != null) { - throw; + return result; } - - lastException = ex; } - - // Check the timeout after evaluating the function to ensure conditions - // with a zero timeout can succeed. - if (!this.clock.IsNowBefore(endTime)) + } + catch (Exception ex) + { + if (!this.IsIgnoredException(ex)) { - string timeoutMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds", this.Timeout.TotalSeconds); - if (!string.IsNullOrEmpty(this.Message)) - { - timeoutMessage += ": " + this.Message; - } + throw; + } + + lastException = ex; + } - this.ThrowTimeoutException(timeoutMessage, lastException); + // Check the timeout after evaluating the function to ensure conditions + // with a zero timeout can succeed. + if (!this.clock.IsNowBefore(endTime)) + { + string timeoutMessage = string.Format(CultureInfo.InvariantCulture, "Timed out after {0} seconds", this.Timeout.TotalSeconds); + if (!string.IsNullOrEmpty(this.Message)) + { + timeoutMessage += ": " + this.Message; } - Thread.Sleep(this.PollingInterval); + this.ThrowTimeoutException(timeoutMessage, lastException); } - } - /// - /// Throws a with the given message. - /// - /// The message of the exception. - /// The last exception thrown by the condition. - /// This method may be overridden to throw an exception that is - /// idiomatic for a particular test infrastructure. - protected virtual void ThrowTimeoutException(string exceptionMessage, Exception? lastException) - { - throw new WebDriverTimeoutException(exceptionMessage, lastException); + Thread.Sleep(this.PollingInterval); } + } - private bool IsIgnoredException(Exception exception) - { - return this.ignoredExceptions.Any(type => type.IsAssignableFrom(exception.GetType())); - } + /// + /// Throws a with the given message. + /// + /// The message of the exception. + /// The last exception thrown by the condition. + /// This method may be overridden to throw an exception that is + /// idiomatic for a particular test infrastructure. + protected virtual void ThrowTimeoutException(string exceptionMessage, Exception? lastException) + { + throw new WebDriverTimeoutException(exceptionMessage, lastException); + } + + private bool IsIgnoredException(Exception exception) + { + return this.ignoredExceptions.Any(type => type.IsAssignableFrom(exception.GetType())); } } diff --git a/dotnet/src/webdriver/Support/IClock.cs b/dotnet/src/webdriver/Support/IClock.cs index ceb2dab588f04..fcb8821bf6378 100644 --- a/dotnet/src/webdriver/Support/IClock.cs +++ b/dotnet/src/webdriver/Support/IClock.cs @@ -19,30 +19,29 @@ using System; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// An interface describing time handling functions for timeouts. +/// +public interface IClock { /// - /// An interface describing time handling functions for timeouts. + /// Gets the current date and time values. /// - public interface IClock - { - /// - /// Gets the current date and time values. - /// - DateTime Now { get; } + DateTime Now { get; } - /// - /// Gets the at a specified offset in the future. - /// - /// The offset to use. - /// The at the specified offset in the future. - DateTime LaterBy(TimeSpan delay); + /// + /// Gets the at a specified offset in the future. + /// + /// The offset to use. + /// The at the specified offset in the future. + DateTime LaterBy(TimeSpan delay); - /// - /// Gets a value indicating whether the current date and time is before the specified date and time. - /// - /// The date and time values to compare the current date and time values to. - /// if the current date and time is before the specified date and time; otherwise, . - bool IsNowBefore(DateTime otherDateTime); - } + /// + /// Gets a value indicating whether the current date and time is before the specified date and time. + /// + /// The date and time values to compare the current date and time values to. + /// if the current date and time is before the specified date and time; otherwise, . + bool IsNowBefore(DateTime otherDateTime); } diff --git a/dotnet/src/webdriver/Support/IWait{T}.cs b/dotnet/src/webdriver/Support/IWait{T}.cs index 8ca49d1c93299..1ecd7849c228f 100644 --- a/dotnet/src/webdriver/Support/IWait{T}.cs +++ b/dotnet/src/webdriver/Support/IWait{T}.cs @@ -20,45 +20,44 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Interface describing a class designed to wait for a condition. +/// +/// The type of object used to detect the condition. +public interface IWait { /// - /// Interface describing a class designed to wait for a condition. + /// Gets or sets how long to wait for the evaluated condition to be true. /// - /// The type of object used to detect the condition. - public interface IWait - { - /// - /// Gets or sets how long to wait for the evaluated condition to be true. - /// - TimeSpan Timeout { get; set; } + TimeSpan Timeout { get; set; } - /// - /// Gets or sets how often the condition should be evaluated. - /// - TimeSpan PollingInterval { get; set; } + /// + /// Gets or sets how often the condition should be evaluated. + /// + TimeSpan PollingInterval { get; set; } - /// - /// Gets or sets the message to be displayed when time expires. - /// - string Message { get; set; } + /// + /// Gets or sets the message to be displayed when time expires. + /// + string Message { get; set; } - /// - /// Configures this instance to ignore specific types of exceptions while waiting for a condition. - /// Any exceptions not whitelisted will be allowed to propagate, terminating the wait. - /// - /// The types of exceptions to ignore. - void IgnoreExceptionTypes(params Type[] exceptionTypes); + /// + /// Configures this instance to ignore specific types of exceptions while waiting for a condition. + /// Any exceptions not whitelisted will be allowed to propagate, terminating the wait. + /// + /// The types of exceptions to ignore. + void IgnoreExceptionTypes(params Type[] exceptionTypes); - /// - /// Waits until a condition is true or times out. - /// - /// The type of result to expect from the condition. - /// A delegate taking a TSource as its parameter, and returning a TResult. - /// If TResult is a boolean, the method returns when the condition is true, and otherwise. - /// If TResult is an object, the method returns the object when the condition evaluates to a value other than . - /// Thrown when TResult is not boolean or an object type. - [return: NotNull] - TResult Until(Func condition); - } + /// + /// Waits until a condition is true or times out. + /// + /// The type of result to expect from the condition. + /// A delegate taking a TSource as its parameter, and returning a TResult. + /// If TResult is a boolean, the method returns when the condition is true, and otherwise. + /// If TResult is an object, the method returns the object when the condition evaluates to a value other than . + /// Thrown when TResult is not boolean or an object type. + [return: NotNull] + TResult Until(Func condition); } diff --git a/dotnet/src/webdriver/Support/SystemClock.cs b/dotnet/src/webdriver/Support/SystemClock.cs index 427b2d00cd626..d44fa8f64ed8f 100644 --- a/dotnet/src/webdriver/Support/SystemClock.cs +++ b/dotnet/src/webdriver/Support/SystemClock.cs @@ -19,35 +19,34 @@ using System; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Uses the system clock to calculate time for timeouts. +/// +public class SystemClock : IClock { /// - /// Uses the system clock to calculate time for timeouts. + /// An instance of the type. /// - public class SystemClock : IClock - { - /// - /// An instance of the type. - /// - public static SystemClock Instance { get; } = new(); + public static SystemClock Instance { get; } = new(); - /// - /// Gets the current date and time values. - /// - public DateTime Now => DateTime.Now; + /// + /// Gets the current date and time values. + /// + public DateTime Now => DateTime.Now; - /// - /// Calculates the date and time values after a specific delay. - /// - /// The delay after to calculate. - /// The future date and time values. - public DateTime LaterBy(TimeSpan delay) => DateTime.Now.Add(delay); + /// + /// Calculates the date and time values after a specific delay. + /// + /// The delay after to calculate. + /// The future date and time values. + public DateTime LaterBy(TimeSpan delay) => DateTime.Now.Add(delay); - /// - /// Gets a value indicating whether the current date and time is before the specified date and time. - /// - /// The date and time values to compare the current date and time values to. - /// if the current date and time is before the specified date and time; otherwise, . - public bool IsNowBefore(DateTime otherDateTime) => DateTime.Now < otherDateTime; - } + /// + /// Gets a value indicating whether the current date and time is before the specified date and time. + /// + /// The date and time values to compare the current date and time values to. + /// if the current date and time is before the specified date and time; otherwise, . + public bool IsNowBefore(DateTime otherDateTime) => DateTime.Now < otherDateTime; } diff --git a/dotnet/src/webdriver/Support/WebDriverWait.cs b/dotnet/src/webdriver/Support/WebDriverWait.cs index dd52454f11c65..60f02765bab57 100644 --- a/dotnet/src/webdriver/Support/WebDriverWait.cs +++ b/dotnet/src/webdriver/Support/WebDriverWait.cs @@ -19,45 +19,44 @@ using System; -namespace OpenQA.Selenium.Support.UI +namespace OpenQA.Selenium.Support.UI; + +/// +/// Provides the ability to wait for an arbitrary condition during test execution. +/// +/// +/// +/// IWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3)) +/// IWebElement element = wait.Until(driver => driver.FindElement(By.Name("q"))); +/// +/// +public class WebDriverWait : DefaultWait { /// - /// Provides the ability to wait for an arbitrary condition during test execution. + /// Initializes a new instance of the class. /// - /// - /// - /// IWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3)) - /// IWebElement element = wait.Until(driver => driver.FindElement(By.Name("q"))); - /// - /// - public class WebDriverWait : DefaultWait + /// The WebDriver instance used to wait. + /// The timeout value indicating how long to wait for the condition. + public WebDriverWait(IWebDriver driver, TimeSpan timeout) + : this(SystemClock.Instance, driver, timeout, DefaultSleepTimeout) { - /// - /// Initializes a new instance of the class. - /// - /// The WebDriver instance used to wait. - /// The timeout value indicating how long to wait for the condition. - public WebDriverWait(IWebDriver driver, TimeSpan timeout) - : this(SystemClock.Instance, driver, timeout, DefaultSleepTimeout) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// An object implementing the interface used to determine when time has passed. - /// The WebDriver instance used to wait. - /// The timeout value indicating how long to wait for the condition. - /// A value indicating how often to check for the condition to be true. - /// If or are . - public WebDriverWait(IClock clock, IWebDriver driver, TimeSpan timeout, TimeSpan sleepInterval) - : base(driver, clock) - { - this.Timeout = timeout; - this.PollingInterval = sleepInterval; - this.IgnoreExceptionTypes(typeof(NotFoundException)); - } + } - private static TimeSpan DefaultSleepTimeout => TimeSpan.FromMilliseconds(500); + /// + /// Initializes a new instance of the class. + /// + /// An object implementing the interface used to determine when time has passed. + /// The WebDriver instance used to wait. + /// The timeout value indicating how long to wait for the condition. + /// A value indicating how often to check for the condition to be true. + /// If or are . + public WebDriverWait(IClock clock, IWebDriver driver, TimeSpan timeout, TimeSpan sleepInterval) + : base(driver, clock) + { + this.Timeout = timeout; + this.PollingInterval = sleepInterval; + this.IgnoreExceptionTypes(typeof(NotFoundException)); } + + private static TimeSpan DefaultSleepTimeout => TimeSpan.FromMilliseconds(500); } diff --git a/dotnet/src/webdriver/TargetLocator.cs b/dotnet/src/webdriver/TargetLocator.cs index d5e779def4f8c..263dca0fe19a7 100644 --- a/dotnet/src/webdriver/TargetLocator.cs +++ b/dotnet/src/webdriver/TargetLocator.cs @@ -23,217 +23,216 @@ using System.Collections.ObjectModel; using System.Text.RegularExpressions; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Provides a mechanism for finding elements on the page with locators. +/// +internal sealed class TargetLocator : ITargetLocator { + private readonly WebDriver driver; + /// - /// Provides a mechanism for finding elements on the page with locators. + /// Initializes a new instance of the class /// - internal sealed class TargetLocator : ITargetLocator + /// The driver that is currently in use + /// If is . + public TargetLocator(WebDriver driver) { - private readonly WebDriver driver; - - /// - /// Initializes a new instance of the class - /// - /// The driver that is currently in use - /// If is . - public TargetLocator(WebDriver driver) - { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - /// - /// Move to a different frame using its index - /// - /// The index of the - /// A WebDriver instance that is currently in use - public IWebDriver Frame(int frameIndex) + /// + /// Move to a different frame using its index + /// + /// The index of the + /// A WebDriver instance that is currently in use + public IWebDriver Frame(int frameIndex) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", frameIndex); + this.driver.Execute(DriverCommand.SwitchToFrame, parameters); + return this.driver; + } + + /// + /// Move to different frame using its name + /// + /// name of the frame + /// A WebDriver instance that is currently in use + /// If is . + public IWebDriver Frame(string frameName) + { + if (frameName == null) { - Dictionary parameters = new Dictionary(); - parameters.Add("id", frameIndex); - this.driver.Execute(DriverCommand.SwitchToFrame, parameters); - return this.driver; + throw new ArgumentNullException(nameof(frameName), "Frame name cannot be null"); } - /// - /// Move to different frame using its name - /// - /// name of the frame - /// A WebDriver instance that is currently in use - /// If is . - public IWebDriver Frame(string frameName) + string name = Regex.Replace(frameName, @"(['""\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]\(\)])", @"\$1"); + ReadOnlyCollection frameElements = this.driver.FindElements(By.CssSelector("frame[name='" + name + "'],iframe[name='" + name + "']")); + if (frameElements.Count == 0) { - if (frameName == null) - { - throw new ArgumentNullException(nameof(frameName), "Frame name cannot be null"); - } - - string name = Regex.Replace(frameName, @"(['""\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]\(\)])", @"\$1"); - ReadOnlyCollection frameElements = this.driver.FindElements(By.CssSelector("frame[name='" + name + "'],iframe[name='" + name + "']")); + frameElements = this.driver.FindElements(By.CssSelector("frame#" + name + ",iframe#" + name)); if (frameElements.Count == 0) { - frameElements = this.driver.FindElements(By.CssSelector("frame#" + name + ",iframe#" + name)); - if (frameElements.Count == 0) - { - throw new NoSuchFrameException("No frame element found with name or id " + frameName); - } + throw new NoSuchFrameException("No frame element found with name or id " + frameName); } + } + + return this.Frame(frameElements[0]); + } - return this.Frame(frameElements[0]); + /// + /// Move to a frame element. + /// + /// a previously found FRAME or IFRAME element. + /// A WebDriver instance that is currently in use. + /// If is . + /// If cannot be converted to an . + public IWebDriver Frame(IWebElement frameElement) + { + if (frameElement == null) + { + throw new ArgumentNullException(nameof(frameElement), "Frame element cannot be null"); } - /// - /// Move to a frame element. - /// - /// a previously found FRAME or IFRAME element. - /// A WebDriver instance that is currently in use. - /// If is . - /// If cannot be converted to an . - public IWebDriver Frame(IWebElement frameElement) + IWebDriverObjectReference? elementReference = frameElement as IWebDriverObjectReference; + if (elementReference == null) { - if (frameElement == null) + if (frameElement is IWrapsElement elementWrapper) { - throw new ArgumentNullException(nameof(frameElement), "Frame element cannot be null"); + elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; } + } - IWebDriverObjectReference? elementReference = frameElement as IWebDriverObjectReference; - if (elementReference == null) - { - if (frameElement is IWrapsElement elementWrapper) - { - elementReference = elementWrapper.WrappedElement as IWebDriverObjectReference; - } - } + if (elementReference == null) + { + throw new ArgumentException($"{nameof(frameElement)} cannot be converted to {nameof(IWebDriverObjectReference)}", nameof(frameElement)); + } - if (elementReference == null) - { - throw new ArgumentException($"{nameof(frameElement)} cannot be converted to {nameof(IWebDriverObjectReference)}", nameof(frameElement)); - } + Dictionary elementDictionary = elementReference.ToDictionary(); - Dictionary elementDictionary = elementReference.ToDictionary(); + Dictionary parameters = new Dictionary(); + parameters.Add("id", elementDictionary); + this.driver.Execute(DriverCommand.SwitchToFrame, parameters); + return this.driver; + } - Dictionary parameters = new Dictionary(); - parameters.Add("id", elementDictionary); - this.driver.Execute(DriverCommand.SwitchToFrame, parameters); - return this.driver; + /// + /// Select the parent frame of the currently selected frame. + /// + /// An instance focused on the specified frame. + public IWebDriver ParentFrame() + { + Dictionary parameters = new Dictionary(); + this.driver.Execute(DriverCommand.SwitchToParentFrame, parameters); + return this.driver; + } + + /// + /// Change to the Window by passing in the name + /// + /// Window handle or name of the window that you wish to move to + /// A WebDriver instance that is currently in use + /// If is . + public IWebDriver Window(string windowHandleOrName) + { + if (windowHandleOrName is null) + { + throw new ArgumentNullException(nameof(windowHandleOrName)); } - /// - /// Select the parent frame of the currently selected frame. - /// - /// An instance focused on the specified frame. - public IWebDriver ParentFrame() + Dictionary parameters = new Dictionary(); + parameters.Add("handle", windowHandleOrName); + try { - Dictionary parameters = new Dictionary(); - this.driver.Execute(DriverCommand.SwitchToParentFrame, parameters); + this.driver.Execute(DriverCommand.SwitchToWindow, parameters); return this.driver; } - - /// - /// Change to the Window by passing in the name - /// - /// Window handle or name of the window that you wish to move to - /// A WebDriver instance that is currently in use - /// If is . - public IWebDriver Window(string windowHandleOrName) + catch (NoSuchWindowException) { - if (windowHandleOrName is null) - { - throw new ArgumentNullException(nameof(windowHandleOrName)); - } - - Dictionary parameters = new Dictionary(); - parameters.Add("handle", windowHandleOrName); + // simulate search by name + string? original = null; try { - this.driver.Execute(DriverCommand.SwitchToWindow, parameters); - return this.driver; + original = this.driver.CurrentWindowHandle; } catch (NoSuchWindowException) { - // simulate search by name - string? original = null; - try - { - original = this.driver.CurrentWindowHandle; - } - catch (NoSuchWindowException) - { - } - - foreach (string handle in this.driver.WindowHandles) - { - this.Window(handle); - if (windowHandleOrName == this.driver.ExecuteScript("return window.name")!.ToString()) - { - return this.driver; // found by name - } - } + } - if (original != null) + foreach (string handle in this.driver.WindowHandles) + { + this.Window(handle); + if (windowHandleOrName == this.driver.ExecuteScript("return window.name")!.ToString()) { - this.Window(original); + return this.driver; // found by name } + } - throw; + if (original != null) + { + this.Window(original); } + + throw; } + } - /// - /// Creates a new browser window and switches the focus for future commands - /// of this driver to the new window. - /// - /// The type of new browser window to be created. - /// The created window is not guaranteed to be of the requested type; if - /// the driver does not support the requested type, a new browser window - /// will be created of whatever type the driver does support. - /// An instance focused on the new browser. - public IWebDriver NewWindow(WindowType typeHint) - { - Dictionary parameters = new Dictionary(); - parameters.Add("type", typeHint.ToString().ToLowerInvariant()); + /// + /// Creates a new browser window and switches the focus for future commands + /// of this driver to the new window. + /// + /// The type of new browser window to be created. + /// The created window is not guaranteed to be of the requested type; if + /// the driver does not support the requested type, a new browser window + /// will be created of whatever type the driver does support. + /// An instance focused on the new browser. + public IWebDriver NewWindow(WindowType typeHint) + { + Dictionary parameters = new Dictionary(); + parameters.Add("type", typeHint.ToString().ToLowerInvariant()); - Response response = this.driver.Execute(DriverCommand.NewWindow, parameters); + Response response = this.driver.Execute(DriverCommand.NewWindow, parameters); - Dictionary result = (Dictionary)response.Value!; - string newWindowHandle = result["handle"].ToString()!; - this.Window(newWindowHandle); + Dictionary result = (Dictionary)response.Value!; + string newWindowHandle = result["handle"].ToString()!; + this.Window(newWindowHandle); - return this.driver; - } + return this.driver; + } - /// - /// Change the active frame to the default - /// - /// Element of the default - public IWebDriver DefaultContent() - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", null); - this.driver.Execute(DriverCommand.SwitchToFrame, parameters); - return this.driver; - } + /// + /// Change the active frame to the default + /// + /// Element of the default + public IWebDriver DefaultContent() + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", null); + this.driver.Execute(DriverCommand.SwitchToFrame, parameters); + return this.driver; + } - /// - /// Finds the active element on the page and returns it - /// - /// Element that is active - public IWebElement ActiveElement() - { - Response response = this.driver.Execute(DriverCommand.GetActiveElement, null); - return this.driver.GetElementFromResponse(response)!; - } + /// + /// Finds the active element on the page and returns it + /// + /// Element that is active + public IWebElement ActiveElement() + { + Response response = this.driver.Execute(DriverCommand.GetActiveElement, null); + return this.driver.GetElementFromResponse(response)!; + } - /// - /// Switches to the currently active modal dialog for this particular driver instance. - /// - /// A handle to the dialog. - public IAlert Alert() - { - // N.B. We only execute the GetAlertText command to be able to throw - // a NoAlertPresentException if there is no alert found. - this.driver.Execute(DriverCommand.GetAlertText, null); - return new Alert(this.driver); - } + /// + /// Switches to the currently active modal dialog for this particular driver instance. + /// + /// A handle to the dialog. + public IAlert Alert() + { + // N.B. We only execute the GetAlertText command to be able to throw + // a NoAlertPresentException if there is no alert found. + this.driver.Execute(DriverCommand.GetAlertText, null); + return new Alert(this.driver); } } diff --git a/dotnet/src/webdriver/Timeouts.cs b/dotnet/src/webdriver/Timeouts.cs index 6f267709d3831..c42334454038e 100644 --- a/dotnet/src/webdriver/Timeouts.cs +++ b/dotnet/src/webdriver/Timeouts.cs @@ -21,128 +21,127 @@ using System.Collections.Generic; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can define timeouts. +/// +internal class Timeouts : ITimeouts { + private const string ImplicitTimeoutName = "implicit"; + private const string AsyncScriptTimeoutName = "script"; + private const string PageLoadTimeoutName = "pageLoad"; + private const string LegacyPageLoadTimeoutName = "page load"; + + private static readonly TimeSpan DefaultImplicitWaitTimeout = TimeSpan.FromSeconds(0); + private static readonly TimeSpan DefaultAsyncScriptTimeout = TimeSpan.FromSeconds(30); + private static readonly TimeSpan DefaultPageLoadTimeout = TimeSpan.FromSeconds(300); + + private readonly WebDriver driver; + /// - /// Defines the interface through which the user can define timeouts. + /// Initializes a new instance of the class /// - internal class Timeouts : ITimeouts + /// The driver that is currently in use + public Timeouts(WebDriver driver) { - private const string ImplicitTimeoutName = "implicit"; - private const string AsyncScriptTimeoutName = "script"; - private const string PageLoadTimeoutName = "pageLoad"; - private const string LegacyPageLoadTimeoutName = "page load"; + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - private static readonly TimeSpan DefaultImplicitWaitTimeout = TimeSpan.FromSeconds(0); - private static readonly TimeSpan DefaultAsyncScriptTimeout = TimeSpan.FromSeconds(30); - private static readonly TimeSpan DefaultPageLoadTimeout = TimeSpan.FromSeconds(300); + /// + /// Gets or sets the implicit wait timeout, which is the amount of time the + /// driver should wait when searching for an element if it is not immediately + /// present. + /// + /// + /// When searching for a single element, the driver should poll the page + /// until the element has been found, or this timeout expires before throwing + /// a . When searching for multiple elements, + /// the driver should poll the page until at least one element has been found + /// or this timeout has expired. + /// + /// Increasing the implicit wait timeout should be used judiciously as it + /// will have an adverse effect on test run time, especially when used with + /// slower location strategies like XPath. + /// + /// + /// Also can be managed via driver option. + /// + /// + public TimeSpan ImplicitWait + { + get => this.ExecuteGetTimeout(ImplicitTimeoutName); + set => this.ExecuteSetTimeout(ImplicitTimeoutName, value); + } - private readonly WebDriver driver; + /// + /// Gets or sets the asynchronous script timeout, which is the amount + /// of time the driver should wait when executing JavaScript asynchronously. + /// This timeout only affects the + /// method. + /// + /// + /// + /// Also can be managed via driver option. + /// + /// + public TimeSpan AsynchronousJavaScript + { + get => this.ExecuteGetTimeout(AsyncScriptTimeoutName); + set => this.ExecuteSetTimeout(AsyncScriptTimeoutName, value); + } - /// - /// Initializes a new instance of the class - /// - /// The driver that is currently in use - public Timeouts(WebDriver driver) - { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + /// + /// Gets or sets the page load timeout, which is the amount of time the driver + /// should wait for a page to load when setting the + /// property. + /// + /// + /// + /// Also can be managed via driver option. + /// + /// + public TimeSpan PageLoad + { + get => this.ExecuteGetTimeout(PageLoadTimeoutName); + set => this.ExecuteSetTimeout(PageLoadTimeoutName, value); + } - /// - /// Gets or sets the implicit wait timeout, which is the amount of time the - /// driver should wait when searching for an element if it is not immediately - /// present. - /// - /// - /// When searching for a single element, the driver should poll the page - /// until the element has been found, or this timeout expires before throwing - /// a . When searching for multiple elements, - /// the driver should poll the page until at least one element has been found - /// or this timeout has expired. - /// - /// Increasing the implicit wait timeout should be used judiciously as it - /// will have an adverse effect on test run time, especially when used with - /// slower location strategies like XPath. - /// - /// - /// Also can be managed via driver option. - /// - /// - public TimeSpan ImplicitWait - { - get => this.ExecuteGetTimeout(ImplicitTimeoutName); - set => this.ExecuteSetTimeout(ImplicitTimeoutName, value); - } + private TimeSpan ExecuteGetTimeout(string timeoutType) + { + Response commandResponse = this.driver.Execute(DriverCommand.GetTimeouts, null); - /// - /// Gets or sets the asynchronous script timeout, which is the amount - /// of time the driver should wait when executing JavaScript asynchronously. - /// This timeout only affects the - /// method. - /// - /// - /// - /// Also can be managed via driver option. - /// - /// - public TimeSpan AsynchronousJavaScript + Dictionary responseValue = (Dictionary)commandResponse.Value!; + if (!responseValue.TryGetValue(timeoutType, out object? timeout)) { - get => this.ExecuteGetTimeout(AsyncScriptTimeoutName); - set => this.ExecuteSetTimeout(AsyncScriptTimeoutName, value); + throw new WebDriverException("Specified timeout type not defined"); } - /// - /// Gets or sets the page load timeout, which is the amount of time the driver - /// should wait for a page to load when setting the - /// property. - /// - /// - /// - /// Also can be managed via driver option. - /// - /// - public TimeSpan PageLoad - { - get => this.ExecuteGetTimeout(PageLoadTimeoutName); - set => this.ExecuteSetTimeout(PageLoadTimeoutName, value); - } + return TimeSpan.FromMilliseconds(Convert.ToDouble(timeout, CultureInfo.InvariantCulture)); + } - private TimeSpan ExecuteGetTimeout(string timeoutType) + private void ExecuteSetTimeout(string timeoutType, TimeSpan timeToWait) + { + double milliseconds = timeToWait.TotalMilliseconds; + if (timeToWait == TimeSpan.MinValue) { - Response commandResponse = this.driver.Execute(DriverCommand.GetTimeouts, null); - - Dictionary responseValue = (Dictionary)commandResponse.Value!; - if (!responseValue.TryGetValue(timeoutType, out object? timeout)) + if (timeoutType == ImplicitTimeoutName) { - throw new WebDriverException("Specified timeout type not defined"); + milliseconds = DefaultImplicitWaitTimeout.TotalMilliseconds; } - - return TimeSpan.FromMilliseconds(Convert.ToDouble(timeout, CultureInfo.InvariantCulture)); - } - - private void ExecuteSetTimeout(string timeoutType, TimeSpan timeToWait) - { - double milliseconds = timeToWait.TotalMilliseconds; - if (timeToWait == TimeSpan.MinValue) + else if (timeoutType == AsyncScriptTimeoutName) { - if (timeoutType == ImplicitTimeoutName) - { - milliseconds = DefaultImplicitWaitTimeout.TotalMilliseconds; - } - else if (timeoutType == AsyncScriptTimeoutName) - { - milliseconds = DefaultAsyncScriptTimeout.TotalMilliseconds; - } - else - { - milliseconds = DefaultPageLoadTimeout.TotalMilliseconds; - } + milliseconds = DefaultAsyncScriptTimeout.TotalMilliseconds; } + else + { + milliseconds = DefaultPageLoadTimeout.TotalMilliseconds; + } + } - Dictionary parameters = new Dictionary(); - parameters.Add(timeoutType, Convert.ToInt64(milliseconds)); + Dictionary parameters = new Dictionary(); + parameters.Add(timeoutType, Convert.ToInt64(milliseconds)); - this.driver.Execute(DriverCommand.SetTimeouts, parameters); - } + this.driver.Execute(DriverCommand.SetTimeouts, parameters); } } diff --git a/dotnet/src/webdriver/UnableToSetCookieException.cs b/dotnet/src/webdriver/UnableToSetCookieException.cs index 97a104cf473da..005f9ce599f28 100644 --- a/dotnet/src/webdriver/UnableToSetCookieException.cs +++ b/dotnet/src/webdriver/UnableToSetCookieException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when the user is unable to set a cookie. +/// +[Serializable] +public class UnableToSetCookieException : WebDriverException { /// - /// The exception that is thrown when the user is unable to set a cookie. + /// Initializes a new instance of the class. /// - [Serializable] - public class UnableToSetCookieException : WebDriverException + public UnableToSetCookieException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public UnableToSetCookieException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public UnableToSetCookieException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public UnableToSetCookieException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public UnableToSetCookieException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public UnableToSetCookieException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/UnhandledAlertException.cs b/dotnet/src/webdriver/UnhandledAlertException.cs index b672064898888..3ba22e472b451 100644 --- a/dotnet/src/webdriver/UnhandledAlertException.cs +++ b/dotnet/src/webdriver/UnhandledAlertException.cs @@ -19,60 +19,59 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an unhandled alert is present. +/// +[Serializable] +public class UnhandledAlertException : WebDriverException { /// - /// The exception that is thrown when an unhandled alert is present. + /// Gets the text of the unhandled alert. /// - [Serializable] - public class UnhandledAlertException : WebDriverException - { - /// - /// Gets the text of the unhandled alert. - /// - public string AlertText { get; } = string.Empty; + public string AlertText { get; } = string.Empty; - /// - /// Initializes a new instance of the class. - /// - public UnhandledAlertException() - : base() - { - } + /// + /// Initializes a new instance of the class. + /// + public UnhandledAlertException() + : base() + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public UnhandledAlertException(string message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public UnhandledAlertException(string message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and alert text. - /// - /// The message that describes the error. - /// The text of the unhandled alert. - public UnhandledAlertException(string message, string alertText) - : base(message) - { - this.AlertText = alertText; - } + /// + /// Initializes a new instance of the class with + /// a specified error message and alert text. + /// + /// The message that describes the error. + /// The text of the unhandled alert. + public UnhandledAlertException(string message, string alertText) + : base(message) + { + this.AlertText = alertText; + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public UnhandledAlertException(string message, Exception innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public UnhandledAlertException(string message, Exception innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/UnknownErrorException.cs b/dotnet/src/webdriver/UnknownErrorException.cs index 08d5074814219..4e43cf6d63214 100644 --- a/dotnet/src/webdriver/UnknownErrorException.cs +++ b/dotnet/src/webdriver/UnknownErrorException.cs @@ -19,31 +19,30 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// An unknown error occurred in the remote end while processing the command. +/// +[Serializable] +public class UnknownErrorException : WebDriverException { /// - /// An unknown error occurred in the remote end while processing the command. + /// Initializes a new instance of the class with the specified message. /// - [Serializable] - public class UnknownErrorException : WebDriverException + /// The message of the exception. + public UnknownErrorException(string? message) + : base(message) { - /// - /// Initializes a new instance of the class with the specified message. - /// - /// The message of the exception. - public UnknownErrorException(string? message) - : base(message) - { - } + } - /// - /// Initializes a new instance of the class with the specified message and inner exception. - /// - /// The message of the exception. - /// The inner exception for this exception. - public UnknownErrorException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with the specified message and inner exception. + /// + /// The message of the exception. + /// The inner exception for this exception. + public UnknownErrorException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/UnknownMethodException.cs b/dotnet/src/webdriver/UnknownMethodException.cs index b8d74b0b8850f..665f06c17b668 100644 --- a/dotnet/src/webdriver/UnknownMethodException.cs +++ b/dotnet/src/webdriver/UnknownMethodException.cs @@ -19,29 +19,28 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Exception that is thrown when the requested command matched a known URL but did not match any method for that URL. +/// +[Serializable] +public class UnknownMethodException : WebDriverException { /// - /// Exception that is thrown when the requested command matched a known URL but did not match any method for that URL. + /// Initializes a new instance of the class with the specified message. /// - [Serializable] - public class UnknownMethodException : WebDriverException + /// The message of the exception. + public UnknownMethodException(string? message) : base(message) { - /// - /// Initializes a new instance of the class with the specified message. - /// - /// The message of the exception. - public UnknownMethodException(string? message) : base(message) - { - } + } - /// - /// Initializes a new instance of the class with the specified message and inner exception. - /// - /// The message of the exception. - /// The inner exception for this exception. - public UnknownMethodException(string? message, Exception? innerException) : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with the specified message and inner exception. + /// + /// The message of the exception. + /// The inner exception for this exception. + public UnknownMethodException(string? message, Exception? innerException) : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/UnsupportedOperationException.cs b/dotnet/src/webdriver/UnsupportedOperationException.cs index 0bf3b4fe4653f..093927a2eabbe 100644 --- a/dotnet/src/webdriver/UnsupportedOperationException.cs +++ b/dotnet/src/webdriver/UnsupportedOperationException.cs @@ -19,30 +19,29 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Indicates that a command that should have executed properly cannot be supported for some reason. +/// +public class UnsupportedOperationException : WebDriverException { /// - /// Indicates that a command that should have executed properly cannot be supported for some reason. + /// Initializes a new instance of the class with the specified message. /// - public class UnsupportedOperationException : WebDriverException + /// The message of the exception. + public UnsupportedOperationException(string? message) + : base(message) { - /// - /// Initializes a new instance of the class with the specified message. - /// - /// The message of the exception. - public UnsupportedOperationException(string? message) - : base(message) - { - } + } - /// - /// Initializes a new instance of the class with the specified message and inner exception. - /// - /// The message of the exception. - /// The inner exception for this exception. - public UnsupportedOperationException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with the specified message and inner exception. + /// + /// The message of the exception. + /// The inner exception for this exception. + public UnsupportedOperationException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/UserAgent.cs b/dotnet/src/webdriver/UserAgent.cs index 00190673e7d09..223129a7a526a 100644 --- a/dotnet/src/webdriver/UserAgent.cs +++ b/dotnet/src/webdriver/UserAgent.cs @@ -19,44 +19,43 @@ using System; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +/// +/// Represents a user agent string. +/// +public class UserAgent { /// - /// Represents a user agent string. + /// Initializes a new instance of the type. /// - public class UserAgent + [Obsolete("Use the constructor which sets the userAgentString")] + public UserAgent() { - /// - /// Initializes a new instance of the type. - /// - [Obsolete("Use the constructor which sets the userAgentString")] - public UserAgent() - { - UserAgentString = null!; - } + UserAgentString = null!; + } - /// - /// Initializes a new instance of the type. - /// - /// The user agent string. - public UserAgent(string userAgentString) - { - UserAgentString = userAgentString; - } + /// + /// Initializes a new instance of the type. + /// + /// The user agent string. + public UserAgent(string userAgentString) + { + UserAgentString = userAgentString; + } - /// - /// Gets or sets the user agent string. - /// - public string UserAgentString { get; set; } + /// + /// Gets or sets the user agent string. + /// + public string UserAgentString { get; set; } - /// - /// Gets or sets the language to accept in headers. - /// - public string? AcceptLanguage { get; set; } + /// + /// Gets or sets the language to accept in headers. + /// + public string? AcceptLanguage { get; set; } - /// - /// Gets or sets the value of the platform. - /// - public string? Platform { get; set; } - } + /// + /// Gets or sets the value of the platform. + /// + public string? Platform { get; set; } } diff --git a/dotnet/src/webdriver/VirtualAuth/Credential.cs b/dotnet/src/webdriver/VirtualAuth/Credential.cs index dd5f13c3c6918..71c5f8719e555 100644 --- a/dotnet/src/webdriver/VirtualAuth/Credential.cs +++ b/dotnet/src/webdriver/VirtualAuth/Credential.cs @@ -21,125 +21,124 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.VirtualAuth +namespace OpenQA.Selenium.VirtualAuth; + +/// +/// A credential stored in a virtual authenticator. +/// Refer +/// +public sealed class Credential { + private readonly byte[] id; + private readonly byte[]? userHandle; + + private Credential(byte[] id, bool isResidentCredential, string? rpId, string privateKey, byte[]? userHandle, int signCount) + { + this.id = id ?? throw new ArgumentNullException(nameof(id)); + this.IsResidentCredential = isResidentCredential; + this.RpId = rpId; + this.PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); + this.userHandle = userHandle; + this.SignCount = signCount; + } + /// - /// A credential stored in a virtual authenticator. - /// Refer + /// Creates a credential for use with a virtual authenticator. /// - public sealed class Credential + /// A byte array representing the ID of the credentials. + /// The ID of the relying party to which the credential is scoped. + /// The private Key for the credentials. + /// The signature counter for the credentials. + /// The created instance of the Credential class. + /// If or are . + public static Credential CreateNonResidentCredential(byte[] id, string rpId, string privateKey, int signCount) { - private readonly byte[] id; - private readonly byte[]? userHandle; + return new Credential(id, false, rpId, privateKey, null, signCount); + } - private Credential(byte[] id, bool isResidentCredential, string? rpId, string privateKey, byte[]? userHandle, int signCount) - { - this.id = id ?? throw new ArgumentNullException(nameof(id)); - this.IsResidentCredential = isResidentCredential; - this.RpId = rpId; - this.PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); - this.userHandle = userHandle; - this.SignCount = signCount; - } + /// + /// Creates a credential for use with a virtual authenticator. + /// + /// A byte array representing the ID of the credentials. + /// The ID of the relying party to which the credential is scoped. + /// The private Key for the credentials. + /// The user handle associated to the credential. + /// The signature counter for the credentials. + /// The created instance of the Credential class. + /// If or are . + public static Credential CreateResidentCredential(byte[] id, string rpId, string privateKey, byte[] userHandle, int signCount) + { + return new Credential(id, true, rpId, privateKey, userHandle, signCount); + } - /// - /// Creates a credential for use with a virtual authenticator. - /// - /// A byte array representing the ID of the credentials. - /// The ID of the relying party to which the credential is scoped. - /// The private Key for the credentials. - /// The signature counter for the credentials. - /// The created instance of the Credential class. - /// If or are . - public static Credential CreateNonResidentCredential(byte[] id, string rpId, string privateKey, int signCount) - { - return new Credential(id, false, rpId, privateKey, null, signCount); - } + /// + /// Gets the byte array of the ID of the credential. + /// + public byte[] Id => (byte[])id.Clone(); - /// - /// Creates a credential for use with a virtual authenticator. - /// - /// A byte array representing the ID of the credentials. - /// The ID of the relying party to which the credential is scoped. - /// The private Key for the credentials. - /// The user handle associated to the credential. - /// The signature counter for the credentials. - /// The created instance of the Credential class. - /// If or are . - public static Credential CreateResidentCredential(byte[] id, string rpId, string privateKey, byte[] userHandle, int signCount) - { - return new Credential(id, true, rpId, privateKey, userHandle, signCount); - } + /// + /// Gets a value indicating whether this Credential is a resident credential. + /// + public bool IsResidentCredential { get; } - /// - /// Gets the byte array of the ID of the credential. - /// - public byte[] Id => (byte[])id.Clone(); - - /// - /// Gets a value indicating whether this Credential is a resident credential. - /// - public bool IsResidentCredential { get; } - - /// - /// Gets the ID of the relying party of this credential. - /// - public string? RpId { get; } - - /// - /// Gets the private key of the credential. - /// - public string PrivateKey { get; } - - /// - /// Gets the user handle of the credential. - /// - public byte[]? UserHandle => (byte[]?)userHandle?.Clone(); - - /// - /// Gets the signature counter associated to the public key credential source. - /// - public int SignCount { get; } - - /// - /// Creates a Credential instance from a dictionary of values. - /// - /// The dictionary of values to use to create the Credential instance. - /// The created instance of the Credential. - public static Credential FromDictionary(Dictionary dictionary) + /// + /// Gets the ID of the relying party of this credential. + /// + public string? RpId { get; } + + /// + /// Gets the private key of the credential. + /// + public string PrivateKey { get; } + + /// + /// Gets the user handle of the credential. + /// + public byte[]? UserHandle => (byte[]?)userHandle?.Clone(); + + /// + /// Gets the signature counter associated to the public key credential source. + /// + public int SignCount { get; } + + /// + /// Creates a Credential instance from a dictionary of values. + /// + /// The dictionary of values to use to create the Credential instance. + /// The created instance of the Credential. + public static Credential FromDictionary(Dictionary dictionary) + { + byte[] id = Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]); + bool isResidentCredential = (bool)dictionary["isResidentCredential"]; + string? rpId = dictionary.TryGetValue("rpId", out object? r) ? (string)r : null; + string privateKey = (string)dictionary["privateKey"]; + byte[]? userHandle = dictionary.TryGetValue("userHandle", out object? u) ? Base64UrlEncoder.DecodeBytes((string)u) : null; + int signCount = (int)(long)dictionary["signCount"]; + + return new Credential(id, isResidentCredential, rpId, privateKey, userHandle, signCount); + } + + /// + /// Serializes this Credential instance to a dictionary. + /// + /// The dictionary containing the values for this Credential. + public Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); + + toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id); + toReturn["isResidentCredential"] = this.IsResidentCredential; + if (this.RpId is not null) { - byte[] id = Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]); - bool isResidentCredential = (bool)dictionary["isResidentCredential"]; - string? rpId = dictionary.TryGetValue("rpId", out object? r) ? (string)r : null; - string privateKey = (string)dictionary["privateKey"]; - byte[]? userHandle = dictionary.TryGetValue("userHandle", out object? u) ? Base64UrlEncoder.DecodeBytes((string)u) : null; - int signCount = (int)(long)dictionary["signCount"]; - - return new Credential(id, isResidentCredential, rpId, privateKey, userHandle, signCount); + toReturn["rpId"] = this.RpId; } - - /// - /// Serializes this Credential instance to a dictionary. - /// - /// The dictionary containing the values for this Credential. - public Dictionary ToDictionary() + toReturn["privateKey"] = this.PrivateKey; + toReturn["signCount"] = this.SignCount; + if (this.userHandle is not null) { - Dictionary toReturn = new Dictionary(); - - toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id); - toReturn["isResidentCredential"] = this.IsResidentCredential; - if (this.RpId is not null) - { - toReturn["rpId"] = this.RpId; - } - toReturn["privateKey"] = this.PrivateKey; - toReturn["signCount"] = this.SignCount; - if (this.userHandle is not null) - { - toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle); - } - - return toReturn; + toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle); } + + return toReturn; } } diff --git a/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs b/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs index 96595fa7699aa..23abad5528f90 100644 --- a/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs +++ b/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs @@ -20,70 +20,69 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.VirtualAuth +namespace OpenQA.Selenium.VirtualAuth; + +/// +/// Interface indicating that an object supports using a virtual authenticator. +/// +public interface IHasVirtualAuthenticator { /// - /// Interface indicating that an object supports using a virtual authenticator. + /// Adds a virtual authenticator. /// - public interface IHasVirtualAuthenticator - { - /// - /// Adds a virtual authenticator. - /// - /// The VirtualAuthenticatorOptions to use in creating the authenticator. - /// The ID of the added virtual authenticator. - /// If is . - string AddVirtualAuthenticator(VirtualAuthenticatorOptions options); + /// The VirtualAuthenticatorOptions to use in creating the authenticator. + /// The ID of the added virtual authenticator. + /// If is . + string AddVirtualAuthenticator(VirtualAuthenticatorOptions options); - /// - /// Removes a virtual authenticator. - /// - /// The ID of the virtual authenticator to remove. - /// If is . - /// If the specified virtual authenticator does not exist. - void RemoveVirtualAuthenticator(string id); + /// + /// Removes a virtual authenticator. + /// + /// The ID of the virtual authenticator to remove. + /// If is . + /// If the specified virtual authenticator does not exist. + void RemoveVirtualAuthenticator(string id); - /// - /// Adds a credential to the virtual authenticator. - /// - /// The credential to add to the authenticator. - /// If is . - /// If a Virtual Authenticator has not been added yet. - void AddCredential(Credential credential); + /// + /// Adds a credential to the virtual authenticator. + /// + /// The credential to add to the authenticator. + /// If is . + /// If a Virtual Authenticator has not been added yet. + void AddCredential(Credential credential); - /// - /// Gets a list of the credentials registered to the virtual authenticator. - /// - /// The list of credentials registered to the virtual authenticator. - List GetCredentials(); + /// + /// Gets a list of the credentials registered to the virtual authenticator. + /// + /// The list of credentials registered to the virtual authenticator. + List GetCredentials(); - /// - /// Removes a credential from the virtual authenticator. - /// - /// A byte array representing the ID of the credential to remove. - /// If is . - /// If a Virtual Authenticator has not been added yet. - void RemoveCredential(byte[] credentialId); + /// + /// Removes a credential from the virtual authenticator. + /// + /// A byte array representing the ID of the credential to remove. + /// If is . + /// If a Virtual Authenticator has not been added yet. + void RemoveCredential(byte[] credentialId); - /// - /// Removes a credential from the virtual authenticator. - /// - /// A string representing the ID of the credential to remove. - /// If is . - /// If a Virtual Authenticator has not been added yet. - void RemoveCredential(string credentialId); + /// + /// Removes a credential from the virtual authenticator. + /// + /// A string representing the ID of the credential to remove. + /// If is . + /// If a Virtual Authenticator has not been added yet. + void RemoveCredential(string credentialId); - /// - /// Removes all credentials registered to this virtual authenticator. - /// - /// If a Virtual Authenticator has not been added yet. - void RemoveAllCredentials(); + /// + /// Removes all credentials registered to this virtual authenticator. + /// + /// If a Virtual Authenticator has not been added yet. + void RemoveAllCredentials(); - /// - /// Sets whether or not a user is verified in this virtual authenticator. - /// - /// if the user is verified; otherwise . - /// If a Virtual Authenticator has not been added yet. - void SetUserVerified(bool verified); - } + /// + /// Sets whether or not a user is verified in this virtual authenticator. + /// + /// if the user is verified; otherwise . + /// If a Virtual Authenticator has not been added yet. + void SetUserVerified(bool verified); } diff --git a/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs b/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs index 52e606a28072a..85389975f0f5e 100644 --- a/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs +++ b/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs @@ -20,168 +20,167 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium.VirtualAuth +namespace OpenQA.Selenium.VirtualAuth; + +/// +/// Options for the creation of virtual authenticators. +/// Refer https://w3c.github.io/webauthn/#sctn-automation +/// +public class VirtualAuthenticatorOptions { /// - /// Options for the creation of virtual authenticators. - /// Refer https://w3c.github.io/webauthn/#sctn-automation + /// The protocol to use for the virtual authenticator. /// - public class VirtualAuthenticatorOptions + public static class Protocol { /// - /// The protocol to use for the virtual authenticator. + /// Value representing the CTAP2 protocol. /// - public static class Protocol - { - /// - /// Value representing the CTAP2 protocol. - /// - public static readonly string CTAP2 = "ctap2"; - - /// - /// Value representing the U2F protocol. - /// - public static readonly string U2F = "ctap1/u2f"; - } + public static readonly string CTAP2 = "ctap2"; /// - /// The transport to use for the virtual authenticator. + /// Value representing the U2F protocol. /// - public static class Transport - { - /// - /// Value representing the BLE transport. - /// - public static readonly string BLE = "ble"; - - /// - /// Value representing the "internal" transport. - /// - public static readonly string INTERNAL = "internal"; - - /// - /// Value representing the near-field communications transport. - /// - public static readonly string NFC = "nfc"; - - /// - /// Value representing the USB transport. - /// - public static readonly string USB = "usb"; - } - - private string protocol = Protocol.CTAP2; - private string transport = Transport.USB; - private bool hasResidentKey = false; - private bool hasUserVerification = false; - private bool isUserConsenting = true; - private bool isUserVerified = false; + public static readonly string U2F = "ctap1/u2f"; + } + /// + /// The transport to use for the virtual authenticator. + /// + public static class Transport + { /// - /// Sets the Client to Authenticator Protocol (CTAP) this Virtual Authenticator speaks. + /// Value representing the BLE transport. /// - /// The CTAP protocol identifier. - /// This options instance for chaining. - /// Valid protocols are available on the type. - /// If is not a supported protocol value. - /// - public VirtualAuthenticatorOptions SetProtocol(string protocol) - { - if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol)) - { - this.protocol = protocol; - return this; - } - else - { - throw new ArgumentException("Enter a valid protocol value." + - "Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols."); - } - } + public static readonly string BLE = "ble"; /// - /// Sets the Authenticator Transport this Virtual Authenticator needs to implement, to communicate with clients. + /// Value representing the "internal" transport. /// - /// Valid transport value. - /// - /// This options instance for chaining. - /// Valid protocols are available on the type. - /// If is not a supported transport value. - /// - public VirtualAuthenticatorOptions SetTransport(string transport) - { - if (Transport.BLE == transport || Transport.INTERNAL == transport || Transport.NFC == transport || Transport.USB == transport) - { - this.transport = transport; - return this; - } - else - { - throw new ArgumentException("Enter a valid transport value." + - "Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values."); - } - } + public static readonly string INTERNAL = "internal"; /// - /// If set to , the authenticator will support Client-side discoverable Credentials. + /// Value representing the near-field communications transport. /// - /// Whether authenticator will support client-side discoverable credentials. - /// This options instance for chaining. - public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey) - { - this.hasResidentKey = hasResidentKey; - return this; - } + public static readonly string NFC = "nfc"; /// - /// If set to , the authenticator will support User Verification. + /// Value representing the USB transport. /// - /// Whether the authenticator supports user verification. - /// This options instance for chaining. - public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification) + public static readonly string USB = "usb"; + } + + private string protocol = Protocol.CTAP2; + private string transport = Transport.USB; + private bool hasResidentKey = false; + private bool hasUserVerification = false; + private bool isUserConsenting = true; + private bool isUserVerified = false; + + /// + /// Sets the Client to Authenticator Protocol (CTAP) this Virtual Authenticator speaks. + /// + /// The CTAP protocol identifier. + /// This options instance for chaining. + /// Valid protocols are available on the type. + /// If is not a supported protocol value. + /// + public VirtualAuthenticatorOptions SetProtocol(string protocol) + { + if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol)) { - this.hasUserVerification = hasUserVerification; + this.protocol = protocol; return this; } - - /// - /// If set to , a User Consent will always be granted. - /// - /// Whether a user consent will always be granted. - /// This options instance for chaining. - public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting) + else { - this.isUserConsenting = isUserConsenting; - return this; + throw new ArgumentException("Enter a valid protocol value." + + "Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols."); } + } - /// - /// If set to , User Verification will always succeed. - /// - /// Whether User Verification will always succeed. - /// This options instance for chaining. - public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified) + /// + /// Sets the Authenticator Transport this Virtual Authenticator needs to implement, to communicate with clients. + /// + /// Valid transport value. + /// + /// This options instance for chaining. + /// Valid protocols are available on the type. + /// If is not a supported transport value. + /// + public VirtualAuthenticatorOptions SetTransport(string transport) + { + if (Transport.BLE == transport || Transport.INTERNAL == transport || Transport.NFC == transport || Transport.USB == transport) { - this.isUserVerified = isUserVerified; + this.transport = transport; return this; } - - /// - /// Serializes this set of options into a dictionary of key-value pairs. - /// - /// The dictionary containing the values of this set of options. - public Dictionary ToDictionary() + else { - Dictionary toReturn = new Dictionary(); + throw new ArgumentException("Enter a valid transport value." + + "Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values."); + } + } + + /// + /// If set to , the authenticator will support Client-side discoverable Credentials. + /// + /// Whether authenticator will support client-side discoverable credentials. + /// This options instance for chaining. + public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey) + { + this.hasResidentKey = hasResidentKey; + return this; + } - toReturn["protocol"] = this.protocol; - toReturn["transport"] = this.transport; - toReturn["hasResidentKey"] = this.hasResidentKey; - toReturn["hasUserVerification"] = this.hasUserVerification; - toReturn["isUserConsenting"] = this.isUserConsenting; - toReturn["isUserVerified"] = this.isUserVerified; + /// + /// If set to , the authenticator will support User Verification. + /// + /// Whether the authenticator supports user verification. + /// This options instance for chaining. + public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification) + { + this.hasUserVerification = hasUserVerification; + return this; + } - return toReturn; - } + /// + /// If set to , a User Consent will always be granted. + /// + /// Whether a user consent will always be granted. + /// This options instance for chaining. + public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting) + { + this.isUserConsenting = isUserConsenting; + return this; + } + + /// + /// If set to , User Verification will always succeed. + /// + /// Whether User Verification will always succeed. + /// This options instance for chaining. + public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified) + { + this.isUserVerified = isUserVerified; + return this; + } + + /// + /// Serializes this set of options into a dictionary of key-value pairs. + /// + /// The dictionary containing the values of this set of options. + public Dictionary ToDictionary() + { + Dictionary toReturn = new Dictionary(); + + toReturn["protocol"] = this.protocol; + toReturn["transport"] = this.transport; + toReturn["hasResidentKey"] = this.hasResidentKey; + toReturn["hasUserVerification"] = this.hasUserVerification; + toReturn["isUserConsenting"] = this.isUserConsenting; + toReturn["isUserVerified"] = this.isUserVerified; + + return toReturn; } } diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 4008ec1e15b66..77f82461053d7 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -28,1125 +28,1124 @@ using System.Globalization; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// A base class representing a driver for a web browser. +/// +public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFindsElement, ITakesScreenshot, ISupportsPrint, IActionExecutor, IAllowsFileDetection, IHasCapabilities, IHasCommandExecutor, IHasSessionId, ICustomDriverCommandExecutor, IHasVirtualAuthenticator { /// - /// A base class representing a driver for a web browser. + /// The default command timeout for HTTP requests in a RemoteWebDriver instance. /// - public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFindsElement, ITakesScreenshot, ISupportsPrint, IActionExecutor, IAllowsFileDetection, IHasCapabilities, IHasCommandExecutor, IHasSessionId, ICustomDriverCommandExecutor, IHasVirtualAuthenticator - { - /// - /// The default command timeout for HTTP requests in a RemoteWebDriver instance. - /// - protected static readonly TimeSpan DefaultCommandTimeout = TimeSpan.FromSeconds(60); - private IFileDetector fileDetector = new DefaultFileDetector(); - private NetworkManager network; - private WebElementFactory elementFactory; - - private readonly List registeredCommands = new List(); - - /// - /// Initializes a new instance of the class. - /// - /// The object used to execute commands. - /// The object used to configure the driver session. - protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) - { - this.CommandExecutor = executor; + protected static readonly TimeSpan DefaultCommandTimeout = TimeSpan.FromSeconds(60); + private IFileDetector fileDetector = new DefaultFileDetector(); + private NetworkManager network; + private WebElementFactory elementFactory; - try - { - this.StartSession(capabilities); - } - catch (Exception) - { - try - { - // Failed to start driver session, disposing of driver - this.Quit(); - } - catch - { - // Ignore the clean-up exception. We'll propagate the original failure. - } - throw; - } + private readonly List registeredCommands = new List(); - this.elementFactory = new WebElementFactory(this); - this.registeredCommands.AddRange(DriverCommand.KnownCommands); - - if (this is ISupportsLogs) - { - // Only add the legacy log commands if the driver supports - // retrieving the logs via the extension end points. - this.RegisterDriverCommand(DriverCommand.GetAvailableLogTypes, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/log/types"), true); - this.RegisterDriverCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log"), true); - } - } + /// + /// Initializes a new instance of the class. + /// + /// The object used to execute commands. + /// The object used to configure the driver session. + protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) + { + this.CommandExecutor = executor; - /// - /// Gets the which executes commands for this driver. - /// - public ICommandExecutor CommandExecutor { get; } - - /// - /// Gets the that the driver session was created with, which may be different from those requested. - /// - public ICapabilities Capabilities { get; private set; } - - /// - /// Gets or sets the URL the browser is currently displaying. - /// - /// - /// - /// - public string Url + try { - get - { - Response commandResponse = this.Execute(DriverCommand.GetCurrentUrl, null); - - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; - } - - set => new Navigator(this).GoToUrl(value); + this.StartSession(capabilities); } - - /// - /// Gets the title of the current browser window. - /// - public string Title + catch (Exception) { - get + try { - Response commandResponse = this.Execute(DriverCommand.GetTitle, null); - - return commandResponse.Value?.ToString() ?? string.Empty; + // Failed to start driver session, disposing of driver + this.Quit(); } - } - - /// - /// Gets the source of the page last loaded by the browser. - /// - public string PageSource - { - get + catch { - Response commandResponse = this.Execute(DriverCommand.GetPageSource, null); - - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; + // Ignore the clean-up exception. We'll propagate the original failure. } + throw; } - /// - /// Gets the current window handle, which is an opaque handle to this - /// window that uniquely identifies it within this driver instance. - /// - public string CurrentWindowHandle - { - get - { - Response commandResponse = this.Execute(DriverCommand.GetCurrentWindowHandle, null); + this.elementFactory = new WebElementFactory(this); + this.registeredCommands.AddRange(DriverCommand.KnownCommands); - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; - } - } - - /// - /// Gets the window handles of open browser windows. - /// - public ReadOnlyCollection WindowHandles + if (this is ISupportsLogs) { - get - { - Response commandResponse = this.Execute(DriverCommand.GetWindowHandles, null); + // Only add the legacy log commands if the driver supports + // retrieving the logs via the extension end points. + this.RegisterDriverCommand(DriverCommand.GetAvailableLogTypes, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/log/types"), true); + this.RegisterDriverCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log"), true); + } + } - commandResponse.EnsureValueIsNotNull(); - object?[] handles = (object?[])commandResponse.Value; - List handleList = new List(handles.Length); - foreach (object? handle in handles) - { - handleList.Add(handle!.ToString()!); - } + /// + /// Gets the which executes commands for this driver. + /// + public ICommandExecutor CommandExecutor { get; } - return handleList.AsReadOnly(); - } - } + /// + /// Gets the that the driver session was created with, which may be different from those requested. + /// + public ICapabilities Capabilities { get; private set; } - /// - /// Gets a value indicating whether this object is a valid action executor. - /// - public bool IsActionExecutor => true; - - /// - /// Gets the for the current session of this driver. - /// - public SessionId SessionId { get; private set; } - - /// - /// Gets or sets the responsible for detecting - /// sequences of keystrokes representing file paths and names. - /// - /// If value is set to . - public virtual IFileDetector FileDetector + /// + /// Gets or sets the URL the browser is currently displaying. + /// + /// + /// + /// + public string Url + { + get { - get => this.fileDetector; - set => this.fileDetector = value ?? throw new ArgumentNullException(nameof(value), "FileDetector cannot be null"); + Response commandResponse = this.Execute(DriverCommand.GetCurrentUrl, null); + + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } - internal INetwork Network => this.network ??= new NetworkManager(this); + set => new Navigator(this).GoToUrl(value); + } - /// - /// Gets or sets the factory object used to create instances of - /// or its subclasses. - /// - /// If value is set to . - protected WebElementFactory ElementFactory + /// + /// Gets the title of the current browser window. + /// + public string Title + { + get { - get => this.elementFactory; - set => this.elementFactory = value ?? throw new ArgumentNullException(nameof(value)); - } + Response commandResponse = this.Execute(DriverCommand.GetTitle, null); - /// - /// Closes the Browser - /// - public void Close() - { - this.Execute(DriverCommand.Close, null); + return commandResponse.Value?.ToString() ?? string.Empty; } + } - /// - /// Dispose the WebDriver Instance - /// - public void Dispose() + /// + /// Gets the source of the page last loaded by the browser. + /// + public string PageSource + { + get { - this.Dispose(true); - GC.SuppressFinalize(this); - } + Response commandResponse = this.Execute(DriverCommand.GetPageSource, null); - /// - /// Executes JavaScript "asynchronously" in the context of the currently selected frame or window, - /// executing the callback function specified as the last argument in the list of arguments. - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - public object? ExecuteAsyncScript(string script, params object?[]? args) - { - return this.ExecuteScriptCommand(script, DriverCommand.ExecuteAsyncScript, args); + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } + } - /// - /// Executes JavaScript in the context of the currently selected frame or window - /// - /// The JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - public object? ExecuteScript(string script, params object?[]? args) + /// + /// Gets the current window handle, which is an opaque handle to this + /// window that uniquely identifies it within this driver instance. + /// + public string CurrentWindowHandle + { + get { - return this.ExecuteScriptCommand(script, DriverCommand.ExecuteScript, args); + Response commandResponse = this.Execute(DriverCommand.GetCurrentWindowHandle, null); + + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } + } - /// - /// Executes JavaScript in the context of the currently selected frame or window - /// - /// A object containing the JavaScript code to execute. - /// The arguments to the script. - /// The value returned by the script. - /// If is . - public object? ExecuteScript(PinnedScript script, params object?[]? args) + /// + /// Gets the window handles of open browser windows. + /// + public ReadOnlyCollection WindowHandles + { + get { - if (script == null) + Response commandResponse = this.Execute(DriverCommand.GetWindowHandles, null); + + commandResponse.EnsureValueIsNotNull(); + object?[] handles = (object?[])commandResponse.Value; + List handleList = new List(handles.Length); + foreach (object? handle in handles) { - throw new ArgumentNullException(nameof(script)); + handleList.Add(handle!.ToString()!); } - return this.ExecuteScript(script.MakeExecutionScript(), args); + return handleList.AsReadOnly(); } + } - /// - /// Finds the first element in the page that matches the object - /// - /// By mechanism to find the object - /// IWebElement object so that you can interact with that object - /// If is . - /// - /// - /// IWebDriver driver = new InternetExplorerDriver(); - /// IWebElement elem = driver.FindElement(By.Name("q")); - /// - /// - public IWebElement FindElement(By by) - { - if (by == null) - { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); - } + /// + /// Gets a value indicating whether this object is a valid action executor. + /// + public bool IsActionExecutor => true; - return by.FindElement(this); - } + /// + /// Gets the for the current session of this driver. + /// + public SessionId SessionId { get; private set; } - /// - /// Finds an element matching the given mechanism and value. - /// - /// The mechanism by which to find the element. - /// The value to use to search for the element. - /// The first matching the given criteria. - public virtual IWebElement FindElement(string mechanism, string value) - { - Dictionary parameters = new Dictionary(); - parameters.Add("using", mechanism); - parameters.Add("value", value); + /// + /// Gets or sets the responsible for detecting + /// sequences of keystrokes representing file paths and names. + /// + /// If value is set to . + public virtual IFileDetector FileDetector + { + get => this.fileDetector; + set => this.fileDetector = value ?? throw new ArgumentNullException(nameof(value), "FileDetector cannot be null"); + } - Response commandResponse = this.Execute(DriverCommand.FindElement, parameters); + internal INetwork Network => this.network ??= new NetworkManager(this); - return this.GetElementFromResponse(commandResponse)!; - } + /// + /// Gets or sets the factory object used to create instances of + /// or its subclasses. + /// + /// If value is set to . + protected WebElementFactory ElementFactory + { + get => this.elementFactory; + set => this.elementFactory = value ?? throw new ArgumentNullException(nameof(value)); + } - /// - /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page - /// - /// By mechanism to find the element - /// ReadOnlyCollection of IWebElement - /// - /// - /// IWebDriver driver = new InternetExplorerDriver(); - /// ReadOnlyCollection]]> classList = driver.FindElements(By.ClassName("class")); - /// - /// - public ReadOnlyCollection FindElements(By by) - { - if (by == null) - { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); - } + /// + /// Closes the Browser + /// + public void Close() + { + this.Execute(DriverCommand.Close, null); + } - return by.FindElements(this); - } + /// + /// Dispose the WebDriver Instance + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Finds all elements matching the given mechanism and value. - /// - /// The mechanism by which to find the elements. - /// The value to use to search for the elements. - /// A collection of all of the IWebElements matching the given criteria. - public virtual ReadOnlyCollection FindElements(string mechanism, string value) - { - Dictionary parameters = new Dictionary(); - parameters.Add("using", mechanism); - parameters.Add("value", value); + /// + /// Executes JavaScript "asynchronously" in the context of the currently selected frame or window, + /// executing the callback function specified as the last argument in the list of arguments. + /// + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + public object? ExecuteAsyncScript(string script, params object?[]? args) + { + return this.ExecuteScriptCommand(script, DriverCommand.ExecuteAsyncScript, args); + } - Response commandResponse = this.Execute(DriverCommand.FindElements, parameters); + /// + /// Executes JavaScript in the context of the currently selected frame or window + /// + /// The JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + public object? ExecuteScript(string script, params object?[]? args) + { + return this.ExecuteScriptCommand(script, DriverCommand.ExecuteScript, args); + } - return this.GetElementsFromResponse(commandResponse); + /// + /// Executes JavaScript in the context of the currently selected frame or window + /// + /// A object containing the JavaScript code to execute. + /// The arguments to the script. + /// The value returned by the script. + /// If is . + public object? ExecuteScript(PinnedScript script, params object?[]? args) + { + if (script == null) + { + throw new ArgumentNullException(nameof(script)); } - /// - /// Gets a object representing the image of the page on the screen. - /// - /// A object containing the image. - public Screenshot GetScreenshot() - { - Response screenshotResponse = this.Execute(DriverCommand.Screenshot, null); + return this.ExecuteScript(script.MakeExecutionScript(), args); + } - screenshotResponse.EnsureValueIsNotNull(); - string base64 = screenshotResponse.Value.ToString()!; - return new Screenshot(base64); + /// + /// Finds the first element in the page that matches the object + /// + /// By mechanism to find the object + /// IWebElement object so that you can interact with that object + /// If is . + /// + /// + /// IWebDriver driver = new InternetExplorerDriver(); + /// IWebElement elem = driver.FindElement(By.Name("q")); + /// + /// + public IWebElement FindElement(By by) + { + if (by == null) + { + throw new ArgumentNullException(nameof(@by), "by cannot be null"); } - /// - /// Gets a object representing a PDF-formatted print representation of the page. - /// - /// A object describing the options of the printed document. - /// The object containing the PDF-formatted print representation of the page. - /// If is . - public PrintDocument Print(PrintOptions printOptions) - { - if (printOptions is null) - { - throw new ArgumentNullException(nameof(printOptions)); - } + return by.FindElement(this); + } - Response commandResponse = this.Execute(DriverCommand.Print, printOptions.ToDictionary()); + /// + /// Finds an element matching the given mechanism and value. + /// + /// The mechanism by which to find the element. + /// The value to use to search for the element. + /// The first matching the given criteria. + public virtual IWebElement FindElement(string mechanism, string value) + { + Dictionary parameters = new Dictionary(); + parameters.Add("using", mechanism); + parameters.Add("value", value); - commandResponse.EnsureValueIsNotNull(); - string base64 = commandResponse.Value.ToString()!; - return new PrintDocument(base64); - } + Response commandResponse = this.Execute(DriverCommand.FindElement, parameters); + + return this.GetElementFromResponse(commandResponse)!; + } - /// - /// Performs the specified list of actions with this action executor. - /// - /// The list of action sequences to perform. - public void PerformActions(IList actionSequenceList) + /// + /// Finds the elements on the page by using the object and returns a ReadOnlyCollection of the Elements on the page + /// + /// By mechanism to find the element + /// ReadOnlyCollection of IWebElement + /// + /// + /// IWebDriver driver = new InternetExplorerDriver(); + /// ReadOnlyCollection]]> classList = driver.FindElements(By.ClassName("class")); + /// + /// + public ReadOnlyCollection FindElements(By by) + { + if (by == null) { - if (actionSequenceList == null) - { - throw new ArgumentNullException(nameof(actionSequenceList), "List of action sequences must not be null"); - } + throw new ArgumentNullException(nameof(@by), "by cannot be null"); + } - List objectList = new List(); - foreach (ActionSequence sequence in actionSequenceList) - { - objectList.Add(sequence.ToDictionary()); - } + return by.FindElements(this); + } - Dictionary parameters = new Dictionary(); - parameters["actions"] = objectList; + /// + /// Finds all elements matching the given mechanism and value. + /// + /// The mechanism by which to find the elements. + /// The value to use to search for the elements. + /// A collection of all of the IWebElements matching the given criteria. + public virtual ReadOnlyCollection FindElements(string mechanism, string value) + { + Dictionary parameters = new Dictionary(); + parameters.Add("using", mechanism); + parameters.Add("value", value); - this.Execute(DriverCommand.Actions, parameters); - } + Response commandResponse = this.Execute(DriverCommand.FindElements, parameters); - /// - /// Resets the input state of the action executor. - /// - public void ResetInputState() - { - this.Execute(DriverCommand.CancelActions, null); - } + return this.GetElementsFromResponse(commandResponse); + } - /// - /// Close the Browser and Dispose of WebDriver - /// - public void Quit() - { - this.Dispose(); - } + /// + /// Gets a object representing the image of the page on the screen. + /// + /// A object containing the image. + public Screenshot GetScreenshot() + { + Response screenshotResponse = this.Execute(DriverCommand.Screenshot, null); + + screenshotResponse.EnsureValueIsNotNull(); + string base64 = screenshotResponse.Value.ToString()!; + return new Screenshot(base64); + } - /// - /// Method to give you access to switch frames and windows - /// - /// Returns an Object that allows you to Switch Frames and Windows - /// - /// - /// IWebDriver driver = new InternetExplorerDriver(); - /// driver.SwitchTo().Frame("FrameName"); - /// - /// - public ITargetLocator SwitchTo() + /// + /// Gets a object representing a PDF-formatted print representation of the page. + /// + /// A object describing the options of the printed document. + /// The object containing the PDF-formatted print representation of the page. + /// If is . + public PrintDocument Print(PrintOptions printOptions) + { + if (printOptions is null) { - return new TargetLocator(this); + throw new ArgumentNullException(nameof(printOptions)); } - /// - /// Instructs the driver to change its settings. - /// - /// An object allowing the user to change - /// the settings of the driver. - public IOptions Manage() + Response commandResponse = this.Execute(DriverCommand.Print, printOptions.ToDictionary()); + + commandResponse.EnsureValueIsNotNull(); + string base64 = commandResponse.Value.ToString()!; + return new PrintDocument(base64); + } + + /// + /// Performs the specified list of actions with this action executor. + /// + /// The list of action sequences to perform. + public void PerformActions(IList actionSequenceList) + { + if (actionSequenceList == null) { - return new OptionsManager(this); + throw new ArgumentNullException(nameof(actionSequenceList), "List of action sequences must not be null"); } - /// - /// Instructs the driver to navigate the browser to another location. - /// - /// An object allowing the user to access - /// the browser's history and to navigate to a given URL. - public INavigation Navigate() + List objectList = new List(); + foreach (ActionSequence sequence in actionSequenceList) { - return new Navigator(this); + objectList.Add(sequence.ToDictionary()); } - /// - /// Executes a command with this driver. - /// - /// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type. - /// A containing the names and values of the parameters of the command. - /// A containing information about the success or failure of the command and any data returned by the command. - /// The command returned an exceptional value. - public object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters) - { - if (this.registeredCommands.Contains(driverCommandToExecute)) - { - throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "A command named '{0}' is predefined by the driver class and cannot be executed with ExecuteCustomDriverCommand. It should be executed using a named method instead.", driverCommandToExecute)); - } + Dictionary parameters = new Dictionary(); + parameters["actions"] = objectList; - return this.Execute(driverCommandToExecute, parameters).Value; - } + this.Execute(DriverCommand.Actions, parameters); + } + + /// + /// Resets the input state of the action executor. + /// + public void ResetInputState() + { + this.Execute(DriverCommand.CancelActions, null); + } + + /// + /// Close the Browser and Dispose of WebDriver + /// + public void Quit() + { + this.Dispose(); + } + + /// + /// Method to give you access to switch frames and windows + /// + /// Returns an Object that allows you to Switch Frames and Windows + /// + /// + /// IWebDriver driver = new InternetExplorerDriver(); + /// driver.SwitchTo().Frame("FrameName"); + /// + /// + public ITargetLocator SwitchTo() + { + return new TargetLocator(this); + } + + /// + /// Instructs the driver to change its settings. + /// + /// An object allowing the user to change + /// the settings of the driver. + public IOptions Manage() + { + return new OptionsManager(this); + } + + /// + /// Instructs the driver to navigate the browser to another location. + /// + /// An object allowing the user to access + /// the browser's history and to navigate to a given URL. + public INavigation Navigate() + { + return new Navigator(this); + } - /// - /// Registers a set of commands to be executed with this driver instance. - /// - /// An where the keys are the names of the commands to register, and the values are the objects describing the commands. - public void RegisterCustomDriverCommands(IReadOnlyDictionary commands) + /// + /// Executes a command with this driver. + /// + /// The name of the command to execute. The command name must be registered with the command executor, and must not be a command name known to this driver type. + /// A containing the names and values of the parameters of the command. + /// A containing information about the success or failure of the command and any data returned by the command. + /// The command returned an exceptional value. + public object? ExecuteCustomDriverCommand(string driverCommandToExecute, Dictionary parameters) + { + if (this.registeredCommands.Contains(driverCommandToExecute)) { - foreach (KeyValuePair entry in commands) - { - this.RegisterCustomDriverCommand(entry.Key, entry.Value); - } + throw new WebDriverException(string.Format(CultureInfo.InvariantCulture, "A command named '{0}' is predefined by the driver class and cannot be executed with ExecuteCustomDriverCommand. It should be executed using a named method instead.", driverCommandToExecute)); } - /// - /// Registers a command to be executed with this driver instance. - /// - /// The unique name of the command to register. - /// The object describing the command. - /// if the command was registered; otherwise, . - public bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo) + return this.Execute(driverCommandToExecute, parameters).Value; + } + + /// + /// Registers a set of commands to be executed with this driver instance. + /// + /// An where the keys are the names of the commands to register, and the values are the objects describing the commands. + public void RegisterCustomDriverCommands(IReadOnlyDictionary commands) + { + foreach (KeyValuePair entry in commands) { - return this.RegisterDriverCommand(commandName, commandInfo, false); + this.RegisterCustomDriverCommand(entry.Key, entry.Value); } + } + + /// + /// Registers a command to be executed with this driver instance. + /// + /// The unique name of the command to register. + /// The object describing the command. + /// if the command was registered; otherwise, . + public bool RegisterCustomDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo) + { + return this.RegisterDriverCommand(commandName, commandInfo, false); + } - /// - /// Registers a command to be executed with this driver instance. - /// - /// The unique name of the command to register. - /// The object describing the command. - /// if the registered command is internal to the driver; otherwise . - /// if the command was registered; otherwise, . - internal bool RegisterDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo, bool isInternalCommand) + /// + /// Registers a command to be executed with this driver instance. + /// + /// The unique name of the command to register. + /// The object describing the command. + /// if the registered command is internal to the driver; otherwise . + /// if the command was registered; otherwise, . + internal bool RegisterDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo, bool isInternalCommand) + { + if (this.CommandExecutor.TryAddCommand(commandName, commandInfo)) { - if (this.CommandExecutor.TryAddCommand(commandName, commandInfo)) + if (isInternalCommand) { - if (isInternalCommand) - { - this.registeredCommands.Add(commandName); - } - - return true; + this.registeredCommands.Add(commandName); } - return false; + return true; } - /// - /// Find the element in the response - /// - /// Response from the browser - /// Element from the page, or if the response does not contain a dictionary. - internal IWebElement? GetElementFromResponse(Response response) - { - if (response.Value is Dictionary elementDictionary) - { - return this.elementFactory.CreateElement(elementDictionary); - } + return false; + } - return null; + /// + /// Find the element in the response + /// + /// Response from the browser + /// Element from the page, or if the response does not contain a dictionary. + internal IWebElement? GetElementFromResponse(Response response) + { + if (response.Value is Dictionary elementDictionary) + { + return this.elementFactory.CreateElement(elementDictionary); } - /// - /// Finds the elements that are in the response - /// - /// Response from the browser - /// Collection of elements - internal ReadOnlyCollection GetElementsFromResponse(Response response) + return null; + } + + /// + /// Finds the elements that are in the response + /// + /// Response from the browser + /// Collection of elements + internal ReadOnlyCollection GetElementsFromResponse(Response response) + { + List toReturn = new List(); + if (response.Value is object?[] elements) { - List toReturn = new List(); - if (response.Value is object?[] elements) + foreach (object? elementObject in elements) { - foreach (object? elementObject in elements) + if (elementObject is Dictionary elementDictionary) { - if (elementObject is Dictionary elementDictionary) - { - WebElement element = this.elementFactory.CreateElement(elementDictionary); - toReturn.Add(element); - } + WebElement element = this.elementFactory.CreateElement(elementDictionary); + toReturn.Add(element); } } - - return toReturn.AsReadOnly(); } - /// - /// Executes a command with this driver. - /// - /// A value representing the command to execute. - /// A containing the names and values of the parameters of the command. - /// A containing information about the success or failure of the command and any data returned by the command. - /// If is . - protected internal virtual Response Execute(string driverCommandToExecute, Dictionary + /// Executes a command with this driver. + /// + /// A value representing the command to execute. + /// A containing the names and values of the parameters of the command. + /// A containing information about the success or failure of the command and any data returned by the command. + /// If is . + protected internal virtual Response Execute(string driverCommandToExecute, Dictionary? parameters) - { - return Task.Run(() => this.ExecuteAsync(driverCommandToExecute, parameters)).GetAwaiter().GetResult(); - } + >? parameters) + { + return Task.Run(() => this.ExecuteAsync(driverCommandToExecute, parameters)).GetAwaiter().GetResult(); + } - /// - /// Executes a command with this driver. - /// - /// A value representing the command to execute. - /// A containing the names and values of the parameters of the command. - /// A containing information about the success or failure of the command and any data returned by the command. - /// If is . - protected internal virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary + /// Executes a command with this driver. + /// + /// A value representing the command to execute. + /// A containing the names and values of the parameters of the command. + /// A containing information about the success or failure of the command and any data returned by the command. + /// If is . + protected internal virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary? parameters) - { - Command commandToExecute = new Command(SessionId, driverCommandToExecute, parameters); - - Response commandResponse = await this.CommandExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false); + >? parameters) + { + Command commandToExecute = new Command(SessionId, driverCommandToExecute, parameters); - if (commandResponse.Status != WebDriverResult.Success) - { - UnpackAndThrowOnError(commandResponse, driverCommandToExecute); - } + Response commandResponse = await this.CommandExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false); - return commandResponse; + if (commandResponse.Status != WebDriverResult.Success) + { + UnpackAndThrowOnError(commandResponse, driverCommandToExecute); } - /// - /// Starts a session with the driver - /// - /// Capabilities of the browser - [MemberNotNull(nameof(SessionId))] - [MemberNotNull(nameof(Capabilities))] - protected void StartSession(ICapabilities capabilities) + return commandResponse; + } + + /// + /// Starts a session with the driver + /// + /// Capabilities of the browser + [MemberNotNull(nameof(SessionId))] + [MemberNotNull(nameof(Capabilities))] + protected void StartSession(ICapabilities capabilities) + { + Dictionary parameters = new Dictionary(); + + // If the object passed into the RemoteWebDriver constructor is a + // RemoteSessionSettings object, it is expected that all intermediate + // and end nodes are compliant with the W3C WebDriver Specification, + // and therefore will already contain all of the appropriate values + // for establishing a session. + if (capabilities is not RemoteSessionSettings remoteSettings) { - Dictionary parameters = new Dictionary(); - - // If the object passed into the RemoteWebDriver constructor is a - // RemoteSessionSettings object, it is expected that all intermediate - // and end nodes are compliant with the W3C WebDriver Specification, - // and therefore will already contain all of the appropriate values - // for establishing a session. - if (capabilities is not RemoteSessionSettings remoteSettings) - { - Dictionary matchCapabilities = this.GetCapabilitiesDictionary(capabilities); + Dictionary matchCapabilities = this.GetCapabilitiesDictionary(capabilities); - List firstMatchCapabilitiesList = new List(); - firstMatchCapabilitiesList.Add(matchCapabilities); + List firstMatchCapabilitiesList = new List(); + firstMatchCapabilitiesList.Add(matchCapabilities); - Dictionary specCompliantCapabilitiesDictionary = new Dictionary(); - specCompliantCapabilitiesDictionary["firstMatch"] = firstMatchCapabilitiesList; + Dictionary specCompliantCapabilitiesDictionary = new Dictionary(); + specCompliantCapabilitiesDictionary["firstMatch"] = firstMatchCapabilitiesList; - parameters.Add("capabilities", specCompliantCapabilitiesDictionary); - } - else - { - parameters.Add("capabilities", remoteSettings.ToDictionary()); - } + parameters.Add("capabilities", specCompliantCapabilitiesDictionary); + } + else + { + parameters.Add("capabilities", remoteSettings.ToDictionary()); + } - Response response = this.Execute(DriverCommand.NewSession, parameters); + Response response = this.Execute(DriverCommand.NewSession, parameters); - response.EnsureValueIsNotNull(); - if (response.Value is not Dictionary rawCapabilities) - { - string errorMessage = string.Format(CultureInfo.InvariantCulture, "The new session command returned a value ('{0}') that is not a valid JSON object.", response.Value); - throw new WebDriverException(errorMessage); - } + response.EnsureValueIsNotNull(); + if (response.Value is not Dictionary rawCapabilities) + { + string errorMessage = string.Format(CultureInfo.InvariantCulture, "The new session command returned a value ('{0}') that is not a valid JSON object.", response.Value); + throw new WebDriverException(errorMessage); + } + + this.Capabilities = new ReturnedCapabilities(rawCapabilities); - this.Capabilities = new ReturnedCapabilities(rawCapabilities); + string sessionId = response.SessionId ?? throw new WebDriverException($"The remote end did not respond with ID of a session when it was required. {response.Value}"); + this.SessionId = new SessionId(sessionId); + } - string sessionId = response.SessionId ?? throw new WebDriverException($"The remote end did not respond with ID of a session when it was required. {response.Value}"); - this.SessionId = new SessionId(sessionId); + /// + /// Gets the capabilities as a dictionary. + /// + /// The dictionary to return. + /// A Dictionary consisting of the capabilities requested. + /// This method is only transitional. Do not rely on it. It will be removed + /// once browser driver capability formats stabilize. + /// If is . + protected virtual Dictionary GetCapabilitiesDictionary(ICapabilities capabilitiesToConvert) + { + if (capabilitiesToConvert is null) + { + throw new ArgumentNullException(nameof(capabilitiesToConvert)); } - /// - /// Gets the capabilities as a dictionary. - /// - /// The dictionary to return. - /// A Dictionary consisting of the capabilities requested. - /// This method is only transitional. Do not rely on it. It will be removed - /// once browser driver capability formats stabilize. - /// If is . - protected virtual Dictionary GetCapabilitiesDictionary(ICapabilities capabilitiesToConvert) + Dictionary capabilitiesDictionary = new Dictionary(); + + foreach (KeyValuePair entry in ((IHasCapabilitiesDictionary)capabilitiesToConvert).CapabilitiesDictionary) { - if (capabilitiesToConvert is null) + if (CapabilityType.IsSpecCompliantCapabilityName(entry.Key)) { - throw new ArgumentNullException(nameof(capabilitiesToConvert)); + capabilitiesDictionary.Add(entry.Key, entry.Value); } + } + + return capabilitiesDictionary; + } - Dictionary capabilitiesDictionary = new Dictionary(); + /// + /// Registers a command to be executed with this driver instance as an internally known driver command. + /// + /// The unique name of the command to register. + /// The object describing the command. + /// if the command was registered; otherwise, . + protected bool RegisterInternalDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo) + { + return this.RegisterDriverCommand(commandName, commandInfo, true); + } - foreach (KeyValuePair entry in ((IHasCapabilitiesDictionary)capabilitiesToConvert).CapabilitiesDictionary) + /// + /// Stops the client from running + /// + /// if its in the process of disposing + protected virtual void Dispose(bool disposing) + { + try + { + if (this.SessionId is not null) { - if (CapabilityType.IsSpecCompliantCapabilityName(entry.Key)) - { - capabilitiesDictionary.Add(entry.Key, entry.Value); - } + this.Execute(DriverCommand.Quit, null); } - - return capabilitiesDictionary; } - - /// - /// Registers a command to be executed with this driver instance as an internally known driver command. - /// - /// The unique name of the command to register. - /// The object describing the command. - /// if the command was registered; otherwise, . - protected bool RegisterInternalDriverCommand(string commandName, [NotNullWhen(true)] CommandInfo? commandInfo) + catch (NotImplementedException) { - return this.RegisterDriverCommand(commandName, commandInfo, true); } - - /// - /// Stops the client from running - /// - /// if its in the process of disposing - protected virtual void Dispose(bool disposing) + catch (InvalidOperationException) { - try - { - if (this.SessionId is not null) - { - this.Execute(DriverCommand.Quit, null); - } - } - catch (NotImplementedException) - { - } - catch (InvalidOperationException) - { - } - catch (WebDriverException) - { - } - finally - { - this.SessionId = null!; - } - - this.CommandExecutor.Dispose(); + } + catch (WebDriverException) + { + } + finally + { + this.SessionId = null!; } - private static void UnpackAndThrowOnError(Response errorResponse, string commandToExecute) + this.CommandExecutor.Dispose(); + } + + private static void UnpackAndThrowOnError(Response errorResponse, string commandToExecute) + { + // Check the status code of the error, and only handle if not success. + if (errorResponse.Status != WebDriverResult.Success) { - // Check the status code of the error, and only handle if not success. - if (errorResponse.Status != WebDriverResult.Success) + if (errorResponse.Value is Dictionary errorAsDictionary) { - if (errorResponse.Value is Dictionary errorAsDictionary) + ErrorResponse errorResponseObject = new ErrorResponse(errorAsDictionary); + string errorMessage = errorResponseObject.Message; + switch (errorResponse.Status) { - ErrorResponse errorResponseObject = new ErrorResponse(errorAsDictionary); - string errorMessage = errorResponseObject.Message; - switch (errorResponse.Status) - { - case WebDriverResult.NoSuchElement: - throw new NoSuchElementException(errorMessage); + case WebDriverResult.NoSuchElement: + throw new NoSuchElementException(errorMessage); - case WebDriverResult.NoSuchFrame: - throw new NoSuchFrameException(errorMessage); + case WebDriverResult.NoSuchFrame: + throw new NoSuchFrameException(errorMessage); - case WebDriverResult.UnknownCommand: - throw new NotImplementedException(errorMessage); + case WebDriverResult.UnknownCommand: + throw new NotImplementedException(errorMessage); - case WebDriverResult.ObsoleteElement: - throw new StaleElementReferenceException(errorMessage); + case WebDriverResult.ObsoleteElement: + throw new StaleElementReferenceException(errorMessage); - case WebDriverResult.ElementClickIntercepted: - throw new ElementClickInterceptedException(errorMessage); + case WebDriverResult.ElementClickIntercepted: + throw new ElementClickInterceptedException(errorMessage); - case WebDriverResult.ElementNotInteractable: - throw new ElementNotInteractableException(errorMessage); + case WebDriverResult.ElementNotInteractable: + throw new ElementNotInteractableException(errorMessage); - case WebDriverResult.InvalidElementState: - throw new InvalidElementStateException(errorMessage); + case WebDriverResult.InvalidElementState: + throw new InvalidElementStateException(errorMessage); - case WebDriverResult.Timeout: - throw new WebDriverTimeoutException(errorMessage); + case WebDriverResult.Timeout: + throw new WebDriverTimeoutException(errorMessage); - case WebDriverResult.NoSuchWindow: - throw new NoSuchWindowException(errorMessage); + case WebDriverResult.NoSuchWindow: + throw new NoSuchWindowException(errorMessage); - case WebDriverResult.InvalidCookieDomain: - throw new InvalidCookieDomainException(errorMessage); + case WebDriverResult.InvalidCookieDomain: + throw new InvalidCookieDomainException(errorMessage); - case WebDriverResult.UnableToSetCookie: - throw new UnableToSetCookieException(errorMessage); + case WebDriverResult.UnableToSetCookie: + throw new UnableToSetCookieException(errorMessage); - case WebDriverResult.AsyncScriptTimeout: - throw new WebDriverTimeoutException(errorMessage); + case WebDriverResult.AsyncScriptTimeout: + throw new WebDriverTimeoutException(errorMessage); - case WebDriverResult.UnexpectedAlertOpen: - // TODO(JimEvans): Handle the case where the unexpected alert setting - // has been set to "ignore", so there is still a valid alert to be - // handled. - string? alertText = null; - if (errorAsDictionary.TryGetValue("alert", out object? alert)) + case WebDriverResult.UnexpectedAlertOpen: + // TODO(JimEvans): Handle the case where the unexpected alert setting + // has been set to "ignore", so there is still a valid alert to be + // handled. + string? alertText = null; + if (errorAsDictionary.TryGetValue("alert", out object? alert)) + { + if (alert is Dictionary alertDescription + && alertDescription.TryGetValue("text", out object? text)) { - if (alert is Dictionary alertDescription - && alertDescription.TryGetValue("text", out object? text)) - { - alertText = text?.ToString(); - } + alertText = text?.ToString(); } - else if (errorAsDictionary.TryGetValue("data", out object? data)) + } + else if (errorAsDictionary.TryGetValue("data", out object? data)) + { + if (data is Dictionary alertData + && alertData.TryGetValue("text", out object? dataText)) { - if (data is Dictionary alertData - && alertData.TryGetValue("text", out object? dataText)) - { - alertText = dataText?.ToString(); - } + alertText = dataText?.ToString(); } + } - throw new UnhandledAlertException(errorMessage, alertText ?? string.Empty); + throw new UnhandledAlertException(errorMessage, alertText ?? string.Empty); - case WebDriverResult.NoAlertPresent: - throw new NoAlertPresentException(errorMessage); + case WebDriverResult.NoAlertPresent: + throw new NoAlertPresentException(errorMessage); - case WebDriverResult.InvalidSelector: - throw new InvalidSelectorException(errorMessage); + case WebDriverResult.InvalidSelector: + throw new InvalidSelectorException(errorMessage); - case WebDriverResult.NoSuchDriver: - throw new WebDriverException(errorMessage); + case WebDriverResult.NoSuchDriver: + throw new WebDriverException(errorMessage); - case WebDriverResult.InvalidArgument: - throw new WebDriverArgumentException(errorMessage); + case WebDriverResult.InvalidArgument: + throw new WebDriverArgumentException(errorMessage); - case WebDriverResult.UnexpectedJavaScriptError: - throw new JavaScriptException(errorMessage); + case WebDriverResult.UnexpectedJavaScriptError: + throw new JavaScriptException(errorMessage); - case WebDriverResult.MoveTargetOutOfBounds: - throw new MoveTargetOutOfBoundsException(errorMessage); + case WebDriverResult.MoveTargetOutOfBounds: + throw new MoveTargetOutOfBoundsException(errorMessage); - case WebDriverResult.NoSuchShadowRoot: - throw new NoSuchShadowRootException(errorMessage); + case WebDriverResult.NoSuchShadowRoot: + throw new NoSuchShadowRootException(errorMessage); - case WebDriverResult.DetachedShadowRoot: - throw new DetachedShadowRootException(errorMessage); + case WebDriverResult.DetachedShadowRoot: + throw new DetachedShadowRootException(errorMessage); - case WebDriverResult.InsecureCertificate: - throw new InsecureCertificateException(errorMessage); + case WebDriverResult.InsecureCertificate: + throw new InsecureCertificateException(errorMessage); - case WebDriverResult.UnknownError: - throw new UnknownErrorException(errorMessage); + case WebDriverResult.UnknownError: + throw new UnknownErrorException(errorMessage); - case WebDriverResult.UnknownMethod: - throw new UnknownMethodException(errorMessage); + case WebDriverResult.UnknownMethod: + throw new UnknownMethodException(errorMessage); - case WebDriverResult.UnsupportedOperation: - throw new UnsupportedOperationException(errorMessage); + case WebDriverResult.UnsupportedOperation: + throw new UnsupportedOperationException(errorMessage); - case WebDriverResult.NoSuchCookie: - throw new NoSuchCookieException(errorMessage); + case WebDriverResult.NoSuchCookie: + throw new NoSuchCookieException(errorMessage); - default: - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", errorMessage, errorResponse.Status)); - } + default: + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", errorMessage, errorResponse.Status)); } - - throw new WebDriverException($"The {commandToExecute} command returned an unexpected error. {errorResponse.Value}"); } - } - /// - /// Executes JavaScript in the context of the currently selected frame or window using a specific command. - /// - /// The JavaScript code to execute. - /// The name of the command to execute. - /// The arguments to the script. - /// The value returned by the script. - protected object? ExecuteScriptCommand(string script, string commandName, params object?[]? args) - { - object?[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args); + throw new WebDriverException($"The {commandToExecute} command returned an unexpected error. {errorResponse.Value}"); + } + } - Dictionary parameters = new Dictionary(); - parameters.Add("script", script); + /// + /// Executes JavaScript in the context of the currently selected frame or window using a specific command. + /// + /// The JavaScript code to execute. + /// The name of the command to execute. + /// The arguments to the script. + /// The value returned by the script. + protected object? ExecuteScriptCommand(string script, string commandName, params object?[]? args) + { + object?[] convertedArgs = ConvertArgumentsToJavaScriptObjects(args); - if (convertedArgs != null && convertedArgs.Length > 0) - { - parameters.Add("args", convertedArgs); - } - else - { - parameters.Add("args", new object[] { }); - } + Dictionary parameters = new Dictionary(); + parameters.Add("script", script); - Response commandResponse = this.Execute(commandName, parameters); - return this.ParseJavaScriptReturnValue(commandResponse.Value); + if (convertedArgs != null && convertedArgs.Length > 0) + { + parameters.Add("args", convertedArgs); } - - private static object? ConvertObjectToJavaScriptObject(object? arg) + else { - IWebDriverObjectReference? argAsObjectReference = arg as IWebDriverObjectReference; + parameters.Add("args", new object[] { }); + } - if (argAsObjectReference == null && arg is IWrapsElement argAsWrapsElement) - { - argAsObjectReference = argAsWrapsElement.WrappedElement as IWebDriverObjectReference; - } + Response commandResponse = this.Execute(commandName, parameters); + return this.ParseJavaScriptReturnValue(commandResponse.Value); + } - object? converted; + private static object? ConvertObjectToJavaScriptObject(object? arg) + { + IWebDriverObjectReference? argAsObjectReference = arg as IWebDriverObjectReference; - if (arg is string || arg is float || arg is double || arg is int || arg is long || arg is bool || arg == null) - { - converted = arg; - } - else if (argAsObjectReference != null) - { - Dictionary webDriverObjectReferenceDictionary = argAsObjectReference.ToDictionary(); - converted = webDriverObjectReferenceDictionary; - } - else if (arg is IDictionary argAsDictionary) - { - // Note that we must check for the argument being a dictionary before - // checking for IEnumerable, since dictionaries also implement IEnumerable. - // Additionally, JavaScript objects have property names as strings, so all - // keys will be converted to strings. - Dictionary dictionary = new Dictionary(); - foreach (DictionaryEntry argEntry in argAsDictionary) - { - dictionary.Add(argEntry.Key.ToString()!, ConvertObjectToJavaScriptObject(argEntry.Value)); - } + if (argAsObjectReference == null && arg is IWrapsElement argAsWrapsElement) + { + argAsObjectReference = argAsWrapsElement.WrappedElement as IWebDriverObjectReference; + } - converted = dictionary; - } - else if (arg is IEnumerable argAsEnumerable) - { - List objectList = new List(); - foreach (object? item in argAsEnumerable) - { - objectList.Add(ConvertObjectToJavaScriptObject(item)); - } + object? converted; - converted = objectList.ToArray(); - } - else + if (arg is string || arg is float || arg is double || arg is int || arg is long || arg is bool || arg == null) + { + converted = arg; + } + else if (argAsObjectReference != null) + { + Dictionary webDriverObjectReferenceDictionary = argAsObjectReference.ToDictionary(); + converted = webDriverObjectReferenceDictionary; + } + else if (arg is IDictionary argAsDictionary) + { + // Note that we must check for the argument being a dictionary before + // checking for IEnumerable, since dictionaries also implement IEnumerable. + // Additionally, JavaScript objects have property names as strings, so all + // keys will be converted to strings. + Dictionary dictionary = new Dictionary(); + foreach (DictionaryEntry argEntry in argAsDictionary) { - throw new ArgumentException("Argument is of an illegal type: " + arg.ToString(), nameof(arg)); + dictionary.Add(argEntry.Key.ToString()!, ConvertObjectToJavaScriptObject(argEntry.Value)); } - return converted; + converted = dictionary; } - - /// - /// Converts the arguments to JavaScript objects. - /// - /// The arguments. - /// The list of the arguments converted to JavaScript objects. - private static object?[] ConvertArgumentsToJavaScriptObjects(object?[]? args) + else if (arg is IEnumerable argAsEnumerable) { - if (args == null) + List objectList = new List(); + foreach (object? item in argAsEnumerable) { - return new object?[] { null }; + objectList.Add(ConvertObjectToJavaScriptObject(item)); } - for (int i = 0; i < args.Length; i++) - { - args[i] = ConvertObjectToJavaScriptObject(args[i]); - } + converted = objectList.ToArray(); + } + else + { + throw new ArgumentException("Argument is of an illegal type: " + arg.ToString(), nameof(arg)); + } + + return converted; + } - return args; + /// + /// Converts the arguments to JavaScript objects. + /// + /// The arguments. + /// The list of the arguments converted to JavaScript objects. + private static object?[] ConvertArgumentsToJavaScriptObjects(object?[]? args) + { + if (args == null) + { + return new object?[] { null }; } - private object? ParseJavaScriptReturnValue(object? responseValue) + for (int i = 0; i < args.Length; i++) { - object? returnValue; + args[i] = ConvertObjectToJavaScriptObject(args[i]); + } + + return args; + } - if (responseValue is Dictionary resultAsDictionary) + private object? ParseJavaScriptReturnValue(object? responseValue) + { + object? returnValue; + + if (responseValue is Dictionary resultAsDictionary) + { + if (this.elementFactory.ContainsElementReference(resultAsDictionary)) { - if (this.elementFactory.ContainsElementReference(resultAsDictionary)) - { - returnValue = this.elementFactory.CreateElement(resultAsDictionary); - } - else if (ShadowRoot.TryCreate(this, resultAsDictionary, out ShadowRoot? shadowRoot)) - { - returnValue = shadowRoot; - } - else + returnValue = this.elementFactory.CreateElement(resultAsDictionary); + } + else if (ShadowRoot.TryCreate(this, resultAsDictionary, out ShadowRoot? shadowRoot)) + { + returnValue = shadowRoot; + } + else + { + // Recurse through the dictionary, re-parsing each value. + string[] keyCopy = new string[resultAsDictionary.Keys.Count]; + resultAsDictionary.Keys.CopyTo(keyCopy, 0); + foreach (string key in keyCopy) { - // Recurse through the dictionary, re-parsing each value. - string[] keyCopy = new string[resultAsDictionary.Keys.Count]; - resultAsDictionary.Keys.CopyTo(keyCopy, 0); - foreach (string key in keyCopy) - { - resultAsDictionary[key] = this.ParseJavaScriptReturnValue(resultAsDictionary[key]); - } - - returnValue = resultAsDictionary; + resultAsDictionary[key] = this.ParseJavaScriptReturnValue(resultAsDictionary[key]); } + + returnValue = resultAsDictionary; } - else if (responseValue is object?[] resultAsArray) + } + else if (responseValue is object?[] resultAsArray) + { + bool allElementsAreWebElements = true; + List toReturn = new List(resultAsArray.Length); + foreach (object? item in resultAsArray) { - bool allElementsAreWebElements = true; - List toReturn = new List(resultAsArray.Length); - foreach (object? item in resultAsArray) + object? parsedItem = this.ParseJavaScriptReturnValue(item); + if (parsedItem is not IWebElement) { - object? parsedItem = this.ParseJavaScriptReturnValue(item); - if (parsedItem is not IWebElement) - { - allElementsAreWebElements = false; - } - - toReturn.Add(parsedItem); + allElementsAreWebElements = false; } - if (toReturn.Count > 0 && allElementsAreWebElements) - { - List elementList = new List(resultAsArray.Length); - foreach (object? listItem in toReturn) - { - elementList.Add((IWebElement)listItem!); - } + toReturn.Add(parsedItem); + } - returnValue = elementList.AsReadOnly(); - } - else + if (toReturn.Count > 0 && allElementsAreWebElements) + { + List elementList = new List(resultAsArray.Length); + foreach (object? listItem in toReturn) { - returnValue = toReturn.AsReadOnly(); + elementList.Add((IWebElement)listItem!); } + + returnValue = elementList.AsReadOnly(); } else { - returnValue = responseValue; + returnValue = toReturn.AsReadOnly(); } - - return returnValue; } - - /// - /// Creates a Virtual Authenticator. - /// - /// Virtual Authenticator Options. - /// Authenticator id as string - /// If is . - public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) + else { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + returnValue = responseValue; + } - Response commandResponse = this.Execute(DriverCommand.AddVirtualAuthenticator, options.ToDictionary()); + return returnValue; + } - commandResponse.EnsureValueIsNotNull(); - string id = (string)commandResponse.Value; - this.AuthenticatorId = id; - return id; + /// + /// Creates a Virtual Authenticator. + /// + /// Virtual Authenticator Options. + /// Authenticator id as string + /// If is . + public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) + { + if (options is null) + { + throw new ArgumentNullException(nameof(options)); } - /// - /// Removes the Virtual Authenticator - /// - /// Id as string that uniquely identifies a Virtual Authenticator. - /// If is . - public void RemoveVirtualAuthenticator(string authenticatorId) - { - if (authenticatorId is null) - { - throw new ArgumentNullException(nameof(authenticatorId)); - } + Response commandResponse = this.Execute(DriverCommand.AddVirtualAuthenticator, options.ToDictionary()); - Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", authenticatorId); + commandResponse.EnsureValueIsNotNull(); + string id = (string)commandResponse.Value; + this.AuthenticatorId = id; + return id; + } - this.Execute(DriverCommand.RemoveVirtualAuthenticator, parameters); - this.AuthenticatorId = null; + /// + /// Removes the Virtual Authenticator + /// + /// Id as string that uniquely identifies a Virtual Authenticator. + /// If is . + public void RemoveVirtualAuthenticator(string authenticatorId) + { + if (authenticatorId is null) + { + throw new ArgumentNullException(nameof(authenticatorId)); } - /// - /// Gets the cached virtual authenticator ID, or if no authenticator ID is set. - /// - public string? AuthenticatorId { get; private set; } - - /// - /// Add a credential to the Virtual Authenticator/ - /// - /// The credential to be stored in the Virtual Authenticator - /// If is . - /// If a Virtual Authenticator has not been added yet. - public void AddCredential(Credential credential) - { - if (credential is null) - { - throw new ArgumentNullException(nameof(credential)); - } + Dictionary parameters = new Dictionary(); + parameters.Add("authenticatorId", authenticatorId); - string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + this.Execute(DriverCommand.RemoveVirtualAuthenticator, parameters); + this.AuthenticatorId = null; + } - Dictionary parameters = new Dictionary(credential.ToDictionary()); - parameters.Add("authenticatorId", authenticatorId); + /// + /// Gets the cached virtual authenticator ID, or if no authenticator ID is set. + /// + public string? AuthenticatorId { get; private set; } - this.Execute(driverCommandToExecute: DriverCommand.AddCredential, parameters); + /// + /// Add a credential to the Virtual Authenticator/ + /// + /// The credential to be stored in the Virtual Authenticator + /// If is . + /// If a Virtual Authenticator has not been added yet. + public void AddCredential(Credential credential) + { + if (credential is null) + { + throw new ArgumentNullException(nameof(credential)); } - /// - /// Retrieves all the credentials stored in the Virtual Authenticator - /// - /// List of credentials - /// If a Virtual Authenticator has not been added yet. - public List GetCredentials() - { - string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); - Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", authenticatorId); + Dictionary parameters = new Dictionary(credential.ToDictionary()); + parameters.Add("authenticatorId", authenticatorId); - Response getCredentialsResponse = this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters); + this.Execute(driverCommandToExecute: DriverCommand.AddCredential, parameters); + } - getCredentialsResponse.EnsureValueIsNotNull(); - if (getCredentialsResponse.Value is not object?[] credentialsList) - { - throw new WebDriverException($"Get credentials call succeeded, but the response was not a list of credentials: {getCredentialsResponse.Value}"); - } + /// + /// Retrieves all the credentials stored in the Virtual Authenticator + /// + /// List of credentials + /// If a Virtual Authenticator has not been added yet. + public List GetCredentials() + { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); - List credentials = new List(credentialsList.Length); - foreach (object? dictionary in credentialsList) - { - Credential credential = Credential.FromDictionary((Dictionary)dictionary!); - credentials.Add(credential); - } + Dictionary parameters = new Dictionary(); + parameters.Add("authenticatorId", authenticatorId); - return credentials; - } + Response getCredentialsResponse = this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters); - /// - /// Removes the credential identified by the credentialId from the Virtual Authenticator. - /// - /// The id as byte array that uniquely identifies a credential - /// If is . - /// If a Virtual Authenticator has not been added yet. - public void RemoveCredential(byte[] credentialId) + getCredentialsResponse.EnsureValueIsNotNull(); + if (getCredentialsResponse.Value is not object?[] credentialsList) { - RemoveCredential(Base64UrlEncoder.Encode(credentialId)); + throw new WebDriverException($"Get credentials call succeeded, but the response was not a list of credentials: {getCredentialsResponse.Value}"); } - /// - /// Removes the credential identified by the credentialId from the Virtual Authenticator. - /// - /// The id as string that uniquely identifies a credential - /// If is . - /// If a Virtual Authenticator has not been added yet. - public void RemoveCredential(string credentialId) + List credentials = new List(credentialsList.Length); + foreach (object? dictionary in credentialsList) { - if (credentialId is null) - { - throw new ArgumentNullException(nameof(credentialId)); - } + Credential credential = Credential.FromDictionary((Dictionary)dictionary!); + credentials.Add(credential); + } - string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + return credentials; + } - Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", authenticatorId); - parameters.Add("credentialId", credentialId); + /// + /// Removes the credential identified by the credentialId from the Virtual Authenticator. + /// + /// The id as byte array that uniquely identifies a credential + /// If is . + /// If a Virtual Authenticator has not been added yet. + public void RemoveCredential(byte[] credentialId) + { + RemoveCredential(Base64UrlEncoder.Encode(credentialId)); + } - this.Execute(driverCommandToExecute: DriverCommand.RemoveCredential, parameters); + /// + /// Removes the credential identified by the credentialId from the Virtual Authenticator. + /// + /// The id as string that uniquely identifies a credential + /// If is . + /// If a Virtual Authenticator has not been added yet. + public void RemoveCredential(string credentialId) + { + if (credentialId is null) + { + throw new ArgumentNullException(nameof(credentialId)); } - /// - /// Removes all the credentials stored in the Virtual Authenticator. - /// - /// If a Virtual Authenticator has not been added yet. - public void RemoveAllCredentials() - { - string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); - Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", authenticatorId); + Dictionary parameters = new Dictionary(); + parameters.Add("authenticatorId", authenticatorId); + parameters.Add("credentialId", credentialId); - this.Execute(driverCommandToExecute: DriverCommand.RemoveAllCredentials, parameters); - } + this.Execute(driverCommandToExecute: DriverCommand.RemoveCredential, parameters); + } - /// - /// Sets the isUserVerified property for the Virtual Authenticator. - /// - /// The boolean value representing value to be set - public void SetUserVerified(bool verified) - { - string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + /// + /// Removes all the credentials stored in the Virtual Authenticator. + /// + /// If a Virtual Authenticator has not been added yet. + public void RemoveAllCredentials() + { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); - Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", authenticatorId); - parameters.Add("isUserVerified", verified); + Dictionary parameters = new Dictionary(); + parameters.Add("authenticatorId", authenticatorId); - this.Execute(driverCommandToExecute: DriverCommand.SetUserVerified, parameters); - } + this.Execute(driverCommandToExecute: DriverCommand.RemoveAllCredentials, parameters); + } + + /// + /// Sets the isUserVerified property for the Virtual Authenticator. + /// + /// The boolean value representing value to be set + public void SetUserVerified(bool verified) + { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + + Dictionary parameters = new Dictionary(); + parameters.Add("authenticatorId", authenticatorId); + parameters.Add("isUserVerified", verified); + + this.Execute(driverCommandToExecute: DriverCommand.SetUserVerified, parameters); } } diff --git a/dotnet/src/webdriver/WebDriverArgumentException.cs b/dotnet/src/webdriver/WebDriverArgumentException.cs index 50c80ab10bf1c..e22c8e0ab0a0f 100644 --- a/dotnet/src/webdriver/WebDriverArgumentException.cs +++ b/dotnet/src/webdriver/WebDriverArgumentException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents exceptions that are thrown when an invalid argument is passed to a WebDriver command. +/// +[Serializable] +public class WebDriverArgumentException : WebDriverException { /// - /// Represents exceptions that are thrown when an invalid argument is passed to a WebDriver command. + /// Initializes a new instance of the class. /// - [Serializable] - public class WebDriverArgumentException : WebDriverException + public WebDriverArgumentException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public WebDriverArgumentException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public WebDriverArgumentException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public WebDriverArgumentException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public WebDriverArgumentException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public WebDriverArgumentException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/WebDriverError.cs b/dotnet/src/webdriver/WebDriverError.cs index 7481f06368b1e..cdff989d8bea8 100644 --- a/dotnet/src/webdriver/WebDriverError.cs +++ b/dotnet/src/webdriver/WebDriverError.cs @@ -20,200 +20,199 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents an error condition from a remote end using the W3C specification +/// dialect of the wire protocol. +/// +internal static class WebDriverError { /// - /// Represents an error condition from a remote end using the W3C specification - /// dialect of the wire protocol. + /// Represents the element click intercepted error. + /// + public const string ElementClickIntercepted = "element click intercepted"; + + /// + /// Represents the element not interactable error. + /// + public const string ElementNotInteractable = "element not interactable"; + + /// + /// Represents the insecure certificate error. + /// + public const string InsecureCertificate = "insecure certificate"; + + /// + /// Represents the invalid argument error. + /// + public const string InvalidArgument = "invalid argument"; + + /// + /// Represents the invalid cookie domain error. + /// + public const string InvalidCookieDomain = "invalid cookie domain"; + + /// + /// Represents the invalid element state error. + /// + public const string InvalidElementState = "invalid element state"; + + /// + /// Represents the invalid selector error. + /// + public const string InvalidSelector = "invalid selector"; + + /// + /// Represents the invalid session ID error. + /// + public const string InvalidSessionId = "invalid session id"; + + /// + /// Represents the unhandled JavaScript error. + /// + public const string JavaScriptError = "javascript error"; + + /// + /// Represents the move target out of bounds error. + /// + public const string MoveTargetOutOfBounds = "move target out of bounds"; + + /// + /// Represents the no such alert error. + /// + public const string NoSuchAlert = "no such alert"; + + /// + /// Represents the no such cookie error. + /// + public const string NoSuchCookie = "no such cookie"; + + /// + /// Represents the no such element error. + /// + public const string NoSuchElement = "no such element"; + + /// + /// Represents the no such alert frame. + /// + public const string NoSuchFrame = "no such frame"; + + /// + /// Represents the no such alert window. + /// + public const string NoSuchWindow = "no such window"; + + /// + /// Represents the no such shadow root error. + /// + public const string NoSuchShadowRoot = "no such shadow root"; + + /// + /// Represents the script timeout error. + /// + public const string ScriptTimeout = "script timeout"; + + /// + /// Represents the session not created error. + /// + public const string SessionNotCreated = "session not created"; + + /// + /// Represents the stale element reference error. /// - internal static class WebDriverError + public const string StaleElementReference = "stale element reference"; + + /// + /// Represents the detached shadow root error. + /// + public const string DetachedShadowRoot = "detached shadow root"; + + /// + /// Represents the timeout error. + /// + public const string Timeout = "timeout"; + + /// + /// Represents the unable to set cookie error. + /// + public const string UnableToSetCookie = "unable to set cookie"; + + /// + /// Represents the unable to capture screen error. + /// + public const string UnableToCaptureScreen = "unable to capture screen"; + + /// + /// Represents the unexpected alert open error. + /// + public const string UnexpectedAlertOpen = "unexpected alert open"; + + /// + /// Represents the unknown command error. + /// + public const string UnknownCommand = "unknown command"; + + /// + /// Represents an unknown error. + /// + public const string UnknownError = "unknown error"; + + /// + /// Represents the unknown method error. + /// + public const string UnknownMethod = "unknown method"; + + /// + /// Represents the unsupported operation error. + /// + public const string UnsupportedOperation = "unsupported operation"; + + private static readonly Dictionary resultMap = new Dictionary { - /// - /// Represents the element click intercepted error. - /// - public const string ElementClickIntercepted = "element click intercepted"; - - /// - /// Represents the element not interactable error. - /// - public const string ElementNotInteractable = "element not interactable"; - - /// - /// Represents the insecure certificate error. - /// - public const string InsecureCertificate = "insecure certificate"; - - /// - /// Represents the invalid argument error. - /// - public const string InvalidArgument = "invalid argument"; - - /// - /// Represents the invalid cookie domain error. - /// - public const string InvalidCookieDomain = "invalid cookie domain"; - - /// - /// Represents the invalid element state error. - /// - public const string InvalidElementState = "invalid element state"; - - /// - /// Represents the invalid selector error. - /// - public const string InvalidSelector = "invalid selector"; - - /// - /// Represents the invalid session ID error. - /// - public const string InvalidSessionId = "invalid session id"; - - /// - /// Represents the unhandled JavaScript error. - /// - public const string JavaScriptError = "javascript error"; - - /// - /// Represents the move target out of bounds error. - /// - public const string MoveTargetOutOfBounds = "move target out of bounds"; - - /// - /// Represents the no such alert error. - /// - public const string NoSuchAlert = "no such alert"; - - /// - /// Represents the no such cookie error. - /// - public const string NoSuchCookie = "no such cookie"; - - /// - /// Represents the no such element error. - /// - public const string NoSuchElement = "no such element"; - - /// - /// Represents the no such alert frame. - /// - public const string NoSuchFrame = "no such frame"; - - /// - /// Represents the no such alert window. - /// - public const string NoSuchWindow = "no such window"; - - /// - /// Represents the no such shadow root error. - /// - public const string NoSuchShadowRoot = "no such shadow root"; - - /// - /// Represents the script timeout error. - /// - public const string ScriptTimeout = "script timeout"; - - /// - /// Represents the session not created error. - /// - public const string SessionNotCreated = "session not created"; - - /// - /// Represents the stale element reference error. - /// - public const string StaleElementReference = "stale element reference"; - - /// - /// Represents the detached shadow root error. - /// - public const string DetachedShadowRoot = "detached shadow root"; - - /// - /// Represents the timeout error. - /// - public const string Timeout = "timeout"; - - /// - /// Represents the unable to set cookie error. - /// - public const string UnableToSetCookie = "unable to set cookie"; - - /// - /// Represents the unable to capture screen error. - /// - public const string UnableToCaptureScreen = "unable to capture screen"; - - /// - /// Represents the unexpected alert open error. - /// - public const string UnexpectedAlertOpen = "unexpected alert open"; - - /// - /// Represents the unknown command error. - /// - public const string UnknownCommand = "unknown command"; - - /// - /// Represents an unknown error. - /// - public const string UnknownError = "unknown error"; - - /// - /// Represents the unknown method error. - /// - public const string UnknownMethod = "unknown method"; - - /// - /// Represents the unsupported operation error. - /// - public const string UnsupportedOperation = "unsupported operation"; - - private static readonly Dictionary resultMap = new Dictionary - { - { ElementClickIntercepted, WebDriverResult.ElementClickIntercepted }, - { ElementNotInteractable, WebDriverResult.ElementNotInteractable }, - { InsecureCertificate, WebDriverResult.InsecureCertificate }, - { InvalidArgument, WebDriverResult.InvalidArgument }, - { InvalidCookieDomain, WebDriverResult.InvalidCookieDomain }, - { InvalidElementState, WebDriverResult.InvalidElementState }, - { InvalidSelector, WebDriverResult.InvalidSelector }, - { InvalidSessionId, WebDriverResult.NoSuchDriver }, - { JavaScriptError, WebDriverResult.UnexpectedJavaScriptError }, - { MoveTargetOutOfBounds, WebDriverResult.MoveTargetOutOfBounds }, - { NoSuchAlert, WebDriverResult.NoAlertPresent }, - { NoSuchCookie, WebDriverResult.NoSuchCookie }, - { NoSuchElement, WebDriverResult.NoSuchElement }, - { NoSuchFrame, WebDriverResult.NoSuchFrame }, - { NoSuchWindow, WebDriverResult.NoSuchWindow }, - { NoSuchShadowRoot, WebDriverResult.NoSuchShadowRoot }, - { ScriptTimeout, WebDriverResult.AsyncScriptTimeout }, - { SessionNotCreated, WebDriverResult.SessionNotCreated }, - { StaleElementReference, WebDriverResult.ObsoleteElement }, - { DetachedShadowRoot, WebDriverResult.DetachedShadowRoot }, - { Timeout, WebDriverResult.Timeout }, - { UnableToSetCookie, WebDriverResult.UnableToSetCookie }, - { UnableToCaptureScreen, WebDriverResult.UnableToCaptureScreen }, - { UnexpectedAlertOpen, WebDriverResult.UnexpectedAlertOpen }, - { UnknownCommand, WebDriverResult.UnknownCommand }, - { UnknownError, WebDriverResult.UnknownError }, - { UnknownMethod, WebDriverResult.UnknownMethod }, - { UnsupportedOperation, WebDriverResult.UnsupportedOperation } - }; - - /// - /// Converts a string error to a value. - /// - /// The error string to convert. - /// The converted value. - /// If is . - public static WebDriverResult ResultFromError(string error) - { - if (!resultMap.TryGetValue(error, out WebDriverResult result)) - { - return WebDriverResult.UnsupportedOperation; - } + { ElementClickIntercepted, WebDriverResult.ElementClickIntercepted }, + { ElementNotInteractable, WebDriverResult.ElementNotInteractable }, + { InsecureCertificate, WebDriverResult.InsecureCertificate }, + { InvalidArgument, WebDriverResult.InvalidArgument }, + { InvalidCookieDomain, WebDriverResult.InvalidCookieDomain }, + { InvalidElementState, WebDriverResult.InvalidElementState }, + { InvalidSelector, WebDriverResult.InvalidSelector }, + { InvalidSessionId, WebDriverResult.NoSuchDriver }, + { JavaScriptError, WebDriverResult.UnexpectedJavaScriptError }, + { MoveTargetOutOfBounds, WebDriverResult.MoveTargetOutOfBounds }, + { NoSuchAlert, WebDriverResult.NoAlertPresent }, + { NoSuchCookie, WebDriverResult.NoSuchCookie }, + { NoSuchElement, WebDriverResult.NoSuchElement }, + { NoSuchFrame, WebDriverResult.NoSuchFrame }, + { NoSuchWindow, WebDriverResult.NoSuchWindow }, + { NoSuchShadowRoot, WebDriverResult.NoSuchShadowRoot }, + { ScriptTimeout, WebDriverResult.AsyncScriptTimeout }, + { SessionNotCreated, WebDriverResult.SessionNotCreated }, + { StaleElementReference, WebDriverResult.ObsoleteElement }, + { DetachedShadowRoot, WebDriverResult.DetachedShadowRoot }, + { Timeout, WebDriverResult.Timeout }, + { UnableToSetCookie, WebDriverResult.UnableToSetCookie }, + { UnableToCaptureScreen, WebDriverResult.UnableToCaptureScreen }, + { UnexpectedAlertOpen, WebDriverResult.UnexpectedAlertOpen }, + { UnknownCommand, WebDriverResult.UnknownCommand }, + { UnknownError, WebDriverResult.UnknownError }, + { UnknownMethod, WebDriverResult.UnknownMethod }, + { UnsupportedOperation, WebDriverResult.UnsupportedOperation } + }; - return result; + /// + /// Converts a string error to a value. + /// + /// The error string to convert. + /// The converted value. + /// If is . + public static WebDriverResult ResultFromError(string error) + { + if (!resultMap.TryGetValue(error, out WebDriverResult result)) + { + return WebDriverResult.UnsupportedOperation; } + + return result; } } diff --git a/dotnet/src/webdriver/WebDriverException.cs b/dotnet/src/webdriver/WebDriverException.cs index caa1020c213ee..19699e7e9fda4 100644 --- a/dotnet/src/webdriver/WebDriverException.cs +++ b/dotnet/src/webdriver/WebDriverException.cs @@ -19,53 +19,52 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents exceptions that are thrown when an error occurs during actions. +/// +[Serializable] +public class WebDriverException : Exception { /// - /// Represents exceptions that are thrown when an error occurs during actions. + /// Intro comment for pointing to documentation /// - [Serializable] - public class WebDriverException : Exception - { - /// - /// Intro comment for pointing to documentation - /// - protected static string supportMsg = "For documentation on this error, please visit: "; + protected static string supportMsg = "For documentation on this error, please visit: "; - /// - /// Location of errors in documentation - /// - protected static string baseSupportUrl = "https://www.selenium.dev/documentation/webdriver/troubleshooting/errors"; + /// + /// Location of errors in documentation + /// + protected static string baseSupportUrl = "https://www.selenium.dev/documentation/webdriver/troubleshooting/errors"; - /// - /// Initializes a new instance of the class. - /// - public WebDriverException() - : base() - { - } + /// + /// Initializes a new instance of the class. + /// + public WebDriverException() + : base() + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public WebDriverException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public WebDriverException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public WebDriverException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public WebDriverException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/WebDriverResult.cs b/dotnet/src/webdriver/WebDriverResult.cs index eb1c5ea804fcb..51e704decec45 100644 --- a/dotnet/src/webdriver/WebDriverResult.cs +++ b/dotnet/src/webdriver/WebDriverResult.cs @@ -17,156 +17,155 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Specifies return values for actions in the driver. +/// +public enum WebDriverResult { /// - /// Specifies return values for actions in the driver. - /// - public enum WebDriverResult - { - /// - /// The action was successful. - /// - Success = 0, - - /// - /// Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that it's not active. - /// - NoSuchDriver = 6, - - /// - /// An element could not be located on the page using the given search parameters. - /// - NoSuchElement = 7, - - /// - /// A command to switch to a frame could not be satisfied because the frame could not be found. - /// - NoSuchFrame = 8, - - /// - /// A command could not be executed because the remote end is not aware of it. - /// - UnknownCommand = 9, - - /// - /// A command failed because the referenced element is no longer attached to the DOM. - /// - ObsoleteElement = 10, - - /// - /// A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element that isn't both editable and resettable. - /// - InvalidElementState = 12, - - /// - /// An unknown error occurred in the remote end while processing the command. - /// - UnknownError = 13, - - /// - /// An error occurred while executing JavaScript supplied by the user. - /// - UnexpectedJavaScriptError = 17, - - /// - /// An operation did not complete before its timeout expired. - /// - Timeout = 21, - - /// - /// A command to switch to a window could not be satisfied because the window could not be found. - /// - NoSuchWindow = 23, - - /// - /// An illegal attempt was made to set a cookie under a different domain than the current page. - /// - InvalidCookieDomain = 24, - - /// - /// A command to set a cookie's value could not be satisfied. - /// - UnableToSetCookie = 25, - - /// - /// A modal dialog was open, blocking this operation. - /// - UnexpectedAlertOpen = 26, - - /// - /// An attempt was made to operate on a modal dialog when one was not open. - /// - NoAlertPresent = 27, - - /// - /// A script did not complete before its timeout expired. - /// - AsyncScriptTimeout = 28, - - /// - /// Argument was an invalid selector. - /// - InvalidSelector = 32, - - /// - /// A new session could not be created. - /// - SessionNotCreated = 33, - - /// - /// The target for mouse interaction is not in the browser's viewport and cannot be brought into that viewport. - /// - MoveTargetOutOfBounds = 34, - - /// - /// Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate. - /// - InsecureCertificate = 59, - - /// - /// A command could not be completed because the element is not pointer- or keyboard interactable. - /// - ElementNotInteractable = 60, - - /// - /// The arguments passed to a command are either invalid or malformed. - /// - InvalidArgument = 61, - - /// - /// No cookie matching the given path name was found amongst the associated cookies of session's current browsing context's active document. - /// - NoSuchCookie = 62, - - /// - /// A screen capture was made impossible. - /// - UnableToCaptureScreen = 63, - - /// - /// The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked. - /// - ElementClickIntercepted = 64, - - /// - /// The element does not have a shadow root. - /// - NoSuchShadowRoot = 65, - - /// - /// The referenced shadow root is no longer attached to the DOM. - /// - DetachedShadowRoot = 66, - - /// - /// The requested command matched a known URL but did not match any method for that URL. - /// - UnknownMethod = 67, - - /// - /// Indicates that a command that should have executed properly cannot be supported for some reason. - /// - UnsupportedOperation = 68, - } + /// The action was successful. + /// + Success = 0, + + /// + /// Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that it's not active. + /// + NoSuchDriver = 6, + + /// + /// An element could not be located on the page using the given search parameters. + /// + NoSuchElement = 7, + + /// + /// A command to switch to a frame could not be satisfied because the frame could not be found. + /// + NoSuchFrame = 8, + + /// + /// A command could not be executed because the remote end is not aware of it. + /// + UnknownCommand = 9, + + /// + /// A command failed because the referenced element is no longer attached to the DOM. + /// + ObsoleteElement = 10, + + /// + /// A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element that isn't both editable and resettable. + /// + InvalidElementState = 12, + + /// + /// An unknown error occurred in the remote end while processing the command. + /// + UnknownError = 13, + + /// + /// An error occurred while executing JavaScript supplied by the user. + /// + UnexpectedJavaScriptError = 17, + + /// + /// An operation did not complete before its timeout expired. + /// + Timeout = 21, + + /// + /// A command to switch to a window could not be satisfied because the window could not be found. + /// + NoSuchWindow = 23, + + /// + /// An illegal attempt was made to set a cookie under a different domain than the current page. + /// + InvalidCookieDomain = 24, + + /// + /// A command to set a cookie's value could not be satisfied. + /// + UnableToSetCookie = 25, + + /// + /// A modal dialog was open, blocking this operation. + /// + UnexpectedAlertOpen = 26, + + /// + /// An attempt was made to operate on a modal dialog when one was not open. + /// + NoAlertPresent = 27, + + /// + /// A script did not complete before its timeout expired. + /// + AsyncScriptTimeout = 28, + + /// + /// Argument was an invalid selector. + /// + InvalidSelector = 32, + + /// + /// A new session could not be created. + /// + SessionNotCreated = 33, + + /// + /// The target for mouse interaction is not in the browser's viewport and cannot be brought into that viewport. + /// + MoveTargetOutOfBounds = 34, + + /// + /// Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate. + /// + InsecureCertificate = 59, + + /// + /// A command could not be completed because the element is not pointer- or keyboard interactable. + /// + ElementNotInteractable = 60, + + /// + /// The arguments passed to a command are either invalid or malformed. + /// + InvalidArgument = 61, + + /// + /// No cookie matching the given path name was found amongst the associated cookies of session's current browsing context's active document. + /// + NoSuchCookie = 62, + + /// + /// A screen capture was made impossible. + /// + UnableToCaptureScreen = 63, + + /// + /// The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked. + /// + ElementClickIntercepted = 64, + + /// + /// The element does not have a shadow root. + /// + NoSuchShadowRoot = 65, + + /// + /// The referenced shadow root is no longer attached to the DOM. + /// + DetachedShadowRoot = 66, + + /// + /// The requested command matched a known URL but did not match any method for that URL. + /// + UnknownMethod = 67, + + /// + /// Indicates that a command that should have executed properly cannot be supported for some reason. + /// + UnsupportedOperation = 68, } diff --git a/dotnet/src/webdriver/WebDriverTimeoutException.cs b/dotnet/src/webdriver/WebDriverTimeoutException.cs index 94da8c679c074..557c654bcc124 100644 --- a/dotnet/src/webdriver/WebDriverTimeoutException.cs +++ b/dotnet/src/webdriver/WebDriverTimeoutException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents exceptions that are thrown when an error occurs during actions. +/// +[Serializable] +public class WebDriverTimeoutException : WebDriverException { /// - /// Represents exceptions that are thrown when an error occurs during actions. + /// Initializes a new instance of the class. /// - [Serializable] - public class WebDriverTimeoutException : WebDriverException + public WebDriverTimeoutException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public WebDriverTimeoutException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public WebDriverTimeoutException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public WebDriverTimeoutException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public WebDriverTimeoutException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public WebDriverTimeoutException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/src/webdriver/WebElement.cs b/dotnet/src/webdriver/WebElement.cs index 44fa063f55a95..4dba6ae8837f4 100644 --- a/dotnet/src/webdriver/WebElement.cs +++ b/dotnet/src/webdriver/WebElement.cs @@ -28,726 +28,725 @@ using System.IO.Compression; using System.Linq; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// A base class representing an HTML element on a page. +/// +public class WebElement : IWebElement, IFindsElement, IWrapsDriver, ILocatable, ITakesScreenshot, IWebDriverObjectReference { /// - /// A base class representing an HTML element on a page. + /// The property name that represents a web element in the wire protocol. /// - public class WebElement : IWebElement, IFindsElement, IWrapsDriver, ILocatable, ITakesScreenshot, IWebDriverObjectReference - { - /// - /// The property name that represents a web element in the wire protocol. - /// - public const string ElementReferencePropertyName = "element-6066-11e4-a52e-4f735466cecf"; + public const string ElementReferencePropertyName = "element-6066-11e4-a52e-4f735466cecf"; - private readonly WebDriver driver; + private readonly WebDriver driver; - /// - /// Initializes a new instance of the class. - /// - /// The instance that is driving this element. - /// The ID value provided to identify the element. - /// If or are . - public WebElement(WebDriver parentDriver, string id) - { - this.driver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); - this.Id = id ?? throw new ArgumentNullException(nameof(id)); - } + /// + /// Initializes a new instance of the class. + /// + /// The instance that is driving this element. + /// The ID value provided to identify the element. + /// If or are . + public WebElement(WebDriver parentDriver, string id) + { + this.driver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + this.Id = id ?? throw new ArgumentNullException(nameof(id)); + } - /// - /// Gets the driving this element. - /// - public IWebDriver WrappedDriver => this.driver; - - /// - /// Gets the tag name of this element. - /// - /// - /// The property returns the tag name of the - /// element, not the value of the name attribute. For example, it will return - /// "input" for an element specified by the HTML markup <input name="foo" />. - /// - /// Thrown when the target element is no longer valid in the document DOM. - public virtual string TagName + /// + /// Gets the driving this element. + /// + public IWebDriver WrappedDriver => this.driver; + + /// + /// Gets the tag name of this element. + /// + /// + /// The property returns the tag name of the + /// element, not the value of the name attribute. For example, it will return + /// "input" for an element specified by the HTML markup <input name="foo" />. + /// + /// Thrown when the target element is no longer valid in the document DOM. + public virtual string TagName + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.GetElementTagName, parameters); + Response commandResponse = this.Execute(DriverCommand.GetElementTagName, parameters); - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; - } + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } + } - /// - /// Gets the innerText of this element, without any leading or trailing whitespace, - /// and with other whitespace collapsed. - /// - /// Thrown when the target element is no longer valid in the document DOM. - public virtual string Text + /// + /// Gets the innerText of this element, without any leading or trailing whitespace, + /// and with other whitespace collapsed. + /// + /// Thrown when the target element is no longer valid in the document DOM. + public virtual string Text + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.GetElementText, parameters); + Response commandResponse = this.Execute(DriverCommand.GetElementText, parameters); - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; - } + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } + } - /// - /// Gets a value indicating whether or not this element is enabled. - /// - /// The property will generally - /// return for everything except explicitly disabled input elements. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual bool Enabled + /// + /// Gets a value indicating whether or not this element is enabled. + /// + /// The property will generally + /// return for everything except explicitly disabled input elements. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual bool Enabled + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.IsElementEnabled, parameters); + Response commandResponse = this.Execute(DriverCommand.IsElementEnabled, parameters); - return Convert.ToBoolean(commandResponse.Value); - } + return Convert.ToBoolean(commandResponse.Value); } + } - /// - /// Gets a value indicating whether or not this element is selected. - /// - /// This operation only applies to input elements such as checkboxes, - /// options in a select element and radio buttons. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual bool Selected + /// + /// Gets a value indicating whether or not this element is selected. + /// + /// This operation only applies to input elements such as checkboxes, + /// options in a select element and radio buttons. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual bool Selected + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.IsElementSelected, parameters); + Response commandResponse = this.Execute(DriverCommand.IsElementSelected, parameters); - return Convert.ToBoolean(commandResponse.Value); - } + return Convert.ToBoolean(commandResponse.Value); } + } - /// - /// Gets a object containing the coordinates of the upper-left corner - /// of this element relative to the upper-left corner of the page. - /// - /// Thrown when the target element is no longer valid in the document DOM. - public virtual Point Location + /// + /// Gets a object containing the coordinates of the upper-left corner + /// of this element relative to the upper-left corner of the page. + /// + /// Thrown when the target element is no longer valid in the document DOM. + public virtual Point Location + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - - Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - if (commandResponse.Value is not Dictionary rawPoint) - { - throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}"); - } + Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters); - int x = Convert.ToInt32(rawPoint["x"], CultureInfo.InvariantCulture); - int y = Convert.ToInt32(rawPoint["y"], CultureInfo.InvariantCulture); - return new Point(x, y); + if (commandResponse.Value is not Dictionary rawPoint) + { + throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}"); } + + int x = Convert.ToInt32(rawPoint["x"], CultureInfo.InvariantCulture); + int y = Convert.ToInt32(rawPoint["y"], CultureInfo.InvariantCulture); + return new Point(x, y); } + } - /// - /// Gets a object containing the height and width of this element. - /// - /// Thrown when the target element is no longer valid in the document DOM. - public virtual Size Size + /// + /// Gets a object containing the height and width of this element. + /// + /// Thrown when the target element is no longer valid in the document DOM. + public virtual Size Size + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - - Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - if (commandResponse.Value is not Dictionary rawSize) - { - throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}"); - } + Response commandResponse = this.Execute(DriverCommand.GetElementRect, parameters); - int width = Convert.ToInt32(rawSize["width"], CultureInfo.InvariantCulture); - int height = Convert.ToInt32(rawSize["height"], CultureInfo.InvariantCulture); - return new Size(width, height); + if (commandResponse.Value is not Dictionary rawSize) + { + throw new WebDriverException($"GetElementRect command was successful, but response was not an object: {commandResponse.Value}"); } + + int width = Convert.ToInt32(rawSize["width"], CultureInfo.InvariantCulture); + int height = Convert.ToInt32(rawSize["height"], CultureInfo.InvariantCulture); + return new Size(width, height); } + } - /// - /// Gets a value indicating whether or not this element is displayed. - /// - /// The property avoids the problem - /// of having to parse an element's "style" attribute to determine - /// visibility of an element. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual bool Displayed + /// + /// Gets a value indicating whether or not this element is displayed. + /// + /// The property avoids the problem + /// of having to parse an element's "style" attribute to determine + /// visibility of an element. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual bool Displayed + { + get { - get - { - Dictionary parameters = new Dictionary(); - string atom = GetAtom("is-displayed.js"); - parameters.Add("script", atom); - parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary() }); + Dictionary parameters = new Dictionary(); + string atom = GetAtom("is-displayed.js"); + parameters.Add("script", atom); + parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary() }); - Response commandResponse = Execute(DriverCommand.ExecuteScript, parameters); + Response commandResponse = Execute(DriverCommand.ExecuteScript, parameters); - return Convert.ToBoolean(commandResponse.Value); - } + return Convert.ToBoolean(commandResponse.Value); } + } - /// - /// Gets the point where the element would be when scrolled into view. - /// - public virtual Point LocationOnScreenOnceScrolledIntoView + /// + /// Gets the point where the element would be when scrolled into view. + /// + public virtual Point LocationOnScreenOnceScrolledIntoView + { + get { - get - { - object scriptResponse = this.driver.ExecuteScript("var rect = arguments[0].getBoundingClientRect(); return {'x': rect.left, 'y': rect.top};", this)!; + object scriptResponse = this.driver.ExecuteScript("var rect = arguments[0].getBoundingClientRect(); return {'x': rect.left, 'y': rect.top};", this)!; - Dictionary rawLocation = (Dictionary)scriptResponse; + Dictionary rawLocation = (Dictionary)scriptResponse; - int x = Convert.ToInt32(rawLocation["x"], CultureInfo.InvariantCulture); - int y = Convert.ToInt32(rawLocation["y"], CultureInfo.InvariantCulture); - return new Point(x, y); - } + int x = Convert.ToInt32(rawLocation["x"], CultureInfo.InvariantCulture); + int y = Convert.ToInt32(rawLocation["y"], CultureInfo.InvariantCulture); + return new Point(x, y); } + } - /// - /// Gets the computed accessible label of this element. - /// - public virtual string ComputedAccessibleLabel + /// + /// Gets the computed accessible label of this element. + /// + public virtual string ComputedAccessibleLabel + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleLabel, parameters); + Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleLabel, parameters); - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; - } + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; } + } - /// - /// Gets the computed ARIA role for this element. - /// - public virtual string ComputedAccessibleRole + /// + /// Gets the computed ARIA role for this element. + /// + public virtual string ComputedAccessibleRole + { + get { - get - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleRole, parameters); + Response commandResponse = this.Execute(DriverCommand.GetComputedAccessibleRole, parameters); #nullable disable - // TODO: Returning this as a string is incorrect. The W3C WebDriver Specification - // needs to be updated to more thoroughly document the structure of what is returned - // by this command. Once that is done, a type-safe class will be created, and will - // be returned by this property. - return commandResponse.Value.ToString(); + // TODO: Returning this as a string is incorrect. The W3C WebDriver Specification + // needs to be updated to more thoroughly document the structure of what is returned + // by this command. Once that is done, a type-safe class will be created, and will + // be returned by this property. + return commandResponse.Value.ToString(); #nullable enable - } } + } - /// - /// Gets the coordinates identifying the location of this element using - /// various frames of reference. - /// - public virtual ICoordinates Coordinates => new ElementCoordinates(this); - - /// - /// Gets the internal ID of the element. - /// - string IWebDriverObjectReference.ObjectReferenceId => this.Id; - - /// - /// Gets the ID of the element - /// - /// This property is internal to the WebDriver instance, and is - /// not intended to be used in your code. The element's ID has no meaning - /// outside of internal WebDriver usage, so it would be improper to scope - /// it as public. However, both subclasses of - /// and the parent driver hosting the element have a need to access the - /// internal element ID. Therefore, we have two properties returning the - /// same value, one scoped as internal, the other as protected. - protected string Id { get; } - - /// - /// Clears the content of this element. - /// - /// If this element is a text entry element, the - /// method will clear the value. It has no effect on other elements. Text entry elements - /// are defined as elements with INPUT or TEXTAREA tags. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual void Clear() - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + /// + /// Gets the coordinates identifying the location of this element using + /// various frames of reference. + /// + public virtual ICoordinates Coordinates => new ElementCoordinates(this); - this.Execute(DriverCommand.ClearElement, parameters); - } + /// + /// Gets the internal ID of the element. + /// + string IWebDriverObjectReference.ObjectReferenceId => this.Id; - /// - /// Clicks this element. - /// - /// - /// Click this element. If the click causes a new page to load, the - /// method will attempt to block until the page has loaded. After calling the - /// method, you should discard all references to this - /// element unless you know that the element and the page will still be present. - /// Otherwise, any further operations performed on this element will have an undefined - /// behavior. - /// - /// Thrown when the target element is not enabled. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual void Click() - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + /// + /// Gets the ID of the element + /// + /// This property is internal to the WebDriver instance, and is + /// not intended to be used in your code. The element's ID has no meaning + /// outside of internal WebDriver usage, so it would be improper to scope + /// it as public. However, both subclasses of + /// and the parent driver hosting the element have a need to access the + /// internal element ID. Therefore, we have two properties returning the + /// same value, one scoped as internal, the other as protected. + protected string Id { get; } - this.Execute(DriverCommand.ClickElement, parameters); - } + /// + /// Clears the content of this element. + /// + /// If this element is a text entry element, the + /// method will clear the value. It has no effect on other elements. Text entry elements + /// are defined as elements with INPUT or TEXTAREA tags. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual void Clear() + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - /// - /// Finds the first using the given method. - /// - /// The locating mechanism to use. - /// The first matching on the current context. - /// If is . - /// If no element matches the criteria. - public virtual IWebElement FindElement(By by) - { - if (by == null) - { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); - } + this.Execute(DriverCommand.ClearElement, parameters); + } - return by.FindElement(this); - } + /// + /// Clicks this element. + /// + /// + /// Click this element. If the click causes a new page to load, the + /// method will attempt to block until the page has loaded. After calling the + /// method, you should discard all references to this + /// element unless you know that the element and the page will still be present. + /// Otherwise, any further operations performed on this element will have an undefined + /// behavior. + /// + /// Thrown when the target element is not enabled. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual void Click() + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + + this.Execute(DriverCommand.ClickElement, parameters); + } - /// - /// Finds a child element matching the given mechanism and value. - /// - /// The mechanism by which to find the element. - /// The value to use to search for the element. - /// The first matching the given criteria. - public virtual IWebElement FindElement(string mechanism, string value) + /// + /// Finds the first using the given method. + /// + /// The locating mechanism to use. + /// The first matching on the current context. + /// If is . + /// If no element matches the criteria. + public virtual IWebElement FindElement(By by) + { + if (by == null) { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("using", mechanism); - parameters.Add("value", value); + throw new ArgumentNullException(nameof(@by), "by cannot be null"); + } - Response commandResponse = this.Execute(DriverCommand.FindChildElement, parameters); + return by.FindElement(this); + } - return this.driver.GetElementFromResponse(commandResponse)!; - } + /// + /// Finds a child element matching the given mechanism and value. + /// + /// The mechanism by which to find the element. + /// The value to use to search for the element. + /// The first matching the given criteria. + public virtual IWebElement FindElement(string mechanism, string value) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("using", mechanism); + parameters.Add("value", value); - /// - /// Finds all IWebElements within the current context - /// using the given mechanism. - /// - /// The locating mechanism to use. - /// A of all WebElements - /// matching the current criteria, or an empty list if nothing matches. - public virtual ReadOnlyCollection FindElements(By by) - { - if (by == null) - { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); - } + Response commandResponse = this.Execute(DriverCommand.FindChildElement, parameters); - return by.FindElements(this); - } + return this.driver.GetElementFromResponse(commandResponse)!; + } - /// - /// Finds all child elements matching the given mechanism and value. - /// - /// The mechanism by which to find the elements. - /// The value to use to search for the elements. - /// A collection of all of the IWebElements matching the given criteria. - public virtual ReadOnlyCollection FindElements(string mechanism, string value) + /// + /// Finds all IWebElements within the current context + /// using the given mechanism. + /// + /// The locating mechanism to use. + /// A of all WebElements + /// matching the current criteria, or an empty list if nothing matches. + public virtual ReadOnlyCollection FindElements(By by) + { + if (by == null) { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("using", mechanism); - parameters.Add("value", value); + throw new ArgumentNullException(nameof(@by), "by cannot be null"); + } - Response commandResponse = this.Execute(DriverCommand.FindChildElements, parameters); + return by.FindElements(this); + } - return this.driver.GetElementsFromResponse(commandResponse); - } + /// + /// Finds all child elements matching the given mechanism and value. + /// + /// The mechanism by which to find the elements. + /// The value to use to search for the elements. + /// A collection of all of the IWebElements matching the given criteria. + public virtual ReadOnlyCollection FindElements(string mechanism, string value) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("using", mechanism); + parameters.Add("value", value); - /// - /// Gets the value of the specified attribute or property for this element. - /// - /// The name of the attribute or property. - /// The attribute's or property's current value. Returns a - /// if the value is not set. - /// The method will return the current value - /// of the attribute or property, even if the value has been modified after the page - /// has been loaded. Note that the value of the following attributes will be returned - /// even if there is no explicit attribute on the element: - /// - /// - /// Attribute name - /// Value returned if not explicitly specified - /// Valid element types - /// - /// - /// checked - /// checked - /// Check Box - /// - /// - /// selected - /// selected - /// Options in Select elements - /// - /// - /// disabled - /// disabled - /// Input and other UI elements - /// - /// - /// The method looks both in declared attributes in the HTML markup of the page, and - /// in the properties of the element as found when accessing the element's properties - /// via JavaScript. - /// - /// Thrown when the target element is no longer valid in the document DOM. - public virtual string? GetAttribute(string attributeName) - { - Dictionary parameters = new Dictionary(); - string atom = GetAtom("get-attribute.js"); - parameters.Add("script", atom); - parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary(), attributeName }); + Response commandResponse = this.Execute(DriverCommand.FindChildElements, parameters); - Response commandResponse = Execute(DriverCommand.ExecuteScript, parameters); + return this.driver.GetElementsFromResponse(commandResponse); + } + /// + /// Gets the value of the specified attribute or property for this element. + /// + /// The name of the attribute or property. + /// The attribute's or property's current value. Returns a + /// if the value is not set. + /// The method will return the current value + /// of the attribute or property, even if the value has been modified after the page + /// has been loaded. Note that the value of the following attributes will be returned + /// even if there is no explicit attribute on the element: + /// + /// + /// Attribute name + /// Value returned if not explicitly specified + /// Valid element types + /// + /// + /// checked + /// checked + /// Check Box + /// + /// + /// selected + /// selected + /// Options in Select elements + /// + /// + /// disabled + /// disabled + /// Input and other UI elements + /// + /// + /// The method looks both in declared attributes in the HTML markup of the page, and + /// in the properties of the element as found when accessing the element's properties + /// via JavaScript. + /// + /// Thrown when the target element is no longer valid in the document DOM. + public virtual string? GetAttribute(string attributeName) + { + Dictionary parameters = new Dictionary(); + string atom = GetAtom("get-attribute.js"); + parameters.Add("script", atom); + parameters.Add("args", new object[] { ((IWebDriverObjectReference)this).ToDictionary(), attributeName }); - // Normalize string values of boolean results as lowercase. - if (commandResponse.Value is bool b) - { - return b ? "true" : "false"; - } + Response commandResponse = Execute(DriverCommand.ExecuteScript, parameters); - return commandResponse.Value?.ToString(); - } - /// - /// Gets the value of a declared HTML attribute of this element. - /// - /// The name of the HTML attribute to get the value of. - /// The HTML attribute's current value. Returns a if the - /// value is not set or the declared attribute does not exist. - /// Thrown when the target element is no longer valid in the document DOM. - /// - /// As opposed to the method, this method - /// only returns attributes declared in the element's HTML markup. To access the value - /// of an IDL property of the element, either use the - /// method or the method. - /// - public virtual string? GetDomAttribute(string attributeName) + // Normalize string values of boolean results as lowercase. + if (commandResponse.Value is bool b) { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("name", attributeName); + return b ? "true" : "false"; + } - Response commandResponse = this.Execute(DriverCommand.GetElementAttribute, parameters); + return commandResponse.Value?.ToString(); + } - return commandResponse.Value?.ToString(); - } + /// + /// Gets the value of a declared HTML attribute of this element. + /// + /// The name of the HTML attribute to get the value of. + /// The HTML attribute's current value. Returns a if the + /// value is not set or the declared attribute does not exist. + /// Thrown when the target element is no longer valid in the document DOM. + /// + /// As opposed to the method, this method + /// only returns attributes declared in the element's HTML markup. To access the value + /// of an IDL property of the element, either use the + /// method or the method. + /// + public virtual string? GetDomAttribute(string attributeName) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("name", attributeName); - /// - /// Gets the value of a JavaScript property of this element. - /// - /// The name of the JavaScript property to get the value of. - /// The JavaScript property's current value. Returns a if the - /// value is not set or the property does not exist. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual string? GetDomProperty(string propertyName) - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("name", propertyName); + Response commandResponse = this.Execute(DriverCommand.GetElementAttribute, parameters); - Response commandResponse = this.Execute(DriverCommand.GetElementProperty, parameters); + return commandResponse.Value?.ToString(); + } - return commandResponse.Value?.ToString(); - } + /// + /// Gets the value of a JavaScript property of this element. + /// + /// The name of the JavaScript property to get the value of. + /// The JavaScript property's current value. Returns a if the + /// value is not set or the property does not exist. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual string? GetDomProperty(string propertyName) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("name", propertyName); - /// - /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. - /// - /// A shadow root representation. - /// Thrown when the target element is no longer valid in the document DOM. - /// Thrown when this element does not have a shadow root. - public virtual ISearchContext GetShadowRoot() - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + Response commandResponse = this.Execute(DriverCommand.GetElementProperty, parameters); - Response commandResponse = this.Execute(DriverCommand.GetElementShadowRoot, parameters); - if (commandResponse.Value is not Dictionary shadowRootDictionary) - { - throw new WebDriverException("Get shadow root command succeeded, but response value does not represent a shadow root."); - } + return commandResponse.Value?.ToString(); + } - if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot? shadowRoot)) - { - throw new WebDriverException("Get shadow root command succeeded, but response value does not have a shadow root key value."); - } + /// + /// Gets the representation of an element's shadow root for accessing the shadow DOM of a web component. + /// + /// A shadow root representation. + /// Thrown when the target element is no longer valid in the document DOM. + /// Thrown when this element does not have a shadow root. + public virtual ISearchContext GetShadowRoot() + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - return shadowRoot; + Response commandResponse = this.Execute(DriverCommand.GetElementShadowRoot, parameters); + if (commandResponse.Value is not Dictionary shadowRootDictionary) + { + throw new WebDriverException("Get shadow root command succeeded, but response value does not represent a shadow root."); } - /// - /// Gets the value of a CSS property of this element. - /// - /// The name of the CSS property to get the value of. - /// The value of the specified CSS property. - /// The value returned by the - /// method is likely to be unpredictable in a cross-browser environment. - /// Color values should be returned as hex strings. For example, a - /// "background-color" property set as "green" in the HTML source, will - /// return "#008000" for its value. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual string GetCssValue(string propertyName) + if (!ShadowRoot.TryCreate(this.driver, shadowRootDictionary, out ShadowRoot? shadowRoot)) { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("name", propertyName); - - Response commandResponse = this.Execute(DriverCommand.GetElementValueOfCssProperty, parameters); - - commandResponse.EnsureValueIsNotNull(); - return commandResponse.Value.ToString()!; + throw new WebDriverException("Get shadow root command succeeded, but response value does not have a shadow root key value."); } - /// - /// Gets a object representing the image of this element on the screen. - /// - /// A object containing the image. - public virtual Screenshot GetScreenshot() - { - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); + return shadowRoot; + } - // Get the screenshot as base64. - Response screenshotResponse = this.Execute(DriverCommand.ElementScreenshot, parameters); + /// + /// Gets the value of a CSS property of this element. + /// + /// The name of the CSS property to get the value of. + /// The value of the specified CSS property. + /// The value returned by the + /// method is likely to be unpredictable in a cross-browser environment. + /// Color values should be returned as hex strings. For example, a + /// "background-color" property set as "green" in the HTML source, will + /// return "#008000" for its value. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual string GetCssValue(string propertyName) + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("name", propertyName); - screenshotResponse.EnsureValueIsNotNull(); - string base64 = screenshotResponse.Value.ToString()!; + Response commandResponse = this.Execute(DriverCommand.GetElementValueOfCssProperty, parameters); - // ... and convert it. - return new Screenshot(base64); - } + commandResponse.EnsureValueIsNotNull(); + return commandResponse.Value.ToString()!; + } - /// - /// Simulates typing text into the element. - /// - /// The text to type into the element. - /// The text to be typed may include special characters like arrow keys, - /// backspaces, function keys, and so on. Valid special keys are defined in - /// . - /// - /// Thrown when the target element is not enabled. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual void SendKeys(string text) - { - if (text == null) - { - throw new ArgumentNullException(nameof(text), "text cannot be null"); - } + /// + /// Gets a object representing the image of this element on the screen. + /// + /// A object containing the image. + public virtual Screenshot GetScreenshot() + { + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); - var fileNames = text.Split('\n'); - if (fileNames.All(this.driver.FileDetector.IsFile)) - { - var uploadResults = new List(); - foreach (var fileName in fileNames) - { - uploadResults.Add(this.UploadFile(fileName)); - } - text = string.Join("\n", uploadResults); - } + // Get the screenshot as base64. + Response screenshotResponse = this.Execute(DriverCommand.ElementScreenshot, parameters); - // N.B. The Java remote server expects a CharSequence as the value input to - // SendKeys. In JSON, these are serialized as an array of strings, with a - // single character to each element of the array. Thus, we must use ToCharArray() - // to get the same effect. - // TODO: Remove either "keysToSend" or "value" property, whichever is not the - // appropriate one for spec compliance. - Dictionary parameters = new Dictionary(); - parameters.Add("id", this.Id); - parameters.Add("text", text); - parameters.Add("value", text.ToCharArray()); + screenshotResponse.EnsureValueIsNotNull(); + string base64 = screenshotResponse.Value.ToString()!; - this.Execute(DriverCommand.SendKeysToElement, parameters); + // ... and convert it. + return new Screenshot(base64); + } + + /// + /// Simulates typing text into the element. + /// + /// The text to type into the element. + /// The text to be typed may include special characters like arrow keys, + /// backspaces, function keys, and so on. Valid special keys are defined in + /// . + /// + /// Thrown when the target element is not enabled. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual void SendKeys(string text) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text), "text cannot be null"); } - /// - /// Submits this element to the web server. - /// - /// If this current element is a form, or an element within a form, - /// then this will be submitted to the web server. If this causes the current - /// page to change, then this method will attempt to block until the new page - /// is loaded. - /// Thrown when the target element is no longer valid in the document DOM. - public virtual void Submit() + var fileNames = text.Split('\n'); + if (fileNames.All(this.driver.FileDetector.IsFile)) { - string? elementType = this.GetAttribute("type"); - if (elementType != null && elementType == "submit") - { - this.Click(); - } - else + var uploadResults = new List(); + foreach (var fileName in fileNames) { - string script = "/* submitForm */var form = arguments[0];\n" + - "while (form.nodeName != \"FORM\" && form.parentNode) {\n" + - " form = form.parentNode;\n" + - "}\n" + - "if (!form) { throw Error('Unable to find containing form element'); }\n" + - "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" + - "var e = form.ownerDocument.createEvent('Event');\n" + - "e.initEvent('submit', true, true);\n" + - "if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"; - - this.driver.ExecuteScript(script, this); + uploadResults.Add(this.UploadFile(fileName)); } + text = string.Join("\n", uploadResults); } - /// - /// Returns a string that represents the current . - /// - /// A string that represents the current . - public override string ToString() + // N.B. The Java remote server expects a CharSequence as the value input to + // SendKeys. In JSON, these are serialized as an array of strings, with a + // single character to each element of the array. Thus, we must use ToCharArray() + // to get the same effect. + // TODO: Remove either "keysToSend" or "value" property, whichever is not the + // appropriate one for spec compliance. + Dictionary parameters = new Dictionary(); + parameters.Add("id", this.Id); + parameters.Add("text", text); + parameters.Add("value", text.ToCharArray()); + + this.Execute(DriverCommand.SendKeysToElement, parameters); + } + + /// + /// Submits this element to the web server. + /// + /// If this current element is a form, or an element within a form, + /// then this will be submitted to the web server. If this causes the current + /// page to change, then this method will attempt to block until the new page + /// is loaded. + /// Thrown when the target element is no longer valid in the document DOM. + public virtual void Submit() + { + string? elementType = this.GetAttribute("type"); + if (elementType != null && elementType == "submit") { - return string.Format(CultureInfo.InvariantCulture, "Element (id = {0})", this.Id); + this.Click(); } - - /// - /// Method to get the hash code of the element - /// - /// Integer of the hash code for the element - public override int GetHashCode() + else { - return this.Id.GetHashCode(); + string script = "/* submitForm */var form = arguments[0];\n" + + "while (form.nodeName != \"FORM\" && form.parentNode) {\n" + + " form = form.parentNode;\n" + + "}\n" + + "if (!form) { throw Error('Unable to find containing form element'); }\n" + + "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" + + "var e = form.ownerDocument.createEvent('Event');\n" + + "e.initEvent('submit', true, true);\n" + + "if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"; + + this.driver.ExecuteScript(script, this); } + } - /// - /// Compares if two elements are equal - /// - /// Object to compare against - /// A boolean if it is equal or not - public override bool Equals(object? obj) - { - if (obj is not IWebElement other) - { - return false; - } + /// + /// Returns a string that represents the current . + /// + /// A string that represents the current . + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Element (id = {0})", this.Id); + } - if (obj is IWrapsElement objAsWrapsElement) - { - other = objAsWrapsElement.WrappedElement; - } + /// + /// Method to get the hash code of the element + /// + /// Integer of the hash code for the element + public override int GetHashCode() + { + return this.Id.GetHashCode(); + } - if (other is not WebElement otherAsElement) - { - return false; - } + /// + /// Compares if two elements are equal + /// + /// Object to compare against + /// A boolean if it is equal or not + public override bool Equals(object? obj) + { + if (obj is not IWebElement other) + { + return false; + } - if (this.Id == otherAsElement.Id) - { - // For drivers that implement ID equality, we can check for equal IDs - // here, and expect them to be equal. There is a potential danger here - // where two different elements are assigned the same ID. - return true; - } + if (obj is IWrapsElement objAsWrapsElement) + { + other = objAsWrapsElement.WrappedElement; + } + if (other is not WebElement otherAsElement) + { return false; } - Dictionary IWebDriverObjectReference.ToDictionary() + if (this.Id == otherAsElement.Id) { - Dictionary elementDictionary = new Dictionary(); - elementDictionary.Add(ElementReferencePropertyName, this.Id); - return elementDictionary; + // For drivers that implement ID equality, we can check for equal IDs + // here, and expect them to be equal. There is a potential danger here + // where two different elements are assigned the same ID. + return true; } - /// - /// Executes a command on this element using the specified parameters. - /// - /// The to execute against this element. - /// A containing names and values of the parameters for the command. - /// The object containing the result of the command execution. - protected virtual Response Execute(string commandToExecute, Dictionary IWebDriverObjectReference.ToDictionary() + { + Dictionary elementDictionary = new Dictionary(); + elementDictionary.Add(ElementReferencePropertyName, this.Id); + return elementDictionary; + } + + /// + /// Executes a command on this element using the specified parameters. + /// + /// The to execute against this element. + /// A containing names and values of the parameters for the command. + /// The object containing the result of the command execution. + protected virtual Response Execute(string commandToExecute, Dictionary? parameters) - { - return this.driver.Execute(commandToExecute, parameters); - } + >? parameters) + { + return this.driver.Execute(commandToExecute, parameters); + } - private static string GetAtom(string atomResourceName) + private static string GetAtom(string atomResourceName) + { + string atom; + using (Stream atomStream = ResourceUtilities.GetResourceStream(atomResourceName, atomResourceName)) { - string atom; - using (Stream atomStream = ResourceUtilities.GetResourceStream(atomResourceName, atomResourceName)) + using (StreamReader atomReader = new StreamReader(atomStream)) { - using (StreamReader atomReader = new StreamReader(atomStream)) - { - atom = atomReader.ReadToEnd(); - } + atom = atomReader.ReadToEnd(); } - - string atomName = atomResourceName.Replace(".js", ""); - string wrappedAtom = string.Format(CultureInfo.InvariantCulture, "/* {0} */return ({1}).apply(null, arguments);", atomName, atom); - return wrappedAtom; } - private string UploadFile(string localFile) + string atomName = atomResourceName.Replace(".js", ""); + string wrappedAtom = string.Format(CultureInfo.InvariantCulture, "/* {0} */return ({1}).apply(null, arguments);", atomName, atom); + return wrappedAtom; + } + + private string UploadFile(string localFile) + { + string base64zip; + try { - string base64zip; - try + using (MemoryStream fileUploadMemoryStream = new MemoryStream()) { - using (MemoryStream fileUploadMemoryStream = new MemoryStream()) + using (ZipArchive zipArchive = new ZipArchive(fileUploadMemoryStream, ZipArchiveMode.Create)) { - using (ZipArchive zipArchive = new ZipArchive(fileUploadMemoryStream, ZipArchiveMode.Create)) - { - string fileName = Path.GetFileName(localFile); - zipArchive.CreateEntryFromFile(localFile, fileName); - } - base64zip = Convert.ToBase64String(fileUploadMemoryStream.ToArray()); + string fileName = Path.GetFileName(localFile); + zipArchive.CreateEntryFromFile(localFile, fileName); } + base64zip = Convert.ToBase64String(fileUploadMemoryStream.ToArray()); + } - Dictionary parameters = new Dictionary(); - parameters.Add("file", base64zip); - Response response = this.Execute(DriverCommand.UploadFile, parameters); + Dictionary parameters = new Dictionary(); + parameters.Add("file", base64zip); + Response response = this.Execute(DriverCommand.UploadFile, parameters); - response.EnsureValueIsNotNull(); - return response.Value.ToString()!; - } - catch (IOException e) - { - throw new WebDriverException("Cannot upload " + localFile, e); - } + response.EnsureValueIsNotNull(); + return response.Value.ToString()!; + } + catch (IOException e) + { + throw new WebDriverException("Cannot upload " + localFile, e); } } } diff --git a/dotnet/src/webdriver/WebElementFactory.cs b/dotnet/src/webdriver/WebElementFactory.cs index 0355184fe4d78..4cc5cc0fa35be 100644 --- a/dotnet/src/webdriver/WebElementFactory.cs +++ b/dotnet/src/webdriver/WebElementFactory.cs @@ -20,84 +20,83 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Object used to create objects for a remote driver instance. +/// +public class WebElementFactory { /// - /// Object used to create objects for a remote driver instance. + /// Initializes a new instance of the class. + /// + /// The object used to locate the elements. + /// If is . + public WebElementFactory(WebDriver parentDriver) + { + this.ParentDriver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + } + + /// + /// Gets the instance used to locate elements. /// - public class WebElementFactory + protected WebDriver ParentDriver { get; } + + /// + /// Creates a from a dictionary containing a reference to an element. + /// + /// The dictionary containing the element reference. + /// A containing the information from the specified dictionary. + /// If is . + /// If the dictionary does not contain the element reference property name. + /// If the element property is or . + public virtual WebElement CreateElement(Dictionary elementDictionary) { - /// - /// Initializes a new instance of the class. - /// - /// The object used to locate the elements. - /// If is . - public WebElementFactory(WebDriver parentDriver) + string elementId = this.GetElementId(elementDictionary); + return new WebElement(this.ParentDriver, elementId); + } + + /// + /// Gets a value indicating whether the specified dictionary represents a reference to a web element. + /// + /// The dictionary to check. + /// if the dictionary contains an element reference; otherwise, . + public bool ContainsElementReference(Dictionary elementDictionary) + { + if (elementDictionary == null) { - this.ParentDriver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + throw new ArgumentNullException(nameof(elementDictionary), "The dictionary containing the element reference cannot be null"); } - /// - /// Gets the instance used to locate elements. - /// - protected WebDriver ParentDriver { get; } + return elementDictionary.ContainsKey(WebElement.ElementReferencePropertyName); + } - /// - /// Creates a from a dictionary containing a reference to an element. - /// - /// The dictionary containing the element reference. - /// A containing the information from the specified dictionary. - /// If is . - /// If the dictionary does not contain the element reference property name. - /// If the element property is or . - public virtual WebElement CreateElement(Dictionary elementDictionary) + /// + /// Gets the internal ID associated with the element. + /// + /// A dictionary containing the element reference. + /// The internal ID associated with the element. + /// If is . + /// If the dictionary does not contain the element reference property name. + /// If the element property is or . + public string GetElementId(Dictionary elementDictionary) + { + if (elementDictionary == null) { - string elementId = this.GetElementId(elementDictionary); - return new WebElement(this.ParentDriver, elementId); + throw new ArgumentNullException(nameof(elementDictionary), "The dictionary containing the element reference cannot be null"); } - /// - /// Gets a value indicating whether the specified dictionary represents a reference to a web element. - /// - /// The dictionary to check. - /// if the dictionary contains an element reference; otherwise, . - public bool ContainsElementReference(Dictionary elementDictionary) + if (!elementDictionary.TryGetValue(WebElement.ElementReferencePropertyName, out object? elementIdObj)) { - if (elementDictionary == null) - { - throw new ArgumentNullException(nameof(elementDictionary), "The dictionary containing the element reference cannot be null"); - } - - return elementDictionary.ContainsKey(WebElement.ElementReferencePropertyName); + throw new ArgumentException("elementDictionary", "The specified dictionary does not contain an element reference"); } - /// - /// Gets the internal ID associated with the element. - /// - /// A dictionary containing the element reference. - /// The internal ID associated with the element. - /// If is . - /// If the dictionary does not contain the element reference property name. - /// If the element property is or . - public string GetElementId(Dictionary elementDictionary) + string? elementId = elementIdObj?.ToString(); + if (string.IsNullOrEmpty(elementId)) { - if (elementDictionary == null) - { - throw new ArgumentNullException(nameof(elementDictionary), "The dictionary containing the element reference cannot be null"); - } - - if (!elementDictionary.TryGetValue(WebElement.ElementReferencePropertyName, out object? elementIdObj)) - { - throw new ArgumentException("elementDictionary", "The specified dictionary does not contain an element reference"); - } - - string? elementId = elementIdObj?.ToString(); - if (string.IsNullOrEmpty(elementId)) - { - throw new InvalidOperationException("The specified element ID is either null or the empty string."); - } - - return elementId!; + throw new InvalidOperationException("The specified element ID is either null or the empty string."); } + + return elementId!; } } diff --git a/dotnet/src/webdriver/Window.cs b/dotnet/src/webdriver/Window.cs index 8534e506d1e9a..40e3855e3fff5 100644 --- a/dotnet/src/webdriver/Window.cs +++ b/dotnet/src/webdriver/Window.cs @@ -22,98 +22,97 @@ using System.Drawing; using System.Globalization; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Defines the interface through which the user can manipulate the browser window. +/// +internal sealed class Window : IWindow { + private WebDriver driver; + /// - /// Defines the interface through which the user can manipulate the browser window. + /// Initializes a new instance of the class. /// - internal sealed class Window : IWindow + /// Instance of the driver currently in use + public Window(WebDriver driver) { - private WebDriver driver; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the driver currently in use - public Window(WebDriver driver) - { - this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); - } + this.driver = driver ?? throw new ArgumentNullException(nameof(driver)); + } - /// - /// Gets or sets the position of the browser window relative to the upper-left corner of the screen. - /// - /// When setting this property, it should act as the JavaScript window.moveTo() method. - public Point Position + /// + /// Gets or sets the position of the browser window relative to the upper-left corner of the screen. + /// + /// When setting this property, it should act as the JavaScript window.moveTo() method. + public Point Position + { + get { - get - { - Response commandResponse = this.driver.Execute(DriverCommand.GetWindowRect, null); + Response commandResponse = this.driver.Execute(DriverCommand.GetWindowRect, null); - Dictionary rawPosition = (Dictionary)commandResponse.Value!; - int x = Convert.ToInt32(rawPosition["x"], CultureInfo.InvariantCulture); - int y = Convert.ToInt32(rawPosition["y"], CultureInfo.InvariantCulture); + Dictionary rawPosition = (Dictionary)commandResponse.Value!; + int x = Convert.ToInt32(rawPosition["x"], CultureInfo.InvariantCulture); + int y = Convert.ToInt32(rawPosition["y"], CultureInfo.InvariantCulture); - return new Point(x, y); - } - - set - { - Dictionary parameters = new Dictionary(); - parameters.Add("x", value.X); - parameters.Add("y", value.Y); - this.driver.Execute(DriverCommand.SetWindowRect, parameters); - } + return new Point(x, y); } - /// - /// Gets or sets the size of the outer browser window, including title bars and window borders. - /// - /// When setting this property, it should act as the JavaScript window.resizeTo() method. - public Size Size + set { - get - { - Response commandResponse = this.driver.Execute(DriverCommand.GetWindowRect, null); + Dictionary parameters = new Dictionary(); + parameters.Add("x", value.X); + parameters.Add("y", value.Y); + this.driver.Execute(DriverCommand.SetWindowRect, parameters); + } + } - Dictionary rawPosition = (Dictionary)commandResponse.Value!; - int height = Convert.ToInt32(rawPosition["height"], CultureInfo.InvariantCulture); - int width = Convert.ToInt32(rawPosition["width"], CultureInfo.InvariantCulture); + /// + /// Gets or sets the size of the outer browser window, including title bars and window borders. + /// + /// When setting this property, it should act as the JavaScript window.resizeTo() method. + public Size Size + { + get + { + Response commandResponse = this.driver.Execute(DriverCommand.GetWindowRect, null); - return new Size(width, height); - } + Dictionary rawPosition = (Dictionary)commandResponse.Value!; + int height = Convert.ToInt32(rawPosition["height"], CultureInfo.InvariantCulture); + int width = Convert.ToInt32(rawPosition["width"], CultureInfo.InvariantCulture); - set - { - Dictionary parameters = new Dictionary(); - parameters.Add("width", value.Width); - parameters.Add("height", value.Height); - this.driver.Execute(DriverCommand.SetWindowRect, parameters); - } + return new Size(width, height); } - /// - /// Maximizes the current window if it is not already maximized. - /// - public void Maximize() + set { - this.driver.Execute(DriverCommand.MaximizeWindow, null); + Dictionary parameters = new Dictionary(); + parameters.Add("width", value.Width); + parameters.Add("height", value.Height); + this.driver.Execute(DriverCommand.SetWindowRect, parameters); } + } - /// - /// Minimizes the current window if it is not already minimized. - /// - public void Minimize() - { - this.driver.Execute(DriverCommand.MinimizeWindow, null); - } + /// + /// Maximizes the current window if it is not already maximized. + /// + public void Maximize() + { + this.driver.Execute(DriverCommand.MaximizeWindow, null); + } - /// - /// Sets the current window to full screen if it is not already in that state. - /// - public void FullScreen() - { - this.driver.Execute(DriverCommand.FullScreenWindow, null); - } + /// + /// Minimizes the current window if it is not already minimized. + /// + public void Minimize() + { + this.driver.Execute(DriverCommand.MinimizeWindow, null); + } + + /// + /// Sets the current window to full screen if it is not already in that state. + /// + public void FullScreen() + { + this.driver.Execute(DriverCommand.FullScreenWindow, null); } } diff --git a/dotnet/src/webdriver/WindowType.cs b/dotnet/src/webdriver/WindowType.cs index de18f4ceeb1b1..b00d713dbd381 100644 --- a/dotnet/src/webdriver/WindowType.cs +++ b/dotnet/src/webdriver/WindowType.cs @@ -18,21 +18,20 @@ // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// Represents the type of a new browser window that may be created +/// +public enum WindowType { /// - /// Represents the type of a new browser window that may be created + /// Create a new browser window using a new top-level window. /// - public enum WindowType - { - /// - /// Create a new browser window using a new top-level window. - /// - Window, + Window, - /// - /// Create a new browser window using a new tab. - /// - Tab - } + /// + /// Create a new browser window using a new tab. + /// + Tab } diff --git a/dotnet/src/webdriver/XPathLookupException.cs b/dotnet/src/webdriver/XPathLookupException.cs index 81b3f8979576c..13ba56c7c35e1 100644 --- a/dotnet/src/webdriver/XPathLookupException.cs +++ b/dotnet/src/webdriver/XPathLookupException.cs @@ -19,43 +19,42 @@ using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +/// +/// The exception that is thrown when an error occurs during an XPath lookup. +/// +[Serializable] +public class XPathLookupException : WebDriverException { /// - /// The exception that is thrown when an error occurs during an XPath lookup. + /// Initializes a new instance of the class. /// - [Serializable] - public class XPathLookupException : WebDriverException + public XPathLookupException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public XPathLookupException() - : base() - { - } + } - /// - /// Initializes a new instance of the class with - /// a specified error message. - /// - /// The message that describes the error. - public XPathLookupException(string? message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The message that describes the error. + public XPathLookupException(string? message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with - /// a specified error message and a reference to the inner exception that is the - /// cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or if no inner exception is specified. - public XPathLookupException(string? message, Exception? innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with + /// a specified error message and a reference to the inner exception that is the + /// cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, + /// or if no inner exception is specified. + public XPathLookupException(string? message, Exception? innerException) + : base(message, innerException) + { } } diff --git a/dotnet/test/chrome/ChromeSpecificTests.cs b/dotnet/test/chrome/ChromeSpecificTests.cs index a0cc07f28ea04..521c5b42b43e5 100644 --- a/dotnet/test/chrome/ChromeSpecificTests.cs +++ b/dotnet/test/chrome/ChromeSpecificTests.cs @@ -21,16 +21,15 @@ using OpenQA.Selenium.Environment; using System.Threading.Tasks; -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +[TestFixture] +public class ChromeSpecificTests : DriverTestFixture { - [TestFixture] - public class ChromeSpecificTests : DriverTestFixture + [OneTimeTearDown] + public async Task RunAfterAnyTestsAsync() { - [OneTimeTearDown] - public async Task RunAfterAnyTestsAsync() - { - EnvironmentManager.Instance.CloseCurrentDriver(); - await EnvironmentManager.Instance.WebServer.StopAsync(); - } + EnvironmentManager.Instance.CloseCurrentDriver(); + await EnvironmentManager.Instance.WebServer.StopAsync(); } } diff --git a/dotnet/test/common/AlertsTest.cs b/dotnet/test/common/AlertsTest.cs index 8d093a783acf9..7bf5985d01a37 100644 --- a/dotnet/test/common/AlertsTest.cs +++ b/dotnet/test/common/AlertsTest.cs @@ -22,542 +22,541 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class AlertsTest : DriverTestFixture { - [TestFixture] - public class AlertsTest : DriverTestFixture + [Test] + public void ShouldBeAbleToOverrideTheWindowAlertMethod() { - [Test] - public void ShouldBeAbleToOverrideTheWindowAlertMethod() - { - driver.Url = CreateAlertPage("cheese"); + driver.Url = CreateAlertPage("cheese"); - ((IJavaScriptExecutor)driver).ExecuteScript( - "window.alert = function(msg) { document.getElementById('text').innerHTML = msg; }"); - driver.FindElement(By.Id("alert")).Click(); - } + ((IJavaScriptExecutor)driver).ExecuteScript( + "window.alert = function(msg) { document.getElementById('text').innerHTML = msg; }"); + driver.FindElement(By.Id("alert")).Click(); + } - [Test] - public void ShouldAllowUsersToAcceptAnAlertManually() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void ShouldAllowUsersToAcceptAnAlertManually() + { + driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("alert")).Click(); + driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - [Test] - public void ShouldThrowArgumentNullExceptionWhenKeysNull() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void ShouldThrowArgumentNullExceptionWhenKeysNull() + { + driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - try - { - Assert.That( - () => alert.SendKeys(null), - Throws.ArgumentNullException); - } - finally - { - alert.Accept(); - } + driver.FindElement(By.Id("alert")).Click(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + try + { + Assert.That( + () => alert.SendKeys(null), + Throws.ArgumentNullException); } - - [Test] - public void ShouldAllowUsersToAcceptAnAlertWithNoTextManually() + finally { - driver.Url = CreateAlertPage(""); - - driver.FindElement(By.Id("alert")).Click(); - - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); alert.Accept(); - - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); } + } - [Test] - public void ShouldAllowUsersToDismissAnAlertManually() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void ShouldAllowUsersToAcceptAnAlertWithNoTextManually() + { + driver.Url = CreateAlertPage(""); - driver.FindElement(By.Id("alert")).Click(); + driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Dismiss(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - [Test] - public void ShouldAllowAUserToAcceptAPrompt() - { - driver.Url = CreatePromptPage(null); + [Test] + public void ShouldAllowUsersToDismissAnAlertManually() + { + driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("prompt")).Click(); + driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Dismiss(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Prompt")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - [Test] - public void ShouldAllowAUserToDismissAPrompt() - { - driver.Url = CreatePromptPage(null); + [Test] + public void ShouldAllowAUserToAcceptAPrompt() + { + driver.Url = CreatePromptPage(null); - driver.FindElement(By.Id("prompt")).Click(); + driver.FindElement(By.Id("prompt")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Dismiss(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Prompt")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Prompt")); + } - [Test] - public void ShouldAllowAUserToSetTheValueOfAPrompt() - { - driver.Url = CreatePromptPage(null); + [Test] + public void ShouldAllowAUserToDismissAPrompt() + { + driver.Url = CreatePromptPage(null); - driver.FindElement(By.Id("prompt")).Click(); + driver.FindElement(By.Id("prompt")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.SendKeys("cheese"); - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Dismiss(); - string result = driver.FindElement(By.Id("text")).Text; - Assert.That(result, Is.EqualTo("cheese")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Prompt")); + } - [Test] - public void SettingTheValueOfAnAlertThrows() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void ShouldAllowAUserToSetTheValueOfAPrompt() + { + driver.Url = CreatePromptPage(null); - driver.FindElement(By.Id("alert")).Click(); + driver.FindElement(By.Id("prompt")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.SendKeys("cheese"); + alert.Accept(); - try - { - Assert.That( - () => alert.SendKeys("cheese"), - Throws.TypeOf()); - } - finally - { - alert.Accept(); - } - } + string result = driver.FindElement(By.Id("text")).Text; + Assert.That(result, Is.EqualTo("cheese")); + } - [Test] - public void ShouldAllowTheUserToGetTheTextOfAnAlert() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void SettingTheValueOfAnAlertThrows() + { + driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("alert")).Click(); + driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - string value = alert.Text; - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - Assert.That(value, Is.EqualTo("cheese")); + try + { + Assert.That( + () => alert.SendKeys("cheese"), + Throws.TypeOf()); } - - [Test] - public void ShouldAllowTheUserToGetTheTextOfAPrompt() + finally { - driver.Url = CreatePromptPage(null); + alert.Accept(); + } + } - driver.FindElement(By.Id("prompt")).Click(); + [Test] + public void ShouldAllowTheUserToGetTheTextOfAnAlert() + { + driver.Url = CreateAlertPage("cheese"); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - string value = alert.Text; - alert.Accept(); + driver.FindElement(By.Id("alert")).Click(); - Assert.That(value, Is.EqualTo("Enter something")); - } + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + string value = alert.Text; + alert.Accept(); - [Test] - public void AlertShouldNotAllowAdditionalCommandsIfDismissed() - { - driver.Url = CreateAlertPage("cheese"); + Assert.That(value, Is.EqualTo("cheese")); + } - driver.FindElement(By.Id("alert")).Click(); + [Test] + public void ShouldAllowTheUserToGetTheTextOfAPrompt() + { + driver.Url = CreatePromptPage(null); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Dismiss(); + driver.FindElement(By.Id("prompt")).Click(); - Assert.That( - () => alert.Text, - Throws.TypeOf()); - } + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + string value = alert.Text; + alert.Accept(); - [Test] - public void ShouldAllowUsersToAcceptAnAlertInAFrame() - { - string iframe = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody("click me")); - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithTitle("Testing Alerts") - .WithBody(String.Format("", iframe))); + Assert.That(value, Is.EqualTo("Enter something")); + } - driver.SwitchTo().Frame("iframeWithAlert"); + [Test] + public void AlertShouldNotAllowAdditionalCommandsIfDismissed() + { + driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("alertInFrame")).Click(); + driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Dismiss(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); - } + Assert.That( + () => alert.Text, + Throws.TypeOf()); + } - [Test] - public void ShouldAllowUsersToAcceptAnAlertInANestedFrame() - { - string iframe = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody("click me")); - string iframe2 = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody(string.Format("", iframe))); - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithTitle("Testing Alerts") - .WithBody(string.Format("", iframe2))); + [Test] + public void ShouldAllowUsersToAcceptAnAlertInAFrame() + { + string iframe = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody("click me")); + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithTitle("Testing Alerts") + .WithBody(String.Format("", iframe))); - driver.SwitchTo().Frame("iframeWithIframe").SwitchTo().Frame("iframeWithAlert"); + driver.SwitchTo().Frame("iframeWithAlert"); - driver.FindElement(By.Id("alertInFrame")).Click(); + driver.FindElement(By.Id("alertInFrame")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Accept(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - // If we can perform any action, we're good to go - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); - } + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - [Test] - public void SwitchingToMissingAlertThrows() - { - driver.Url = CreateAlertPage("cheese"); + [Test] + public void ShouldAllowUsersToAcceptAnAlertInANestedFrame() + { + string iframe = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody("click me")); + string iframe2 = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody(string.Format("", iframe))); + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithTitle("Testing Alerts") + .WithBody(string.Format("", iframe2))); - Assert.That( - () => AlertToBePresent(), - Throws.TypeOf()); - } + driver.SwitchTo().Frame("iframeWithIframe").SwitchTo().Frame("iframeWithAlert"); - [Test] - [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] - public void SwitchingToMissingAlertInAClosedWindowThrows() - { - string blank = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage()); - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody(String.Format( - "open new window", blank))); + driver.FindElement(By.Id("alertInFrame")).Click(); - string mainWindow = driver.CurrentWindowHandle; - try - { - driver.FindElement(By.Id("open-new-window")).Click(); - WaitFor(WindowHandleCountToBe(2), "Window count was not 2"); - WaitFor(WindowWithName("newwindow"), "Could not find window with name 'newwindow'"); - driver.Close(); - WaitFor(WindowHandleCountToBe(1), "Window count was not 1"); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - Assert.That( - () => AlertToBePresent().Accept(), - Throws.TypeOf()); + // If we can perform any action, we're good to go + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - } - finally - { - driver.SwitchTo().Window(mainWindow); - WaitFor(ElementTextToEqual(driver.FindElement(By.Id("open-new-window")), "open new window"), "Could not find element with text 'open new window'"); - } - } + [Test] + public void SwitchingToMissingAlertThrows() + { + driver.Url = CreateAlertPage("cheese"); + + Assert.That( + () => AlertToBePresent(), + Throws.TypeOf()); + } + + [Test] + [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] + public void SwitchingToMissingAlertInAClosedWindowThrows() + { + string blank = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage()); + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody(String.Format( + "open new window", blank))); - [Test] - public void PromptShouldUseDefaultValueIfNoKeysSent() + string mainWindow = driver.CurrentWindowHandle; + try { - driver.Url = CreatePromptPage("This is a default value"); - driver.FindElement(By.Id("prompt")).Click(); + driver.FindElement(By.Id("open-new-window")).Click(); + WaitFor(WindowHandleCountToBe(2), "Window count was not 2"); + WaitFor(WindowWithName("newwindow"), "Could not find window with name 'newwindow'"); + driver.Close(); + WaitFor(WindowHandleCountToBe(1), "Window count was not 1"); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Accept(); + Assert.That( + () => AlertToBePresent().Accept(), + Throws.TypeOf()); - IWebElement element = driver.FindElement(By.Id("text")); - WaitFor(ElementTextToEqual(element, "This is a default value"), "Element text was not 'This is a default value'"); - Assert.That(element.Text, Is.EqualTo("This is a default value")); } - - [Test] - public void PromptShouldHaveNullValueIfDismissed() + finally { - driver.Url = CreatePromptPage("This is a default value"); - driver.FindElement(By.Id("prompt")).Click(); - - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - alert.Dismiss(); - IWebElement element = driver.FindElement(By.Id("text")); - WaitFor(ElementTextToEqual(element, "null"), "Element text was not 'null'"); - Assert.That(element.Text, Is.EqualTo("null")); + driver.SwitchTo().Window(mainWindow); + WaitFor(ElementTextToEqual(driver.FindElement(By.Id("open-new-window")), "open new window"), "Could not find element with text 'open new window'"); } + } - [Test] - [IgnoreBrowser(Browser.Remote)] - public void HandlesTwoAlertsFromOneInteraction() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithScripts( - """ - function setInnerText(id, value) { - document.getElementById(id).innerHTML = '

' + value + '

'; - } - - function displayTwoPrompts() { - setInnerText('text1', prompt('First')); - setInnerText('text2', prompt('Second')); - } - """) - .WithBody( - """ - click me -
-
- """)); - - driver.FindElement(By.Id("double-prompt")).Click(); - - IAlert alert1 = WaitFor(AlertToBePresent, "No alert found"); - alert1.SendKeys("brie"); - alert1.Accept(); - - IAlert alert2 = WaitFor(AlertToBePresent, "No alert found"); - alert2.SendKeys("cheddar"); - alert2.Accept(); - - IWebElement element1 = driver.FindElement(By.Id("text1")); - WaitFor(ElementTextToEqual(element1, "brie"), "Element text was not 'brie'"); - Assert.That(element1.Text, Is.EqualTo("brie")); - IWebElement element2 = driver.FindElement(By.Id("text2")); - WaitFor(ElementTextToEqual(element2, "cheddar"), "Element text was not 'cheddar'"); - Assert.That(element2.Text, Is.EqualTo("cheddar")); - } + [Test] + public void PromptShouldUseDefaultValueIfNoKeysSent() + { + driver.Url = CreatePromptPage("This is a default value"); + driver.FindElement(By.Id("prompt")).Click(); - [Test] - public void ShouldHandleAlertOnPageLoad() - { - string pageWithOnLoad = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithOnLoad("""javascript:alert("onload")""") - .WithBody("

Page with onload event handler

")); - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody(string.Format("open new page", pageWithOnLoad))); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Accept(); - driver.FindElement(By.Id("open-page-with-onload-alert")).Click(); + IWebElement element = driver.FindElement(By.Id("text")); + WaitFor(ElementTextToEqual(element, "This is a default value"), "Element text was not 'This is a default value'"); + Assert.That(element.Text, Is.EqualTo("This is a default value")); + } - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - string value = alert.Text; - alert.Accept(); + [Test] + public void PromptShouldHaveNullValueIfDismissed() + { + driver.Url = CreatePromptPage("This is a default value"); + driver.FindElement(By.Id("prompt")).Click(); + + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + alert.Dismiss(); + IWebElement element = driver.FindElement(By.Id("text")); + WaitFor(ElementTextToEqual(element, "null"), "Element text was not 'null'"); + Assert.That(element.Text, Is.EqualTo("null")); + } - Assert.That(value, Is.EqualTo("onload")); - IWebElement element = driver.FindElement(By.TagName("p")); - WaitFor(ElementTextToEqual(element, "Page with onload event handler"), "Element text was not 'Page with onload event handler'"); - } + [Test] + [IgnoreBrowser(Browser.Remote)] + public void HandlesTwoAlertsFromOneInteraction() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithScripts( + """ + function setInnerText(id, value) { + document.getElementById(id).innerHTML = '

' + value + '

'; + } - [Test] + function displayTwoPrompts() { + setInnerText('text1', prompt('First')); + setInnerText('text2', prompt('Second')); + } + """) + .WithBody( + """ + click me +
+
+ """)); + + driver.FindElement(By.Id("double-prompt")).Click(); + + IAlert alert1 = WaitFor(AlertToBePresent, "No alert found"); + alert1.SendKeys("brie"); + alert1.Accept(); + + IAlert alert2 = WaitFor(AlertToBePresent, "No alert found"); + alert2.SendKeys("cheddar"); + alert2.Accept(); + + IWebElement element1 = driver.FindElement(By.Id("text1")); + WaitFor(ElementTextToEqual(element1, "brie"), "Element text was not 'brie'"); + Assert.That(element1.Text, Is.EqualTo("brie")); + IWebElement element2 = driver.FindElement(By.Id("text2")); + WaitFor(ElementTextToEqual(element2, "cheddar"), "Element text was not 'cheddar'"); + Assert.That(element2.Text, Is.EqualTo("cheddar")); + } - public void ShouldHandleAlertOnPageLoadUsingGet() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithOnLoad("javascript:alert(\"onload\")") - .WithBody("

Page with onload event handler

")); + [Test] + public void ShouldHandleAlertOnPageLoad() + { + string pageWithOnLoad = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithOnLoad("""javascript:alert("onload")""") + .WithBody("

Page with onload event handler

")); + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody(string.Format("open new page", pageWithOnLoad))); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - string value = alert.Text; - alert.Accept(); + driver.FindElement(By.Id("open-page-with-onload-alert")).Click(); - Assert.That(value, Is.EqualTo("onload")); - WaitFor(ElementTextToEqual(driver.FindElement(By.TagName("p")), "Page with onload event handler"), "Could not find element with text 'Page with onload event handler'"); - } + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + string value = alert.Text; + alert.Accept(); - [Test] - [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] - [IgnoreBrowser(Browser.Chrome, "Test with onLoad alert hangs Chrome.")] - [IgnoreBrowser(Browser.Edge, "Test with onLoad alert hangs Edge.")] - [IgnoreBrowser(Browser.Safari, "Safari driver does not allow commands in any window when an alert is active")] - public void ShouldNotHandleAlertInAnotherWindow() - { - string pageWithOnLoad = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithOnLoad("javascript:alert(\"onload\")") - .WithBody("

Page with onload event handler

")); - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody(string.Format( - "open new window", pageWithOnLoad))); - - string mainWindow = driver.CurrentWindowHandle; - string onloadWindow = null; - try - { - driver.FindElement(By.Id("open-new-window")).Click(); - List allWindows = new List(driver.WindowHandles); - allWindows.Remove(mainWindow); - Assert.That(allWindows, Has.One.Items); - onloadWindow = allWindows[0]; - - Assert.That(() => - { - IWebElement el = driver.FindElement(By.Id("open-new-window")); - WaitFor(AlertToBePresent, TimeSpan.FromSeconds(5), "No alert found"); - }, - Throws.TypeOf()); + Assert.That(value, Is.EqualTo("onload")); + IWebElement element = driver.FindElement(By.TagName("p")); + WaitFor(ElementTextToEqual(element, "Page with onload event handler"), "Element text was not 'Page with onload event handler'"); + } - } - finally - { - driver.SwitchTo().Window(onloadWindow); - WaitFor(AlertToBePresent, "No alert found").Dismiss(); - driver.Close(); - driver.SwitchTo().Window(mainWindow); - WaitFor(ElementTextToEqual(driver.FindElement(By.Id("open-new-window")), "open new window"), "Could not find element with text 'open new window'"); - } - } + [Test] - [Test] - [IgnoreBrowser(Browser.Firefox, "Driver chooses not to return text from unhandled alert")] - public void IncludesAlertTextInUnhandledAlertException() - { - driver.Url = CreateAlertPage("cheese"); + public void ShouldHandleAlertOnPageLoadUsingGet() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithOnLoad("javascript:alert(\"onload\")") + .WithBody("

Page with onload event handler

")); - driver.FindElement(By.Id("alert")).Click(); - WaitFor(AlertToBePresent, "No alert found"); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + string value = alert.Text; + alert.Accept(); - Assert.That( - () => driver.Title, - Throws.TypeOf().With.Property(nameof(UnhandledAlertException.AlertText)).EqualTo("cheese")); - } + Assert.That(value, Is.EqualTo("onload")); + WaitFor(ElementTextToEqual(driver.FindElement(By.TagName("p")), "Page with onload event handler"), "Could not find element with text 'Page with onload event handler'"); + } - [Test] - [NeedsFreshDriver(IsCreatedAfterTest = true)] - public void CanQuitWhenAnAlertIsPresent() + [Test] + [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] + [IgnoreBrowser(Browser.Chrome, "Test with onLoad alert hangs Chrome.")] + [IgnoreBrowser(Browser.Edge, "Test with onLoad alert hangs Edge.")] + [IgnoreBrowser(Browser.Safari, "Safari driver does not allow commands in any window when an alert is active")] + public void ShouldNotHandleAlertInAnotherWindow() + { + string pageWithOnLoad = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithOnLoad("javascript:alert(\"onload\")") + .WithBody("

Page with onload event handler

")); + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody(string.Format( + "open new window", pageWithOnLoad))); + + string mainWindow = driver.CurrentWindowHandle; + string onloadWindow = null; + try + { + driver.FindElement(By.Id("open-new-window")).Click(); + List allWindows = new List(driver.WindowHandles); + allWindows.Remove(mainWindow); + Assert.That(allWindows, Has.One.Items); + onloadWindow = allWindows[0]; + + Assert.That(() => + { + IWebElement el = driver.FindElement(By.Id("open-new-window")); + WaitFor(AlertToBePresent, TimeSpan.FromSeconds(5), "No alert found"); + }, + Throws.TypeOf()); + + } + finally { - driver.Url = CreateAlertPage("cheese"); - driver.FindElement(By.Id("alert")).Click(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - EnvironmentManager.Instance.CloseCurrentDriver(); + driver.SwitchTo().Window(onloadWindow); + WaitFor(AlertToBePresent, "No alert found").Dismiss(); + driver.Close(); + driver.SwitchTo().Window(mainWindow); + WaitFor(ElementTextToEqual(driver.FindElement(By.Id("open-new-window")), "open new window"), "Could not find element with text 'open new window'"); } + } - [Test] - public void ShouldHandleAlertOnFormSubmit() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithTitle("Testing Alerts"). - WithBody("
", - "", - "
")); - - IWebElement element = driver.FindElement(By.Id("theForm")); - element.Submit(); - IAlert alert = WaitFor(AlertToBePresent, "No alert found"); - string text = alert.Text; - alert.Accept(); + [Test] + [IgnoreBrowser(Browser.Firefox, "Driver chooses not to return text from unhandled alert")] + public void IncludesAlertTextInUnhandledAlertException() + { + driver.Url = CreateAlertPage("cheese"); - Assert.That(text, Is.EqualTo("Tasty cheese")); - Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); - } + driver.FindElement(By.Id("alert")).Click(); + WaitFor(AlertToBePresent, "No alert found"); - private IAlert AlertToBePresent() - { - return driver.SwitchTo().Alert(); - } + Assert.That( + () => driver.Title, + Throws.TypeOf().With.Property(nameof(UnhandledAlertException.AlertText)).EqualTo("cheese")); + } - private string CreateAlertPage(string alertText) - { - return EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithTitle("Testing Alerts") - .WithBody("click me")); - } + [Test] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + public void CanQuitWhenAnAlertIsPresent() + { + driver.Url = CreateAlertPage("cheese"); + driver.FindElement(By.Id("alert")).Click(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + EnvironmentManager.Instance.CloseCurrentDriver(); + } - private string CreatePromptPage(string defaultText) - { - return EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithTitle("Testing Prompt") - .WithScripts( - "function setInnerText(id, value) {", - " document.getElementById(id).innerHTML = '

' + value + '

';", - "}", - defaultText == null - ? "function displayPrompt() { setInnerText('text', prompt('Enter something')); }" - : "function displayPrompt() { setInnerText('text', prompt('Enter something', '" + defaultText + "')); }") - - .WithBody( - "click me", - "
acceptor
")); - } + [Test] + public void ShouldHandleAlertOnFormSubmit() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithTitle("Testing Alerts"). + WithBody("
", + "", + "
")); + + IWebElement element = driver.FindElement(By.Id("theForm")); + element.Submit(); + IAlert alert = WaitFor(AlertToBePresent, "No alert found"); + string text = alert.Text; + alert.Accept(); + + Assert.That(text, Is.EqualTo("Tasty cheese")); + Assert.That(driver.Title, Is.EqualTo("Testing Alerts")); + } - private void SetSimpleOnBeforeUnload(string returnText) - { - ((IJavaScriptExecutor)driver).ExecuteScript( - "var returnText = arguments[0]; window.onbeforeunload = function() { return returnText; }", - returnText); - } + private IAlert AlertToBePresent() + { + return driver.SwitchTo().Alert(); + } - private Func ElementToBePresent(By locator) - { - return () => - { - try - { - return driver.FindElement(By.Id("open-page-with-onunload-alert")); - } - catch (NoSuchElementException) - { - return null; - } - }; - } + private string CreateAlertPage(string alertText) + { + return EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithTitle("Testing Alerts") + .WithBody("click me")); + } + + private string CreatePromptPage(string defaultText) + { + return EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithTitle("Testing Prompt") + .WithScripts( + "function setInnerText(id, value) {", + " document.getElementById(id).innerHTML = '

' + value + '

';", + "}", + defaultText == null + ? "function displayPrompt() { setInnerText('text', prompt('Enter something')); }" + : "function displayPrompt() { setInnerText('text', prompt('Enter something', '" + defaultText + "')); }") + + .WithBody( + "click me", + "
acceptor
")); + } + + private void SetSimpleOnBeforeUnload(string returnText) + { + ((IJavaScriptExecutor)driver).ExecuteScript( + "var returnText = arguments[0]; window.onbeforeunload = function() { return returnText; }", + returnText); + } - private Func ElementTextToEqual(IWebElement element, string text) + private Func ElementToBePresent(By locator) + { + return () => { - return () => + try { - return element.Text == text; - }; - } + return driver.FindElement(By.Id("open-page-with-onunload-alert")); + } + catch (NoSuchElementException) + { + return null; + } + }; + } - private Func WindowWithName(string name) + private Func ElementTextToEqual(IWebElement element, string text) + { + return () => { - return () => - { - try - { - driver.SwitchTo().Window(name); - return true; - } - catch (NoSuchWindowException) - { - return false; - } - }; - } + return element.Text == text; + }; + } - private Func WindowHandleCountToBe(int count) + private Func WindowWithName(string name) + { + return () => { - return () => + try { - return driver.WindowHandles.Count == count; - }; - } + driver.SwitchTo().Window(name); + return true; + } + catch (NoSuchWindowException) + { + return false; + } + }; + } + private Func WindowHandleCountToBe(int count) + { + return () => + { + return driver.WindowHandles.Count == count; + }; } + } diff --git a/dotnet/test/common/AssemblyFixture.cs b/dotnet/test/common/AssemblyFixture.cs index aacc1b2a7d573..d8605f5f4203e 100644 --- a/dotnet/test/common/AssemblyFixture.cs +++ b/dotnet/test/common/AssemblyFixture.cs @@ -21,37 +21,36 @@ using OpenQA.Selenium.Environment; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[SetUpFixture] +// Outside a namespace to affect the entire assembly +public class AssemblyFixture { - [SetUpFixture] - // Outside a namespace to affect the entire assembly - public class AssemblyFixture + public AssemblyFixture() { - public AssemblyFixture() - { - } + } - [OneTimeSetUp] - public async Task RunBeforeAnyTestAsync() - { - Internal.Logging.Log.SetLevel(Internal.Logging.LogEventLevel.Trace); + [OneTimeSetUp] + public async Task RunBeforeAnyTestAsync() + { + Internal.Logging.Log.SetLevel(Internal.Logging.LogEventLevel.Trace); - await EnvironmentManager.Instance.WebServer.StartAsync(); - if (EnvironmentManager.Instance.Browser == Browser.Remote) - { - await EnvironmentManager.Instance.RemoteServer.StartAsync(); - } + await EnvironmentManager.Instance.WebServer.StartAsync(); + if (EnvironmentManager.Instance.Browser == Browser.Remote) + { + await EnvironmentManager.Instance.RemoteServer.StartAsync(); } + } - [OneTimeTearDown] - public async Task RunAfterAnyTestsAsync() + [OneTimeTearDown] + public async Task RunAfterAnyTestsAsync() + { + EnvironmentManager.Instance.CloseCurrentDriver(); + await EnvironmentManager.Instance.WebServer.StopAsync(); + if (EnvironmentManager.Instance.Browser == Browser.Remote) { - EnvironmentManager.Instance.CloseCurrentDriver(); - await EnvironmentManager.Instance.WebServer.StopAsync(); - if (EnvironmentManager.Instance.Browser == Browser.Remote) - { - await EnvironmentManager.Instance.RemoteServer.StopAsync(); - } + await EnvironmentManager.Instance.RemoteServer.StopAsync(); } } } diff --git a/dotnet/test/common/Browser.cs b/dotnet/test/common/Browser.cs index 9df0012df05ff..c0d40ca3ff5ef 100644 --- a/dotnet/test/common/Browser.cs +++ b/dotnet/test/common/Browser.cs @@ -17,16 +17,15 @@ // under the License. // -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +public enum Browser { - public enum Browser - { - All, - IE, - Edge, - Firefox, - Safari, - Chrome, - Remote, - } + All, + IE, + Edge, + Firefox, + Safari, + Chrome, + Remote, } diff --git a/dotnet/test/common/ChildrenFindingTest.cs b/dotnet/test/common/ChildrenFindingTest.cs index 6588c7c88a43a..85bd4a9d48b96 100644 --- a/dotnet/test/common/ChildrenFindingTest.cs +++ b/dotnet/test/common/ChildrenFindingTest.cs @@ -20,380 +20,379 @@ using NUnit.Framework; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ChildrenFindingTest : DriverTestFixture { - [TestFixture] - public class ChildrenFindingTest : DriverTestFixture - { - [Test] - public void FindElementByXPath() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - IWebElement child = element.FindElement(By.XPath("select")); - Assert.That(child.GetAttribute("id"), Is.EqualTo("2")); - } - - [Test] - public void FindingElementsOnElementByXPathShouldFindTopLevelElements() - { - driver.Url = simpleTestPage; - IWebElement parent = driver.FindElement(By.Id("multiline")); - ReadOnlyCollection allParaElements = driver.FindElements(By.XPath("//p")); - ReadOnlyCollection children = parent.FindElements(By.XPath("//p")); - Assert.That(children, Has.Exactly(allParaElements.Count).Items); - } - - [Test] - public void FindingDotSlashElementsOnElementByXPathShouldFindNotTopLevelElements() - { - driver.Url = simpleTestPage; - IWebElement parent = driver.FindElement(By.Id("multiline")); - - ReadOnlyCollection children = parent.FindElements(By.XPath("./p")); - Assert.That(children, Has.One.Items); - Assert.That(children[0].Text, Is.EqualTo("A div containing")); - } - - [Test] - public void FindElementByXPathWhenNoMatch() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - - Assert.That( - () => element.FindElement(By.XPath("select/x")), - Throws.InstanceOf()); - } - - [Test] - public void FindElementsByXPath() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - - ReadOnlyCollection children = element.FindElements(By.XPath("select/option")); - Assert.That(children, Has.Exactly(8).Items); - Assert.That(children[0].Text, Is.EqualTo("One")); - Assert.That(children[1].Text, Is.EqualTo("Two")); - } - - [Test] - public void FindElementsByXPathWhenNoMatch() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - ReadOnlyCollection children = element.FindElements(By.XPath("select/x")); - Assert.That(children, Is.Empty); - } - - [Test] - public void FindElementByName() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - IWebElement child = element.FindElement(By.Name("selectomatic")); - Assert.That(child.GetAttribute("id"), Is.EqualTo("2")); - } - - [Test] - public void FindElementsByName() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - - ReadOnlyCollection children = element.FindElements(By.Name("selectomatic")); - Assert.That(children, Has.Exactly(2).Items); - } - - [Test] - public void FindElementById() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - - IWebElement child = element.FindElement(By.Id("2")); - Assert.That(child.GetAttribute("name"), Is.EqualTo("selectomatic")); - } - - - [Test] - public void FindElementByIdWhenMultipleMatchesExist() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("test_id_div")); - - IWebElement child = element.FindElement(By.Id("test_id")); - Assert.That(child.Text, Is.EqualTo("inside")); - } - - [Test] - public void FindElementByIdWhenIdContainsNonAlphanumericCharacters() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("test_special_chars")); - - IWebElement childWithSpaces = element.FindElement(By.Id("white space")); - Assert.That(childWithSpaces.Text, Does.Contain("space")); - IWebElement childWithCssChars = element.FindElement(By.Id("css#.chars")); - Assert.That(childWithCssChars.Text, Is.EqualTo("css escapes")); - } - - [Test] - public void FindElementByIdWhenNoMatchInContext() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("test_id_div")); - - Assert.That( - () => element.FindElement(By.Id("test_id_out")), - Throws.InstanceOf()); - } - - [Test] - public void FindElementsById() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("form2")); - ReadOnlyCollection children = element.FindElements(By.Id("2")); - Assert.That(children, Has.Exactly(2).Items); - } - - [Test] - public void FindElementsByIdWithNonAlphanumericCharacters() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("test_special_chars")); - ReadOnlyCollection children = element.FindElements(By.Id("white space")); - Assert.That(children, Has.One.Items); - ReadOnlyCollection children2 = element.FindElements(By.Id("css#.chars")); - Assert.That(children2, Has.One.Items); - } - - [Test] - public void FindElementByLinkText() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("div1")); - - IWebElement child = element.FindElement(By.LinkText("hello world")); - Assert.That(child.GetAttribute("name"), Is.EqualTo("link1")); - } - - - [Test] - public void FindElementsByLinkText() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("div1")); - ReadOnlyCollection elements = element.FindElements(By.LinkText("hello world")); - - Assert.That(elements, Has.Exactly(2).Items); - Assert.That(elements[0].GetAttribute("name"), Is.EqualTo("link1")); - Assert.That(elements[1].GetAttribute("name"), Is.EqualTo("link2")); - } - - [Test] - public void ShouldFindChildElementsById() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Id("test_id_div")); - - IWebElement element = parent.FindElement(By.Id("test_id")); - Assert.That(element.Text, Is.EqualTo("inside")); - } - - [Test] - public void ShouldNotReturnRootElementWhenFindingChildrenById() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Id("test_id")); - - Assert.That(parent.FindElements(By.Id("test_id")), Is.Empty); - Assert.That( - () => parent.FindElement(By.Id("test_id")), - Throws.InstanceOf()); - } - - [Test] - public void ShouldFindChildElementsByClassName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - - IWebElement element = parent.FindElement(By.ClassName("one")); - - Assert.That(element.Text, Is.EqualTo("Find me")); - } - - [Test] - public void ShouldFindChildrenByClassName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - - ReadOnlyCollection elements = parent.FindElements(By.ClassName("one")); - - Assert.That(elements, Has.Exactly(2).Items); - } - - - [Test] - public void ShouldFindChildElementsByTagName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - - IWebElement element = parent.FindElement(By.TagName("a")); - - Assert.That(element.GetAttribute("name"), Is.EqualTo("link1")); - } - - - [Test] - public void ShouldFindChildrenByTagName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - - ReadOnlyCollection elements = parent.FindElements(By.TagName("a")); - - Assert.That(elements, Has.Exactly(2).Items); - } - - [Test] - public void ShouldBeAbleToFindAnElementByCssSelector() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("form2")); - - IWebElement element = parent.FindElement(By.CssSelector("*[name=\"selectomatic\"]")); - - Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); - } - - [Test] - public void ShouldBeAbleToFindAnElementByCss3Selector() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("form2")); - - IWebElement element = parent.FindElement(By.CssSelector("*[name^=\"selecto\"]")); - - Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); - } - - [Test] - public void ShouldBeAbleToFindElementsByCssSelector() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("form2")); - - ReadOnlyCollection elements = parent.FindElements(By.CssSelector("*[name=\"selectomatic\"]")); - - Assert.That(elements, Has.Exactly(2).Items); - } - - [Test] - public void ShouldBeAbleToFindChildrenOfANode() - { - driver.Url = selectableItemsPage; - ReadOnlyCollection elements = driver.FindElements(By.XPath("/html/head")); - IWebElement head = elements[0]; - - ReadOnlyCollection importedScripts = head.FindElements(By.TagName("script")); - Assert.That(importedScripts, Has.Exactly(3).Items); - } - - [Test] - public void ReturnAnEmptyListWhenThereAreNoChildrenOfANode() - { - driver.Url = xhtmlTestPage; - IWebElement table = driver.FindElement(By.Id("table")); - - ReadOnlyCollection rows = table.FindElements(By.TagName("tr")); - Assert.That(rows, Is.Empty); - } - - [Test] - public void ShouldFindGrandChildren() - { - driver.Url = formsPage; - IWebElement form = driver.FindElement(By.Id("nested_form")); - form.FindElement(By.Name("x")); - } - - [Test] - public void ShouldNotFindElementOutSideTree() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Name("login")); - Assert.That( - () => element.FindElement(By.Name("x")), - Throws.InstanceOf()); - } - - [Test] - public void FindingByTagNameShouldNotIncludeParentElementIfSameTagType() - { - driver.Url = xhtmlTestPage; - IWebElement parent = driver.FindElement(By.Id("my_span")); - - Assert.That(parent.FindElements(By.TagName("div")), Has.Exactly(2).Items); - Assert.That(parent.FindElements(By.TagName("span")), Has.Exactly(2).Items); - } - - [Test] - public void FindingByCssShouldNotIncludeParentElementIfSameTagType() - { - driver.Url = xhtmlTestPage; - IWebElement parent = driver.FindElement(By.CssSelector("div#parent")); - IWebElement child = parent.FindElement(By.CssSelector("div")); - - Assert.That(child.GetAttribute("id"), Is.EqualTo("child")); - } - - [Test] - public void FindMultipleElements() - { - driver.Url = simpleTestPage; - IWebElement elem = driver.FindElement(By.Id("links")); - - ReadOnlyCollection elements = elem.FindElements(By.PartialLinkText("link")); - Assert.That(elements, Is.Not.Null); - Assert.That(elements, Has.Exactly(6).Items); - } - - [Test] - [IgnoreBrowser(Browser.Safari, "Safari does not trim")] - public void LinkWithLeadingSpaces() - { - driver.Url = simpleTestPage; - IWebElement elem = driver.FindElement(By.Id("links")); - - IWebElement res = elem.FindElement(By.PartialLinkText("link with leading space")); - Assert.That(res.Text, Is.EqualTo("link with leading space")); - } - - [Test] - [IgnoreBrowser(Browser.Safari, "Safari does not trim")] - public void LinkWithTrailingSpace() - { - driver.Url = simpleTestPage; - IWebElement elem = driver.FindElement(By.Id("links")); - - IWebElement res = elem.FindElement(By.PartialLinkText("link with trailing space")); - Assert.That(res.Text, Is.EqualTo("link with trailing space")); - } - - [Test] - public void ElementCanGetLinkByLinkTestIgnoringTrailingWhitespace() - { - driver.Url = simpleTestPage; - IWebElement elem = driver.FindElement(By.Id("links")); - - IWebElement link = elem.FindElement(By.LinkText("link with trailing space")); - Assert.That(link.GetAttribute("id"), Is.EqualTo("linkWithTrailingSpace")); - } + [Test] + public void FindElementByXPath() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + IWebElement child = element.FindElement(By.XPath("select")); + Assert.That(child.GetAttribute("id"), Is.EqualTo("2")); + } + + [Test] + public void FindingElementsOnElementByXPathShouldFindTopLevelElements() + { + driver.Url = simpleTestPage; + IWebElement parent = driver.FindElement(By.Id("multiline")); + ReadOnlyCollection allParaElements = driver.FindElements(By.XPath("//p")); + ReadOnlyCollection children = parent.FindElements(By.XPath("//p")); + Assert.That(children, Has.Exactly(allParaElements.Count).Items); + } + + [Test] + public void FindingDotSlashElementsOnElementByXPathShouldFindNotTopLevelElements() + { + driver.Url = simpleTestPage; + IWebElement parent = driver.FindElement(By.Id("multiline")); + + ReadOnlyCollection children = parent.FindElements(By.XPath("./p")); + Assert.That(children, Has.One.Items); + Assert.That(children[0].Text, Is.EqualTo("A div containing")); + } + + [Test] + public void FindElementByXPathWhenNoMatch() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + + Assert.That( + () => element.FindElement(By.XPath("select/x")), + Throws.InstanceOf()); + } + + [Test] + public void FindElementsByXPath() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + + ReadOnlyCollection children = element.FindElements(By.XPath("select/option")); + Assert.That(children, Has.Exactly(8).Items); + Assert.That(children[0].Text, Is.EqualTo("One")); + Assert.That(children[1].Text, Is.EqualTo("Two")); + } + + [Test] + public void FindElementsByXPathWhenNoMatch() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + ReadOnlyCollection children = element.FindElements(By.XPath("select/x")); + Assert.That(children, Is.Empty); + } + + [Test] + public void FindElementByName() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + IWebElement child = element.FindElement(By.Name("selectomatic")); + Assert.That(child.GetAttribute("id"), Is.EqualTo("2")); + } + + [Test] + public void FindElementsByName() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + + ReadOnlyCollection children = element.FindElements(By.Name("selectomatic")); + Assert.That(children, Has.Exactly(2).Items); + } + + [Test] + public void FindElementById() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + + IWebElement child = element.FindElement(By.Id("2")); + Assert.That(child.GetAttribute("name"), Is.EqualTo("selectomatic")); + } + + + [Test] + public void FindElementByIdWhenMultipleMatchesExist() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("test_id_div")); + + IWebElement child = element.FindElement(By.Id("test_id")); + Assert.That(child.Text, Is.EqualTo("inside")); + } + + [Test] + public void FindElementByIdWhenIdContainsNonAlphanumericCharacters() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("test_special_chars")); + + IWebElement childWithSpaces = element.FindElement(By.Id("white space")); + Assert.That(childWithSpaces.Text, Does.Contain("space")); + IWebElement childWithCssChars = element.FindElement(By.Id("css#.chars")); + Assert.That(childWithCssChars.Text, Is.EqualTo("css escapes")); + } + + [Test] + public void FindElementByIdWhenNoMatchInContext() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("test_id_div")); + + Assert.That( + () => element.FindElement(By.Id("test_id_out")), + Throws.InstanceOf()); + } + + [Test] + public void FindElementsById() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("form2")); + ReadOnlyCollection children = element.FindElements(By.Id("2")); + Assert.That(children, Has.Exactly(2).Items); + } + + [Test] + public void FindElementsByIdWithNonAlphanumericCharacters() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("test_special_chars")); + ReadOnlyCollection children = element.FindElements(By.Id("white space")); + Assert.That(children, Has.One.Items); + ReadOnlyCollection children2 = element.FindElements(By.Id("css#.chars")); + Assert.That(children2, Has.One.Items); + } + + [Test] + public void FindElementByLinkText() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("div1")); + + IWebElement child = element.FindElement(By.LinkText("hello world")); + Assert.That(child.GetAttribute("name"), Is.EqualTo("link1")); + } + + + [Test] + public void FindElementsByLinkText() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("div1")); + ReadOnlyCollection elements = element.FindElements(By.LinkText("hello world")); + + Assert.That(elements, Has.Exactly(2).Items); + Assert.That(elements[0].GetAttribute("name"), Is.EqualTo("link1")); + Assert.That(elements[1].GetAttribute("name"), Is.EqualTo("link2")); + } + + [Test] + public void ShouldFindChildElementsById() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Id("test_id_div")); + + IWebElement element = parent.FindElement(By.Id("test_id")); + Assert.That(element.Text, Is.EqualTo("inside")); + } + + [Test] + public void ShouldNotReturnRootElementWhenFindingChildrenById() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Id("test_id")); + + Assert.That(parent.FindElements(By.Id("test_id")), Is.Empty); + Assert.That( + () => parent.FindElement(By.Id("test_id")), + Throws.InstanceOf()); + } + + [Test] + public void ShouldFindChildElementsByClassName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + + IWebElement element = parent.FindElement(By.ClassName("one")); + + Assert.That(element.Text, Is.EqualTo("Find me")); + } + + [Test] + public void ShouldFindChildrenByClassName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + + ReadOnlyCollection elements = parent.FindElements(By.ClassName("one")); + + Assert.That(elements, Has.Exactly(2).Items); + } + + + [Test] + public void ShouldFindChildElementsByTagName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + + IWebElement element = parent.FindElement(By.TagName("a")); + + Assert.That(element.GetAttribute("name"), Is.EqualTo("link1")); + } + + + [Test] + public void ShouldFindChildrenByTagName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + + ReadOnlyCollection elements = parent.FindElements(By.TagName("a")); + + Assert.That(elements, Has.Exactly(2).Items); + } + + [Test] + public void ShouldBeAbleToFindAnElementByCssSelector() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("form2")); + + IWebElement element = parent.FindElement(By.CssSelector("*[name=\"selectomatic\"]")); + + Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); + } + + [Test] + public void ShouldBeAbleToFindAnElementByCss3Selector() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("form2")); + + IWebElement element = parent.FindElement(By.CssSelector("*[name^=\"selecto\"]")); + + Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); + } + + [Test] + public void ShouldBeAbleToFindElementsByCssSelector() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("form2")); + + ReadOnlyCollection elements = parent.FindElements(By.CssSelector("*[name=\"selectomatic\"]")); + + Assert.That(elements, Has.Exactly(2).Items); + } + + [Test] + public void ShouldBeAbleToFindChildrenOfANode() + { + driver.Url = selectableItemsPage; + ReadOnlyCollection elements = driver.FindElements(By.XPath("/html/head")); + IWebElement head = elements[0]; + + ReadOnlyCollection importedScripts = head.FindElements(By.TagName("script")); + Assert.That(importedScripts, Has.Exactly(3).Items); + } + + [Test] + public void ReturnAnEmptyListWhenThereAreNoChildrenOfANode() + { + driver.Url = xhtmlTestPage; + IWebElement table = driver.FindElement(By.Id("table")); + + ReadOnlyCollection rows = table.FindElements(By.TagName("tr")); + Assert.That(rows, Is.Empty); + } + + [Test] + public void ShouldFindGrandChildren() + { + driver.Url = formsPage; + IWebElement form = driver.FindElement(By.Id("nested_form")); + form.FindElement(By.Name("x")); + } + + [Test] + public void ShouldNotFindElementOutSideTree() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Name("login")); + Assert.That( + () => element.FindElement(By.Name("x")), + Throws.InstanceOf()); + } + + [Test] + public void FindingByTagNameShouldNotIncludeParentElementIfSameTagType() + { + driver.Url = xhtmlTestPage; + IWebElement parent = driver.FindElement(By.Id("my_span")); + + Assert.That(parent.FindElements(By.TagName("div")), Has.Exactly(2).Items); + Assert.That(parent.FindElements(By.TagName("span")), Has.Exactly(2).Items); + } + + [Test] + public void FindingByCssShouldNotIncludeParentElementIfSameTagType() + { + driver.Url = xhtmlTestPage; + IWebElement parent = driver.FindElement(By.CssSelector("div#parent")); + IWebElement child = parent.FindElement(By.CssSelector("div")); + + Assert.That(child.GetAttribute("id"), Is.EqualTo("child")); + } + + [Test] + public void FindMultipleElements() + { + driver.Url = simpleTestPage; + IWebElement elem = driver.FindElement(By.Id("links")); + + ReadOnlyCollection elements = elem.FindElements(By.PartialLinkText("link")); + Assert.That(elements, Is.Not.Null); + Assert.That(elements, Has.Exactly(6).Items); + } + + [Test] + [IgnoreBrowser(Browser.Safari, "Safari does not trim")] + public void LinkWithLeadingSpaces() + { + driver.Url = simpleTestPage; + IWebElement elem = driver.FindElement(By.Id("links")); + + IWebElement res = elem.FindElement(By.PartialLinkText("link with leading space")); + Assert.That(res.Text, Is.EqualTo("link with leading space")); + } + + [Test] + [IgnoreBrowser(Browser.Safari, "Safari does not trim")] + public void LinkWithTrailingSpace() + { + driver.Url = simpleTestPage; + IWebElement elem = driver.FindElement(By.Id("links")); + + IWebElement res = elem.FindElement(By.PartialLinkText("link with trailing space")); + Assert.That(res.Text, Is.EqualTo("link with trailing space")); + } + + [Test] + public void ElementCanGetLinkByLinkTestIgnoringTrailingWhitespace() + { + driver.Url = simpleTestPage; + IWebElement elem = driver.FindElement(By.Id("links")); + + IWebElement link = elem.FindElement(By.LinkText("link with trailing space")); + Assert.That(link.GetAttribute("id"), Is.EqualTo("linkWithTrailingSpace")); } } diff --git a/dotnet/test/common/ClearTest.cs b/dotnet/test/common/ClearTest.cs index 4b8f8bf0d7b58..a5f8cc99ceed9 100644 --- a/dotnet/test/common/ClearTest.cs +++ b/dotnet/test/common/ClearTest.cs @@ -20,188 +20,187 @@ using NUnit.Framework; using OpenQA.Selenium.Environment; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ClearTest : DriverTestFixture { - [TestFixture] - public class ClearTest : DriverTestFixture - { - [Test] - public void WritableTextInputShouldClear() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("writableTextInput")); - element.Clear(); - Assert.That(element.GetAttribute("value"), Is.Empty); - } - - [Test] - public void TextInputShouldNotClearWhenDisabled() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("textInputNotEnabled")); - Assert.That(element.Enabled, Is.False); - Assert.That( - () => element.Clear(), - Throws.InstanceOf()); - } - - [Test] - public void TextInputShouldNotClearWhenReadOnly() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("readOnlyTextInput")); - Assert.That( - () => element.Clear(), - Throws.InstanceOf()); - } - - [Test] - public void WritableTextAreaShouldClear() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("writableTextArea")); - element.Clear(); - Assert.That(element.GetAttribute("value"), Is.Empty); - } - - [Test] - public void TextAreaShouldNotClearWhenDisabled() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("textAreaNotEnabled")); - Assert.That(() => element.Clear(), Throws.InstanceOf()); - } - - [Test] - public void TextAreaShouldNotClearWhenReadOnly() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("textAreaReadOnly")); - Assert.That( - () => element.Clear(), - Throws.InstanceOf()); - } - - [Test] - public void ContentEditableAreaShouldClear() - { - driver.Url = readOnlyPage; - IWebElement element = driver.FindElement(By.Id("content-editable")); - element.Clear(); - - Assert.That(element.Text, Is.Empty); - } - - [Test] - public void ShouldBeAbleToClearNoTypeInput() - { - ShouldBeAbleToClearInput(By.Name("no_type"), "input with no type"); - } - - [Test] - public void ShouldBeAbleToClearNumberInput() - { - ShouldBeAbleToClearInput(By.Name("number_input"), "42"); - } - - [Test] - public void ShouldBeAbleToClearEmailInput() - { - ShouldBeAbleToClearInput(By.Name("email_input"), "admin@localhost"); - } - - [Test] - public void ShouldBeAbleToClearPasswordInput() - { - ShouldBeAbleToClearInput(By.Name("password_input"), "qwerty"); - } - - [Test] - public void ShouldBeAbleToClearSearchInput() - { - ShouldBeAbleToClearInput(By.Name("search_input"), "search"); - } - - [Test] - public void ShouldBeAbleToClearTelInput() - { - ShouldBeAbleToClearInput(By.Name("tel_input"), "911"); - } - - [Test] - public void ShouldBeAbleToClearTextInput() - { - ShouldBeAbleToClearInput(By.Name("text_input"), "text input"); - } - - [Test] - public void ShouldBeAbleToClearUrlInput() - { - ShouldBeAbleToClearInput(By.Name("url_input"), "https://selenium.dev/"); - } - - [Test] - public void ShouldBeAbleToClearRangeInput() - { - ShouldBeAbleToClearInput(By.Name("range_input"), "42", "50"); - } - - [Test] - [IgnoreBrowser(Browser.IE, "Driver does not support clearing color elements")] - public void ShouldBeAbleToClearColorInput() - { - ShouldBeAbleToClearInput(By.Name("color_input"), "#00ffff", "#000000"); - } - - [Test] - public void ShouldBeAbleToClearDateInput() - { - ShouldBeAbleToClearInput(By.Name("date_input"), "2017-11-22"); - } - - [Test] - public void ShouldBeAbleToClearDatetimeInput() - { - ShouldBeAbleToClearInput(By.Name("datetime_input"), "2017-11-22T11:22"); - } - - [Test] - public void ShouldBeAbleToClearDatetimeLocalInput() - { - ShouldBeAbleToClearInput(By.Name("datetime_local_input"), "2017-11-22T11:22"); - } - - [Test] - public void ShouldBeAbleToClearTimeInput() - { - ShouldBeAbleToClearInput(By.Name("time_input"), "11:22"); - } - - [Test] - public void ShouldBeAbleToClearMonthInput() - { - ShouldBeAbleToClearInput(By.Name("month_input"), "2017-11"); - } - - [Test] - public void ShouldBeAbleToClearWeekInput() - { - ShouldBeAbleToClearInput(By.Name("week_input"), "2017-W47"); - } - - private void ShouldBeAbleToClearInput(By locator, string oldValue) - { - ShouldBeAbleToClearInput(locator, oldValue, string.Empty); - } - - private void ShouldBeAbleToClearInput(By locator, string oldValue, string clearedValue) - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("inputs.html"); - IWebElement element = driver.FindElement(locator); - Assert.That(element.GetAttribute("value"), Is.EqualTo(oldValue)); - - element.Clear(); - Assert.That(element.GetAttribute("value"), Is.EqualTo(clearedValue)); - } + [Test] + public void WritableTextInputShouldClear() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("writableTextInput")); + element.Clear(); + Assert.That(element.GetAttribute("value"), Is.Empty); + } + + [Test] + public void TextInputShouldNotClearWhenDisabled() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("textInputNotEnabled")); + Assert.That(element.Enabled, Is.False); + Assert.That( + () => element.Clear(), + Throws.InstanceOf()); + } + + [Test] + public void TextInputShouldNotClearWhenReadOnly() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("readOnlyTextInput")); + Assert.That( + () => element.Clear(), + Throws.InstanceOf()); + } + + [Test] + public void WritableTextAreaShouldClear() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("writableTextArea")); + element.Clear(); + Assert.That(element.GetAttribute("value"), Is.Empty); + } + + [Test] + public void TextAreaShouldNotClearWhenDisabled() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("textAreaNotEnabled")); + Assert.That(() => element.Clear(), Throws.InstanceOf()); + } + + [Test] + public void TextAreaShouldNotClearWhenReadOnly() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("textAreaReadOnly")); + Assert.That( + () => element.Clear(), + Throws.InstanceOf()); + } + + [Test] + public void ContentEditableAreaShouldClear() + { + driver.Url = readOnlyPage; + IWebElement element = driver.FindElement(By.Id("content-editable")); + element.Clear(); + + Assert.That(element.Text, Is.Empty); + } + + [Test] + public void ShouldBeAbleToClearNoTypeInput() + { + ShouldBeAbleToClearInput(By.Name("no_type"), "input with no type"); + } + + [Test] + public void ShouldBeAbleToClearNumberInput() + { + ShouldBeAbleToClearInput(By.Name("number_input"), "42"); + } + + [Test] + public void ShouldBeAbleToClearEmailInput() + { + ShouldBeAbleToClearInput(By.Name("email_input"), "admin@localhost"); + } + + [Test] + public void ShouldBeAbleToClearPasswordInput() + { + ShouldBeAbleToClearInput(By.Name("password_input"), "qwerty"); + } + + [Test] + public void ShouldBeAbleToClearSearchInput() + { + ShouldBeAbleToClearInput(By.Name("search_input"), "search"); + } + + [Test] + public void ShouldBeAbleToClearTelInput() + { + ShouldBeAbleToClearInput(By.Name("tel_input"), "911"); + } + + [Test] + public void ShouldBeAbleToClearTextInput() + { + ShouldBeAbleToClearInput(By.Name("text_input"), "text input"); + } + + [Test] + public void ShouldBeAbleToClearUrlInput() + { + ShouldBeAbleToClearInput(By.Name("url_input"), "https://selenium.dev/"); + } + + [Test] + public void ShouldBeAbleToClearRangeInput() + { + ShouldBeAbleToClearInput(By.Name("range_input"), "42", "50"); + } + + [Test] + [IgnoreBrowser(Browser.IE, "Driver does not support clearing color elements")] + public void ShouldBeAbleToClearColorInput() + { + ShouldBeAbleToClearInput(By.Name("color_input"), "#00ffff", "#000000"); + } + + [Test] + public void ShouldBeAbleToClearDateInput() + { + ShouldBeAbleToClearInput(By.Name("date_input"), "2017-11-22"); + } + + [Test] + public void ShouldBeAbleToClearDatetimeInput() + { + ShouldBeAbleToClearInput(By.Name("datetime_input"), "2017-11-22T11:22"); + } + + [Test] + public void ShouldBeAbleToClearDatetimeLocalInput() + { + ShouldBeAbleToClearInput(By.Name("datetime_local_input"), "2017-11-22T11:22"); + } + + [Test] + public void ShouldBeAbleToClearTimeInput() + { + ShouldBeAbleToClearInput(By.Name("time_input"), "11:22"); + } + + [Test] + public void ShouldBeAbleToClearMonthInput() + { + ShouldBeAbleToClearInput(By.Name("month_input"), "2017-11"); + } + + [Test] + public void ShouldBeAbleToClearWeekInput() + { + ShouldBeAbleToClearInput(By.Name("week_input"), "2017-W47"); + } + + private void ShouldBeAbleToClearInput(By locator, string oldValue) + { + ShouldBeAbleToClearInput(locator, oldValue, string.Empty); + } + + private void ShouldBeAbleToClearInput(By locator, string oldValue, string clearedValue) + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("inputs.html"); + IWebElement element = driver.FindElement(locator); + Assert.That(element.GetAttribute("value"), Is.EqualTo(oldValue)); + + element.Clear(); + Assert.That(element.GetAttribute("value"), Is.EqualTo(clearedValue)); } } diff --git a/dotnet/test/common/ClickScrollingTest.cs b/dotnet/test/common/ClickScrollingTest.cs index d5a73aed7c33c..a0ded602215bf 100644 --- a/dotnet/test/common/ClickScrollingTest.cs +++ b/dotnet/test/common/ClickScrollingTest.cs @@ -22,265 +22,264 @@ using System; using System.Drawing; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ClickScrollingTest : DriverTestFixture { - [TestFixture] - public class ClickScrollingTest : DriverTestFixture + [Test] + public void ClickingOnAnchorScrollsPage() { - [Test] - public void ClickingOnAnchorScrollsPage() - { - string scrollScript = "var pageY;"; - scrollScript += "if (typeof(window.pageYOffset) == 'number') {"; - scrollScript += "pageY = window.pageYOffset;"; - scrollScript += "} else {"; - scrollScript += "pageY = document.documentElement.scrollTop;"; - scrollScript += "}"; - scrollScript += "return pageY;"; + string scrollScript = "var pageY;"; + scrollScript += "if (typeof(window.pageYOffset) == 'number') {"; + scrollScript += "pageY = window.pageYOffset;"; + scrollScript += "} else {"; + scrollScript += "pageY = document.documentElement.scrollTop;"; + scrollScript += "}"; + scrollScript += "return pageY;"; - driver.Url = macbethPage; + driver.Url = macbethPage; - driver.FindElement(By.PartialLinkText("last speech")).Click(); + driver.FindElement(By.PartialLinkText("last speech")).Click(); - // Sometimes JS is returning a double - object result = ((IJavaScriptExecutor)driver).ExecuteScript(scrollScript); - var yOffset = Convert.ToInt64(result); + // Sometimes JS is returning a double + object result = ((IJavaScriptExecutor)driver).ExecuteScript(scrollScript); + var yOffset = Convert.ToInt64(result); - //Focusing on to click, but not actually following, - //the link will scroll it in to view, which is a few pixels further than 0 - Assert.That(yOffset, Is.GreaterThan(300), "Did not scroll"); - } + //Focusing on to click, but not actually following, + //the link will scroll it in to view, which is a few pixels further than 0 + Assert.That(yOffset, Is.GreaterThan(300), "Did not scroll"); + } - [Test] - public void ShouldScrollToClickOnAnElementHiddenByOverflow() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_out_of_bounds_overflow.html"); - driver.Url = url; + [Test] + public void ShouldScrollToClickOnAnElementHiddenByOverflow() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_out_of_bounds_overflow.html"); + driver.Url = url; - IWebElement link = driver.FindElement(By.Id("link")); - link.Click(); - } + IWebElement link = driver.FindElement(By.Id("link")); + link.Click(); + } - [Test] - public void ShouldBeAbleToClickOnAnElementHiddenByOverflow() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll.html"); + [Test] + public void ShouldBeAbleToClickOnAnElementHiddenByOverflow() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll.html"); - IWebElement link = driver.FindElement(By.Id("line8")); - // This used to throw a MoveTargetOutOfBoundsException - we don't expect it to - link.Click(); + IWebElement link = driver.FindElement(By.Id("line8")); + // This used to throw a MoveTargetOutOfBoundsException - we don't expect it to + link.Click(); - Assert.That(driver.FindElement(By.Id("clicked")).Text, Is.EqualTo("line8")); - } + Assert.That(driver.FindElement(By.Id("clicked")).Text, Is.EqualTo("line8")); + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] - public void ShouldBeAbleToClickOnAnElementHiddenByDoubleOverflow() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_double_overflow_auto.html"); + [Test] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] + public void ShouldBeAbleToClickOnAnElementHiddenByDoubleOverflow() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_double_overflow_auto.html"); - driver.FindElement(By.Id("link")).Click(); - WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); - } + driver.FindElement(By.Id("link")).Click(); + WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); + } - [Test] - public void ShouldBeAbleToClickOnAnElementHiddenByYOverflow() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_y_overflow_auto.html"); + [Test] + public void ShouldBeAbleToClickOnAnElementHiddenByYOverflow() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_y_overflow_auto.html"); - driver.FindElement(By.Id("link")).Click(); - WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); - } + driver.FindElement(By.Id("link")).Click(); + WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); + } - [Test] - [IgnoreBrowser(Browser.IE, "Scroll bar gets in they way of clicking center element")] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] - public void ShouldBeAbleToClickOnAnElementPartiallyHiddenByOverflow() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_partially_hidden_element.html"); - driver.FindElement(By.Id("btn")).Click(); - WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); - } + [Test] + [IgnoreBrowser(Browser.IE, "Scroll bar gets in they way of clicking center element")] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] + public void ShouldBeAbleToClickOnAnElementPartiallyHiddenByOverflow() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_partially_hidden_element.html"); + driver.FindElement(By.Id("btn")).Click(); + WaitFor(TitleToBe("Clicked Successfully!"), "Browser title was not 'Clicked Successfully'"); + } - [Test] - public void ShouldNotScrollOverflowElementsWhichAreVisible() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll2.html"); - IWebElement list = driver.FindElement(By.TagName("ul")); - IWebElement item = list.FindElement(By.Id("desired")); - item.Click(); - long yOffset = (long)((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0].scrollTop;", list); - Assert.That(yOffset, Is.Zero, "Should not have scrolled"); - } + [Test] + public void ShouldNotScrollOverflowElementsWhichAreVisible() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll2.html"); + IWebElement list = driver.FindElement(By.TagName("ul")); + IWebElement item = list.FindElement(By.Id("desired")); + item.Click(); + long yOffset = (long)((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0].scrollTop;", list); + Assert.That(yOffset, Is.Zero, "Should not have scrolled"); + } - [Test] - [IgnoreBrowser(Browser.IE, "IE is scrolling Button2 to top of screen instead of bottom of screen as per spec")] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] - public void ShouldNotScrollIfAlreadyScrolledAndElementIsInView() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll3.html"); - driver.FindElement(By.Id("button2")).Click(); - double scrollTop = GetScrollTop(); - driver.FindElement(By.Id("button1")).Click(); - Assert.That(GetScrollTop(), Is.EqualTo(scrollTop)); - } + [Test] + [IgnoreBrowser(Browser.IE, "IE is scrolling Button2 to top of screen instead of bottom of screen as per spec")] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] + public void ShouldNotScrollIfAlreadyScrolledAndElementIsInView() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll3.html"); + driver.FindElement(By.Id("button2")).Click(); + double scrollTop = GetScrollTop(); + driver.FindElement(By.Id("button1")).Click(); + Assert.That(GetScrollTop(), Is.EqualTo(scrollTop)); + } + + [Test] + public void ShouldBeAbleToClickRadioButtonScrolledIntoView() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll4.html"); + driver.FindElement(By.Id("radio")).Click(); + // If we don't throw, we're good + } - [Test] - public void ShouldBeAbleToClickRadioButtonScrolledIntoView() + [Test] + [IgnoreBrowser(Browser.IE, "IE has special overflow handling")] + public void ShouldScrollOverflowElementsIfClickPointIsOutOfViewButElementIsInView() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll5.html"); + driver.FindElement(By.Id("inner")).Click(); + Assert.That(driver.FindElement(By.Id("clicked")).Text, Is.EqualTo("clicked")); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1314462")] + public void ShouldBeAbleToClickElementInAFrameThatIsOutOfView() + { + try { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll4.html"); - driver.FindElement(By.Id("radio")).Click(); - // If we don't throw, we're good + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_frame_out_of_view.html"); + driver.SwitchTo().Frame("frame"); + IWebElement element = driver.FindElement(By.Name("checkbox")); + element.Click(); + Assert.That(element.Selected, "Element is not selected"); } - - [Test] - [IgnoreBrowser(Browser.IE, "IE has special overflow handling")] - public void ShouldScrollOverflowElementsIfClickPointIsOutOfViewButElementIsInView() + finally { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll5.html"); - driver.FindElement(By.Id("inner")).Click(); - Assert.That(driver.FindElement(By.Id("clicked")).Text, Is.EqualTo("clicked")); + driver.SwitchTo().DefaultContent(); } + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1314462")] - public void ShouldBeAbleToClickElementInAFrameThatIsOutOfView() + [Test] + public void ShouldBeAbleToClickElementThatIsOutOfViewInAFrame() + { + try { - try - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_frame_out_of_view.html"); - driver.SwitchTo().Frame("frame"); - IWebElement element = driver.FindElement(By.Name("checkbox")); - element.Click(); - Assert.That(element.Selected, "Element is not selected"); - } - finally - { - driver.SwitchTo().DefaultContent(); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_scrolling_frame.html"); + driver.SwitchTo().Frame("scrolling_frame"); + IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); + element.Click(); + Assert.That(element.Selected, "Element is not selected"); } - - [Test] - public void ShouldBeAbleToClickElementThatIsOutOfViewInAFrame() + finally { - try - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_scrolling_frame.html"); - driver.SwitchTo().Frame("scrolling_frame"); - IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); - element.Click(); - Assert.That(element.Selected, "Element is not selected"); - } - finally - { - driver.SwitchTo().DefaultContent(); - } + driver.SwitchTo().DefaultContent(); } + } - [Test] - public void ShouldBeAbleToClickElementThatIsOutOfViewInAFrameThatIsOutOfView() + [Test] + public void ShouldBeAbleToClickElementThatIsOutOfViewInAFrameThatIsOutOfView() + { + try { - try - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_scrolling_frame_out_of_view.html"); - driver.SwitchTo().Frame("scrolling_frame"); - IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); - element.Click(); - Assert.That(element.Selected, "Element is not selected"); - } - finally - { - driver.SwitchTo().DefaultContent(); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_scrolling_frame_out_of_view.html"); + driver.SwitchTo().Frame("scrolling_frame"); + IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); + element.Click(); + Assert.That(element.Selected, "Element is not selected"); } - - [Test] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] - public void ShouldBeAbleToClickElementThatIsOutOfViewInANestedFrame() + finally { - try - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_nested_scrolling_frames.html"); - driver.SwitchTo().Frame("scrolling_frame"); - driver.SwitchTo().Frame("nested_scrolling_frame"); - IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); - element.Click(); - Assert.That(element.Selected, "Element is not selected"); - } - finally - { - driver.SwitchTo().DefaultContent(); - } + driver.SwitchTo().DefaultContent(); } + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] - public void ShouldBeAbleToClickElementThatIsOutOfViewInANestedFrameThatIsOutOfView() + [Test] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] + public void ShouldBeAbleToClickElementThatIsOutOfViewInANestedFrame() + { + try { - try - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html"); - driver.SwitchTo().Frame("scrolling_frame"); - driver.SwitchTo().Frame("nested_scrolling_frame"); - IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); - element.Click(); - Assert.That(element.Selected, "Element is not selected"); - } - finally - { - driver.SwitchTo().DefaultContent(); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_nested_scrolling_frames.html"); + driver.SwitchTo().Frame("scrolling_frame"); + driver.SwitchTo().Frame("nested_scrolling_frame"); + IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); + element.Click(); + Assert.That(element.Selected, "Element is not selected"); } - - [Test] - public void ShouldNotScrollWhenGettingElementSize() + finally { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll3.html"); - double scrollTop = GetScrollTop(); - Size ignoredSize = driver.FindElement(By.Id("button1")).Size; - Assert.That(GetScrollTop(), Is.EqualTo(scrollTop)); + driver.SwitchTo().DefaultContent(); } + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1314462")] - public void ShouldBeAbleToClickElementInATallFrame() + [Test] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/2013")] + public void ShouldBeAbleToClickElementThatIsOutOfViewInANestedFrameThatIsOutOfView() + { + try { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_tall_frame.html"); - driver.SwitchTo().Frame("tall_frame"); - IWebElement element = driver.FindElement(By.Name("checkbox")); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_nested_scrolling_frames_out_of_view.html"); + driver.SwitchTo().Frame("scrolling_frame"); + driver.SwitchTo().Frame("nested_scrolling_frame"); + IWebElement element = driver.FindElement(By.Name("scroll_checkbox")); element.Click(); Assert.That(element.Selected, "Element is not selected"); } - - //------------------------------------------------------------------ - // Tests below here are not included in the Java test suite - //------------------------------------------------------------------ - [Test] - [IgnoreBrowser(Browser.IE, "Clicking label is not changing checkbox")] - [IgnoreTarget("net48", "Cannot create inline page with UrlBuilder")] - public void ShouldBeAbleToClickInlineTextElementWithChildElementAfterScrolling() + finally { - driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() - .WithBody( - "
Force scroll needed
")); - IWebElement label = driver.FindElement(By.Id("wrapper")); - label.Click(); - IWebElement checkbox = driver.FindElement(By.Id("check")); - Assert.That(checkbox.Selected, Is.False, "Checkbox should not be selected after click"); + driver.SwitchTo().DefaultContent(); } + } - private double GetScrollTop() - { - return double.Parse(((IJavaScriptExecutor)driver).ExecuteScript("return document.body.scrollTop;").ToString()); - } + [Test] + public void ShouldNotScrollWhenGettingElementSize() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scroll3.html"); + double scrollTop = GetScrollTop(); + Size ignoredSize = driver.FindElement(By.Id("button1")).Size; + Assert.That(GetScrollTop(), Is.EqualTo(scrollTop)); + } - private Func TitleToBe(string desiredTitle) + [Test] + [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1314462")] + public void ShouldBeAbleToClickElementInATallFrame() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/page_with_tall_frame.html"); + driver.SwitchTo().Frame("tall_frame"); + IWebElement element = driver.FindElement(By.Name("checkbox")); + element.Click(); + Assert.That(element.Selected, "Element is not selected"); + } + + //------------------------------------------------------------------ + // Tests below here are not included in the Java test suite + //------------------------------------------------------------------ + [Test] + [IgnoreBrowser(Browser.IE, "Clicking label is not changing checkbox")] + [IgnoreTarget("net48", "Cannot create inline page with UrlBuilder")] + public void ShouldBeAbleToClickInlineTextElementWithChildElementAfterScrolling() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.CreateInlinePage(new InlinePage() + .WithBody( + "
Force scroll needed
")); + IWebElement label = driver.FindElement(By.Id("wrapper")); + label.Click(); + IWebElement checkbox = driver.FindElement(By.Id("check")); + Assert.That(checkbox.Selected, Is.False, "Checkbox should not be selected after click"); + } + + private double GetScrollTop() + { + return double.Parse(((IJavaScriptExecutor)driver).ExecuteScript("return document.body.scrollTop;").ToString()); + } + + private Func TitleToBe(string desiredTitle) + { + return () => { - return () => - { - return driver.Title == desiredTitle; - }; - } + return driver.Title == desiredTitle; + }; } } diff --git a/dotnet/test/common/ClickTest.cs b/dotnet/test/common/ClickTest.cs index 9f359f265f841..6d5bcd1f9d62a 100644 --- a/dotnet/test/common/ClickTest.cs +++ b/dotnet/test/common/ClickTest.cs @@ -21,351 +21,350 @@ using OpenQA.Selenium.Environment; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ClickTest : DriverTestFixture { - [TestFixture] - public class ClickTest : DriverTestFixture + [SetUp] + public void SetupMethod() { - [SetUp] - public void SetupMethod() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("clicks.html"); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("clicks.html"); + } - [TearDown] - public void TearDownMethod() - { - driver.SwitchTo().DefaultContent(); - } + [TearDown] + public void TearDownMethod() + { + driver.SwitchTo().DefaultContent(); + } - [Test] - public void CanClickOnALinkAndFollowIt() - { - driver.FindElement(By.Id("normal")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void CanClickOnALinkAndFollowIt() + { + driver.FindElement(By.Id("normal")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - [Test] - public void CanClickOnALinkThatOverflowsAndFollowIt() - { - driver.FindElement(By.Id("overflowLink")).Click(); + [Test] + public void CanClickOnALinkThatOverflowsAndFollowIt() + { + driver.FindElement(By.Id("overflowLink")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - } + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + } - [Test] - public void CanClickOnAnAnchorAndNotReloadThePage() - { - ((IJavaScriptExecutor)driver).ExecuteScript("document.latch = true"); + [Test] + public void CanClickOnAnAnchorAndNotReloadThePage() + { + ((IJavaScriptExecutor)driver).ExecuteScript("document.latch = true"); - driver.FindElement(By.Id("anchor")).Click(); + driver.FindElement(By.Id("anchor")).Click(); - bool samePage = (bool)((IJavaScriptExecutor)driver).ExecuteScript("return document.latch"); + bool samePage = (bool)((IJavaScriptExecutor)driver).ExecuteScript("return document.latch"); - Assert.That(samePage, Is.True, "Latch was reset"); - } + Assert.That(samePage, Is.True, "Latch was reset"); + } - [Test] - public void CanClickOnALinkThatUpdatesAnotherFrame() - { - driver.SwitchTo().Frame("source"); + [Test] + public void CanClickOnALinkThatUpdatesAnotherFrame() + { + driver.SwitchTo().Frame("source"); - driver.FindElement(By.Id("otherframe")).Click(); - driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); + driver.FindElement(By.Id("otherframe")).Click(); + driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); - Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); - } + Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); + } - [Test] - public void ElementsFoundByJsCanLoadUpdatesInAnotherFrame() - { - driver.SwitchTo().Frame("source"); + [Test] + public void ElementsFoundByJsCanLoadUpdatesInAnotherFrame() + { + driver.SwitchTo().Frame("source"); - IWebElement toClick = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('otherframe');"); - toClick.Click(); - driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); + IWebElement toClick = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('otherframe');"); + toClick.Click(); + driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); - Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); - } + Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); + } - [Test] - public void JsLocatedElementsCanUpdateFramesIfFoundSomehowElse() - { - driver.SwitchTo().Frame("source"); + [Test] + public void JsLocatedElementsCanUpdateFramesIfFoundSomehowElse() + { + driver.SwitchTo().Frame("source"); - // Prime the cache of elements - driver.FindElement(By.Id("otherframe")); + // Prime the cache of elements + driver.FindElement(By.Id("otherframe")); - // This _should_ return the same element - IWebElement toClick = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('otherframe');"); - toClick.Click(); - driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); + // This _should_ return the same element + IWebElement toClick = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('otherframe');"); + toClick.Click(); + driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); - Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); - } + Assert.That(driver.PageSource, Does.Contain("Hello WebDriver")); + } - [Test] + [Test] - public void CanClickOnAnElementWithTopSetToANegativeNumber() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("styledPage.html"); - IWebElement searchBox = driver.FindElement(By.Name("searchBox")); - searchBox.SendKeys("Cheese"); - driver.FindElement(By.Name("btn")).Click(); + public void CanClickOnAnElementWithTopSetToANegativeNumber() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("styledPage.html"); + IWebElement searchBox = driver.FindElement(By.Name("searchBox")); + searchBox.SendKeys("Cheese"); + driver.FindElement(By.Name("btn")).Click(); - string log = driver.FindElement(By.Id("log")).Text; - Assert.That(log, Is.EqualTo("click")); - } + string log = driver.FindElement(By.Id("log")).Text; + Assert.That(log, Is.EqualTo("click")); + } + + [Test] + public void ShouldSetRelatedTargetForMouseOver() + { + driver.Url = javascriptPage; + + driver.FindElement(By.Id("movable")).Click(); - [Test] - public void ShouldSetRelatedTargetForMouseOver() + string log = driver.FindElement(By.Id("result")).Text; + + // Note: It is not guaranteed that the relatedTarget property of the mouseover + // event will be the parent, when using native events. Only check that the mouse + // has moved to this element, not that the parent element was the related target. + if (this.IsNativeEventsEnabled) { - driver.Url = javascriptPage; - - driver.FindElement(By.Id("movable")).Click(); - - string log = driver.FindElement(By.Id("result")).Text; - - // Note: It is not guaranteed that the relatedTarget property of the mouseover - // event will be the parent, when using native events. Only check that the mouse - // has moved to this element, not that the parent element was the related target. - if (this.IsNativeEventsEnabled) - { - Assert.That(log, Does.StartWith("parent matches?")); - } - else - { - Assert.That(log, Is.EqualTo("parent matches? true")); - } + Assert.That(log, Does.StartWith("parent matches?")); } - - [Test] - public void ShouldClickOnFirstBoundingClientRectWithNonZeroSize() + else { - driver.FindElement(By.Id("twoClientRects")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + Assert.That(log, Is.EqualTo("parent matches? true")); } + } - [Test] - [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] - [NeedsFreshDriver(IsCreatedAfterTest = true)] - public void ShouldOnlyFollowHrefOnce() - { - driver.Url = clicksPage; - int windowHandlesBefore = driver.WindowHandles.Count; + [Test] + public void ShouldClickOnFirstBoundingClientRectWithNonZeroSize() + { + driver.FindElement(By.Id("twoClientRects")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - driver.FindElement(By.Id("new-window")).Click(); - WaitFor(() => { return driver.WindowHandles.Count >= windowHandlesBefore + 1; }, "Window handles was not " + (windowHandlesBefore + 1).ToString()); - Assert.That(driver.WindowHandles, Has.Exactly(windowHandlesBefore + 1).Items); - } + [Test] + [IgnoreBrowser(Browser.IE, "Edge in IE Mode does not properly handle multiple windows")] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + public void ShouldOnlyFollowHrefOnce() + { + driver.Url = clicksPage; + int windowHandlesBefore = driver.WindowHandles.Count; - [Test] - public void ClickingLabelShouldSetCheckbox() - { - driver.Url = formsPage; + driver.FindElement(By.Id("new-window")).Click(); + WaitFor(() => { return driver.WindowHandles.Count >= windowHandlesBefore + 1; }, "Window handles was not " + (windowHandlesBefore + 1).ToString()); + Assert.That(driver.WindowHandles, Has.Exactly(windowHandlesBefore + 1).Items); + } - driver.FindElement(By.Id("label-for-checkbox-with-label")).Click(); + [Test] + public void ClickingLabelShouldSetCheckbox() + { + driver.Url = formsPage; - Assert.That(driver.FindElement(By.Id("checkbox-with-label")).Selected, "Checkbox should be selected"); - } + driver.FindElement(By.Id("label-for-checkbox-with-label")).Click(); - [Test] - public void CanClickOnALinkWithEnclosedImage() - { - driver.FindElement(By.Id("link-with-enclosed-image")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + Assert.That(driver.FindElement(By.Id("checkbox-with-label")).Selected, "Checkbox should be selected"); + } - [Test] - public void CanClickOnAnImageEnclosedInALink() - { - driver.FindElement(By.Id("link-with-enclosed-image")).FindElement(By.TagName("img")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void CanClickOnALinkWithEnclosedImage() + { + driver.FindElement(By.Id("link-with-enclosed-image")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - [Test] - public void CanClickOnALinkThatContainsTextWrappedInASpan() - { - driver.FindElement(By.Id("link-with-enclosed-span")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void CanClickOnAnImageEnclosedInALink() + { + driver.FindElement(By.Id("link-with-enclosed-image")).FindElement(By.TagName("img")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/653")] - public void CanClickOnALinkThatContainsEmbeddedBlockElements() - { - driver.FindElement(By.Id("embeddedBlock")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void CanClickOnALinkThatContainsTextWrappedInASpan() + { + driver.FindElement(By.Id("link-with-enclosed-span")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - [Test] - public void CanClickOnAnElementEnclosedInALink() - { - driver.FindElement(By.Id("link-with-enclosed-span")).FindElement(By.TagName("span")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/653")] + public void CanClickOnALinkThatContainsEmbeddedBlockElements() + { + driver.FindElement(By.Id("embeddedBlock")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - [Test] - public void ShouldBeAbleToClickOnAnElementInTheViewport() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_out_of_bounds.html"); + [Test] + public void CanClickOnAnElementEnclosedInALink() + { + driver.FindElement(By.Id("link-with-enclosed-span")).FindElement(By.TagName("span")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - driver.Url = url; - IWebElement button = driver.FindElement(By.Id("button")); - button.Click(); - } + [Test] + public void ShouldBeAbleToClickOnAnElementInTheViewport() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_out_of_bounds.html"); - [Test] - public void ClicksASurroundingStrongTag() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("ClickTest_testClicksASurroundingStrongTag.html"); - driver.FindElement(By.TagName("a")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - } + driver.Url = url; + IWebElement button = driver.FindElement(By.Id("button")); + button.Click(); + } - [Test] - [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1502636")] - public void CanClickAnImageMapArea() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); - driver.FindElement(By.Id("rectG")).Click(); - WaitFor(() => { return driver.Title == "Target Page 1"; }, "Browser title was not 'Target Page 1'"); + [Test] + public void ClicksASurroundingStrongTag() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("ClickTest_testClicksASurroundingStrongTag.html"); + driver.FindElement(By.TagName("a")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + } - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); - driver.FindElement(By.Id("circleO")).Click(); - WaitFor(() => { return driver.Title == "Target Page 2"; }, "Browser title was not 'Target Page 2'"); + [Test] + [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1502636")] + public void CanClickAnImageMapArea() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); + driver.FindElement(By.Id("rectG")).Click(); + WaitFor(() => { return driver.Title == "Target Page 1"; }, "Browser title was not 'Target Page 1'"); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); - driver.FindElement(By.Id("polyLE")).Click(); - WaitFor(() => { return driver.Title == "Target Page 3"; }, "Browser title was not 'Target Page 3'"); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); + driver.FindElement(By.Id("circleO")).Click(); + WaitFor(() => { return driver.Title == "Target Page 2"; }, "Browser title was not 'Target Page 2'"); - [Test] - [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1422272")] - public void ShouldBeAbleToClickOnAnElementGreaterThanTwoViewports() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_too_big.html"); - driver.Url = url; + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/google_map.html"); + driver.FindElement(By.Id("polyLE")).Click(); + WaitFor(() => { return driver.Title == "Target Page 3"; }, "Browser title was not 'Target Page 3'"); + } - IWebElement element = driver.FindElement(By.Id("click")); + [Test] + [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1422272")] + public void ShouldBeAbleToClickOnAnElementGreaterThanTwoViewports() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_too_big.html"); + driver.Url = url; - element.Click(); + IWebElement element = driver.FindElement(By.Id("click")); - WaitFor(() => { return driver.Title == "clicks"; }, "Browser title was not 'clicks'"); - } + element.Click(); - [Test] - [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1937115")] - public void ShouldBeAbleToClickOnAnElementInFrameGreaterThanTwoViewports() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_too_big_in_frame.html"); - driver.Url = url; + WaitFor(() => { return driver.Title == "clicks"; }, "Browser title was not 'clicks'"); + } - IWebElement frame = driver.FindElement(By.Id("iframe1")); - driver.SwitchTo().Frame(frame); + [Test] + [IgnoreBrowser(Browser.Firefox, "https://bugzilla.mozilla.org/show_bug.cgi?id=1937115")] + public void ShouldBeAbleToClickOnAnElementInFrameGreaterThanTwoViewports() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_too_big_in_frame.html"); + driver.Url = url; - IWebElement element = driver.FindElement(By.Id("click")); + IWebElement frame = driver.FindElement(By.Id("iframe1")); + driver.SwitchTo().Frame(frame); - element.Click(); + IWebElement element = driver.FindElement(By.Id("click")); - WaitFor(() => { return driver.Title == "clicks"; }, "Browser title was not 'clicks'"); - } + element.Click(); - [Test] - public void ShouldBeAbleToClickOnRightToLeftLanguageLink() - { - String url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_rtl.html"); - driver.Url = url; + WaitFor(() => { return driver.Title == "clicks"; }, "Browser title was not 'clicks'"); + } - IWebElement element = driver.FindElement(By.Id("ar_link")); - element.Click(); + [Test] + public void ShouldBeAbleToClickOnRightToLeftLanguageLink() + { + String url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_rtl.html"); + driver.Url = url; - WaitFor(() => driver.Title == "clicks", "Expected title to be 'clicks'"); - Assert.That(driver.Title, Is.EqualTo("clicks")); - } + IWebElement element = driver.FindElement(By.Id("ar_link")); + element.Click(); - [Test] - public void ShouldBeAbleToClickOnLinkInAbsolutelyPositionedFooter() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("fixedFooterNoScroll.html"); - driver.Url = url; + WaitFor(() => driver.Title == "clicks", "Expected title to be 'clicks'"); + Assert.That(driver.Title, Is.EqualTo("clicks")); + } - driver.FindElement(By.Id("link")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void ShouldBeAbleToClickOnLinkInAbsolutelyPositionedFooter() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("fixedFooterNoScroll.html"); + driver.Url = url; - [Test] - public void ShouldBeAbleToClickOnLinkInAbsolutelyPositionedFooterInQuirksMode() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("fixedFooterNoScrollQuirksMode.html"); - driver.Url = url; + driver.FindElement(By.Id("link")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - driver.FindElement(By.Id("link")).Click(); - WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - } + [Test] + public void ShouldBeAbleToClickOnLinkInAbsolutelyPositionedFooterInQuirksMode() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("fixedFooterNoScrollQuirksMode.html"); + driver.Url = url; - [Test] - public void ShouldBeAbleToClickOnLinksWithNoHrefAttribute() - { - driver.Url = javascriptPage; + driver.FindElement(By.Id("link")).Click(); + WaitFor(() => { return driver.Title == "XHTML Test Page"; }, "Browser title was not 'XHTML Test Page'"); + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + } - IWebElement element = driver.FindElement(By.LinkText("No href")); - element.Click(); + [Test] + public void ShouldBeAbleToClickOnLinksWithNoHrefAttribute() + { + driver.Url = javascriptPage; - WaitFor(() => driver.Title == "Changed", "Expected title to be 'Changed'"); - Assert.That(driver.Title, Is.EqualTo("Changed")); - } + IWebElement element = driver.FindElement(By.LinkText("No href")); + element.Click(); - [Test] - public void ShouldBeAbleToClickOnALinkThatWrapsToTheNextLine() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/link_that_wraps.html"); + WaitFor(() => driver.Title == "Changed", "Expected title to be 'Changed'"); + Assert.That(driver.Title, Is.EqualTo("Changed")); + } - driver.FindElement(By.Id("link")).Click(); + [Test] + public void ShouldBeAbleToClickOnALinkThatWrapsToTheNextLine() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/link_that_wraps.html"); - WaitFor(() => driver.Title == "Submitted Successfully!", "Expected title to be 'Submitted Successfully!'"); - Assert.That(driver.Title, Is.EqualTo("Submitted Successfully!")); - } + driver.FindElement(By.Id("link")).Click(); - [Test] - public void ShouldBeAbleToClickOnASpanThatWrapsToTheNextLine() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/span_that_wraps.html"); + WaitFor(() => driver.Title == "Submitted Successfully!", "Expected title to be 'Submitted Successfully!'"); + Assert.That(driver.Title, Is.EqualTo("Submitted Successfully!")); + } - driver.FindElement(By.Id("span")).Click(); + [Test] + public void ShouldBeAbleToClickOnASpanThatWrapsToTheNextLine() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/span_that_wraps.html"); - WaitFor(() => driver.Title == "Submitted Successfully!", "Expected title to be 'Submitted Successfully!'"); - Assert.That(driver.Title, Is.EqualTo("Submitted Successfully!")); - } + driver.FindElement(By.Id("span")).Click(); - [Test] - public void ClickingOnADisabledElementIsANoOp() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/disabled_element.html"); + WaitFor(() => driver.Title == "Submitted Successfully!", "Expected title to be 'Submitted Successfully!'"); + Assert.That(driver.Title, Is.EqualTo("Submitted Successfully!")); + } - IWebElement element = driver.FindElement(By.Name("disabled")); - element.Click(); - } + [Test] + public void ClickingOnADisabledElementIsANoOp() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/disabled_element.html"); - //------------------------------------------------------------------ - // Tests below here are not included in the Java test suite - //------------------------------------------------------------------ - [Test] - public void ShouldBeAbleToClickLinkContainingLineBreak() - { - driver.Url = simpleTestPage; - driver.FindElement(By.Id("multilinelink")).Click(); - WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title was not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + IWebElement element = driver.FindElement(By.Name("disabled")); + element.Click(); + } + + //------------------------------------------------------------------ + // Tests below here are not included in the Java test suite + //------------------------------------------------------------------ + [Test] + public void ShouldBeAbleToClickLinkContainingLineBreak() + { + driver.Url = simpleTestPage; + driver.FindElement(By.Id("multilinelink")).Click(); + WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title was not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); } } diff --git a/dotnet/test/common/CommandTests.cs b/dotnet/test/common/CommandTests.cs index f0480c034efd3..249ed0c66bb57 100644 --- a/dotnet/test/common/CommandTests.cs +++ b/dotnet/test/common/CommandTests.cs @@ -20,22 +20,21 @@ using NUnit.Framework; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class CommandTests { - [TestFixture] - public class CommandTests + [Test] + public void CommandSerializesAnonymousType() { - [Test] - public void CommandSerializesAnonymousType() + var parameters = new Dictionary { - var parameters = new Dictionary - { - ["arg"] = new { param1 = true, param2 = false }, - }; + ["arg"] = new { param1 = true, param2 = false }, + }; - var command = new Command(new SessionId("session"), "test command", parameters); + var command = new Command(new SessionId("session"), "test command", parameters); - Assert.That(command.ParametersAsJsonString, Is.EqualTo("""{"arg":{"param1":true,"param2":false}}""")); - } + Assert.That(command.ParametersAsJsonString, Is.EqualTo("""{"arg":{"param1":true,"param2":false}}""")); } } diff --git a/dotnet/test/common/ContentEditableTest.cs b/dotnet/test/common/ContentEditableTest.cs index 73616fe13848f..fb15e0c763856 100644 --- a/dotnet/test/common/ContentEditableTest.cs +++ b/dotnet/test/common/ContentEditableTest.cs @@ -21,117 +21,116 @@ using OpenQA.Selenium.Environment; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ContentEditableTest : DriverTestFixture { - [TestFixture] - public class ContentEditableTest : DriverTestFixture + [TearDown] + public void SwitchToDefaultContent() + { + driver.SwitchTo().DefaultContent(); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] + public void TypingIntoAnIFrameWithContentEditableOrDesignModeSet() + { + driver.Url = richTextPage; + + driver.SwitchTo().Frame("editFrame"); + IWebElement element = driver.SwitchTo().ActiveElement(); + element.SendKeys("Fishy"); + + driver.SwitchTo().DefaultContent(); + IWebElement trusted = driver.FindElement(By.Id("istrusted")); + IWebElement id = driver.FindElement(By.Id("tagId")); + + // Chrome does not set a trusted flag. + Assert.That(trusted.Text, Is.AnyOf("[true]", "[n/a]", "[]")); + Assert.That(id.Text, Is.AnyOf("[frameHtml]", "[theBody]")); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] + [IgnoreBrowser(Browser.Safari, "Non-printable characters do not navigate within element")] + public void NonPrintableCharactersShouldWorkWithContentEditableOrDesignModeSet() + { + driver.Url = richTextPage; + + driver.SwitchTo().Frame("editFrame"); + IWebElement element = driver.SwitchTo().ActiveElement(); + element.SendKeys("Dishy" + Keys.Backspace + Keys.Left + Keys.Left); + element.SendKeys(Keys.Left + Keys.Left + "F" + Keys.Delete + Keys.End + "ee!"); + + Assert.That(element.Text, Is.EqualTo("Fishee!")); + } + + [Test] + public void ShouldBeAbleToTypeIntoEmptyContentEditableElement() + { + driver.Url = readOnlyPage; + IWebElement editable = driver.FindElement(By.Id("content-editable-blank")); + + editable.SendKeys("cheese"); + + Assert.That(editable.Text, Is.EqualTo("cheese")); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Driver prepends text in contentEditable areas")] + [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] + public void ShouldBeAbleToTypeIntoContentEditableElementWithExistingValue() + { + driver.Url = readOnlyPage; + IWebElement editable = driver.FindElement(By.Id("content-editable")); + + String initialText = editable.Text; + editable.SendKeys(", edited"); + + Assert.That(editable.Text, Is.EqualTo(initialText + ", edited")); + } + + [Test] + public void ShouldBeAbleToTypeIntoTinyMCE() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("tinymce.html"); + driver.SwitchTo().Frame("mce_0_ifr"); + + IWebElement editable = driver.FindElement(By.Id("tinymce")); + + editable.Clear(); + editable.SendKeys("cheese"); // requires focus on OS X + + Assert.That(editable.Text, Is.EqualTo("cheese")); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Driver prepends text in contentEditable areas")] + [IgnoreBrowser(Browser.IE, "Prepends text")] + [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] + public void ShouldAppendToTinyMCE() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("tinymce.html"); + driver.SwitchTo().Frame("mce_0_ifr"); + + IWebElement editable = driver.FindElement(By.Id("tinymce")); + + editable.SendKeys(" and cheese"); // requires focus on OS X + WaitFor(() => editable.Text != "Initial content", "Text remained the original text"); + + Assert.That(editable.Text, Is.EqualTo("Initial content and cheese")); + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] + [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] + public void AppendsTextToEndOfContentEditableWithMultipleTextNodes() { - [TearDown] - public void SwitchToDefaultContent() - { - driver.SwitchTo().DefaultContent(); - } - - [Test] - [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] - public void TypingIntoAnIFrameWithContentEditableOrDesignModeSet() - { - driver.Url = richTextPage; - - driver.SwitchTo().Frame("editFrame"); - IWebElement element = driver.SwitchTo().ActiveElement(); - element.SendKeys("Fishy"); - - driver.SwitchTo().DefaultContent(); - IWebElement trusted = driver.FindElement(By.Id("istrusted")); - IWebElement id = driver.FindElement(By.Id("tagId")); - - // Chrome does not set a trusted flag. - Assert.That(trusted.Text, Is.AnyOf("[true]", "[n/a]", "[]")); - Assert.That(id.Text, Is.AnyOf("[frameHtml]", "[theBody]")); - } - - [Test] - [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] - [IgnoreBrowser(Browser.Safari, "Non-printable characters do not navigate within element")] - public void NonPrintableCharactersShouldWorkWithContentEditableOrDesignModeSet() - { - driver.Url = richTextPage; - - driver.SwitchTo().Frame("editFrame"); - IWebElement element = driver.SwitchTo().ActiveElement(); - element.SendKeys("Dishy" + Keys.Backspace + Keys.Left + Keys.Left); - element.SendKeys(Keys.Left + Keys.Left + "F" + Keys.Delete + Keys.End + "ee!"); - - Assert.That(element.Text, Is.EqualTo("Fishee!")); - } - - [Test] - public void ShouldBeAbleToTypeIntoEmptyContentEditableElement() - { - driver.Url = readOnlyPage; - IWebElement editable = driver.FindElement(By.Id("content-editable-blank")); - - editable.SendKeys("cheese"); - - Assert.That(editable.Text, Is.EqualTo("cheese")); - } - - [Test] - [IgnoreBrowser(Browser.Firefox, "Driver prepends text in contentEditable areas")] - [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] - public void ShouldBeAbleToTypeIntoContentEditableElementWithExistingValue() - { - driver.Url = readOnlyPage; - IWebElement editable = driver.FindElement(By.Id("content-editable")); - - String initialText = editable.Text; - editable.SendKeys(", edited"); - - Assert.That(editable.Text, Is.EqualTo(initialText + ", edited")); - } - - [Test] - public void ShouldBeAbleToTypeIntoTinyMCE() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("tinymce.html"); - driver.SwitchTo().Frame("mce_0_ifr"); - - IWebElement editable = driver.FindElement(By.Id("tinymce")); - - editable.Clear(); - editable.SendKeys("cheese"); // requires focus on OS X - - Assert.That(editable.Text, Is.EqualTo("cheese")); - } - - [Test] - [IgnoreBrowser(Browser.Firefox, "Driver prepends text in contentEditable areas")] - [IgnoreBrowser(Browser.IE, "Prepends text")] - [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] - public void ShouldAppendToTinyMCE() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("tinymce.html"); - driver.SwitchTo().Frame("mce_0_ifr"); - - IWebElement editable = driver.FindElement(By.Id("tinymce")); - - editable.SendKeys(" and cheese"); // requires focus on OS X - WaitFor(() => editable.Text != "Initial content", "Text remained the original text"); - - Assert.That(editable.Text, Is.EqualTo("Initial content and cheese")); - } - - [Test] - [IgnoreBrowser(Browser.Firefox, "Browser does not automatically focus body element in frame")] - [IgnoreBrowser(Browser.Safari, "Driver prepends text to contentEditable areas")] - public void AppendsTextToEndOfContentEditableWithMultipleTextNodes() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("content-editable.html"); - IWebElement input = driver.FindElement(By.Id("editable")); - input.SendKeys(", world!"); - WaitFor(() => input.Text != "Why hello", "Text remained the original text"); - Assert.That(input.Text, Is.EqualTo("Why hello, world!")); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("content-editable.html"); + IWebElement input = driver.FindElement(By.Id("editable")); + input.SendKeys(", world!"); + WaitFor(() => input.Text != "Why hello", "Text remained the original text"); + Assert.That(input.Text, Is.EqualTo("Why hello, world!")); } } diff --git a/dotnet/test/common/CookieImplementationTest.cs b/dotnet/test/common/CookieImplementationTest.cs index 323ebf5834207..78950a639dc3f 100644 --- a/dotnet/test/common/CookieImplementationTest.cs +++ b/dotnet/test/common/CookieImplementationTest.cs @@ -25,981 +25,980 @@ using System.Text; using System.Text.RegularExpressions; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class CookieImplementationTest : DriverTestFixture { - [TestFixture] - public class CookieImplementationTest : DriverTestFixture - { - private Random random = new Random(); - private bool isOnAlternativeHostName; - private string hostname; + private Random random = new Random(); + private bool isOnAlternativeHostName; + private string hostname; + + [SetUp] + public void GoToSimplePageAndDeleteCookies() + { + GotoValidDomainAndClearCookies("animals"); + AssertNoCookiesArePresent(); + } - [SetUp] - public void GoToSimplePageAndDeleteCookies() + [Test] + public void ShouldGetCookieByName() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - GotoValidDomainAndClearCookies("animals"); - AssertNoCookiesArePresent(); + return; } - [Test] - public void ShouldGetCookieByName() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string key = string.Format("key_{0}", new Random().Next()); + ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key); - string key = string.Format("key_{0}", new Random().Next()); - ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key); + Cookie cookie = driver.Manage().Cookies.GetCookieNamed(key); + Assert.That(cookie.Value, Is.EqualTo("set")); + } - Cookie cookie = driver.Manage().Cookies.GetCookieNamed(key); - Assert.That(cookie.Value, Is.EqualTo("set")); + [Test] + public void ShouldBeAbleToAddCookie() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldBeAbleToAddCookie() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string key = GenerateUniqueKey(); + string value = "foo"; + Cookie cookie = new Cookie(key, value); + AssertCookieIsNotPresentWithName(key); - string key = GenerateUniqueKey(); - string value = "foo"; - Cookie cookie = new Cookie(key, value); - AssertCookieIsNotPresentWithName(key); + driver.Manage().Cookies.AddCookie(cookie); - driver.Manage().Cookies.AddCookie(cookie); + AssertCookieHasValue(key, value); + Assert.That(driver.Manage().Cookies.AllCookies, Does.Contain(cookie), "Cookie was not added successfully"); + } - AssertCookieHasValue(key, value); - Assert.That(driver.Manage().Cookies.AllCookies, Does.Contain(cookie), "Cookie was not added successfully"); + [Test] + public void GetAllCookies() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void GetAllCookies() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string key1 = GenerateUniqueKey(); + string key2 = GenerateUniqueKey(); - string key1 = GenerateUniqueKey(); - string key2 = GenerateUniqueKey(); + AssertCookieIsNotPresentWithName(key1); + AssertCookieIsNotPresentWithName(key2); - AssertCookieIsNotPresentWithName(key1); - AssertCookieIsNotPresentWithName(key2); + ReadOnlyCollection cookies = driver.Manage().Cookies.AllCookies; + int count = cookies.Count; - ReadOnlyCollection cookies = driver.Manage().Cookies.AllCookies; - int count = cookies.Count; + Cookie one = new Cookie(key1, "value"); + Cookie two = new Cookie(key2, "value"); - Cookie one = new Cookie(key1, "value"); - Cookie two = new Cookie(key2, "value"); + driver.Manage().Cookies.AddCookie(one); + driver.Manage().Cookies.AddCookie(two); - driver.Manage().Cookies.AddCookie(one); - driver.Manage().Cookies.AddCookie(two); + driver.Url = simpleTestPage; + cookies = driver.Manage().Cookies.AllCookies; + Assert.That(cookies, Has.Count.EqualTo(count + 2)); - driver.Url = simpleTestPage; - cookies = driver.Manage().Cookies.AllCookies; - Assert.That(cookies, Has.Count.EqualTo(count + 2)); + Assert.That(cookies, Does.Contain(one)); + Assert.That(cookies, Does.Contain(two)); + } - Assert.That(cookies, Does.Contain(one)); - Assert.That(cookies, Does.Contain(two)); + [Test] + public void DeleteAllCookies() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void DeleteAllCookies() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = 'foo=set';"); + AssertSomeCookiesArePresent(); - ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = 'foo=set';"); - AssertSomeCookiesArePresent(); + driver.Manage().Cookies.DeleteAllCookies(); - driver.Manage().Cookies.DeleteAllCookies(); + AssertNoCookiesArePresent(); + } - AssertNoCookiesArePresent(); + [Test] + public void DeleteCookieWithName() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void DeleteCookieWithName() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string key1 = GenerateUniqueKey(); + string key2 = GenerateUniqueKey(); - string key1 = GenerateUniqueKey(); - string key2 = GenerateUniqueKey(); + ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key1); + ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key2); - ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key1); - ((IJavaScriptExecutor)driver).ExecuteScript("document.cookie = arguments[0] + '=set';", key2); + AssertCookieIsPresentWithName(key1); + AssertCookieIsPresentWithName(key2); - AssertCookieIsPresentWithName(key1); - AssertCookieIsPresentWithName(key2); + driver.Manage().Cookies.DeleteCookieNamed(key1); - driver.Manage().Cookies.DeleteCookieNamed(key1); + AssertCookieIsNotPresentWithName(key1); + AssertCookieIsPresentWithName(key2); + } - AssertCookieIsNotPresentWithName(key1); - AssertCookieIsPresentWithName(key2); + [Test] + public void ShouldNotDeleteCookiesWithASimilarName() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldNotDeleteCookiesWithASimilarName() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string cookieOneName = "fish"; + Cookie cookie1 = new Cookie(cookieOneName, "cod"); + Cookie cookie2 = new Cookie(cookieOneName + "x", "earth"); + IOptions options = driver.Manage(); + AssertCookieIsNotPresentWithName(cookie1.Name); - string cookieOneName = "fish"; - Cookie cookie1 = new Cookie(cookieOneName, "cod"); - Cookie cookie2 = new Cookie(cookieOneName + "x", "earth"); - IOptions options = driver.Manage(); - AssertCookieIsNotPresentWithName(cookie1.Name); + options.Cookies.AddCookie(cookie1); + options.Cookies.AddCookie(cookie2); - options.Cookies.AddCookie(cookie1); - options.Cookies.AddCookie(cookie2); + AssertCookieIsPresentWithName(cookie1.Name); - AssertCookieIsPresentWithName(cookie1.Name); + options.Cookies.DeleteCookieNamed(cookieOneName); - options.Cookies.DeleteCookieNamed(cookieOneName); + Assert.That(driver.Manage().Cookies.AllCookies, Does.Not.Contain(cookie1)); + Assert.That(driver.Manage().Cookies.AllCookies, Does.Contain(cookie2)); + } - Assert.That(driver.Manage().Cookies.AllCookies, Does.Not.Contain(cookie1)); - Assert.That(driver.Manage().Cookies.AllCookies, Does.Contain(cookie2)); + [Test] + public void AddCookiesWithDifferentPathsThatAreRelatedToOurs() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void AddCookiesWithDifferentPathsThatAreRelatedToOurs() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string basePath = EnvironmentManager.Instance.UrlBuilder.Path; - string basePath = EnvironmentManager.Instance.UrlBuilder.Path; + Cookie cookie1 = new Cookie("fish", "cod", "/" + basePath + "/animals"); + Cookie cookie2 = new Cookie("planet", "earth", "/" + basePath + "/"); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie1); + options.Cookies.AddCookie(cookie2); - Cookie cookie1 = new Cookie("fish", "cod", "/" + basePath + "/animals"); - Cookie cookie2 = new Cookie("planet", "earth", "/" + basePath + "/"); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); - options.Cookies.AddCookie(cookie2); + UrlBuilder builder = EnvironmentManager.Instance.UrlBuilder; + driver.Url = builder.WhereIs("animals"); - UrlBuilder builder = EnvironmentManager.Instance.UrlBuilder; - driver.Url = builder.WhereIs("animals"); + ReadOnlyCollection cookies = options.Cookies.AllCookies; + AssertCookieIsPresentWithName(cookie1.Name); + AssertCookieIsPresentWithName(cookie2.Name); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - AssertCookieIsPresentWithName(cookie1.Name); - AssertCookieIsPresentWithName(cookie2.Name); + driver.Url = builder.WhereIs("simpleTest.html"); + AssertCookieIsNotPresentWithName(cookie1.Name); + } - driver.Url = builder.WhereIs("simpleTest.html"); - AssertCookieIsNotPresentWithName(cookie1.Name); - } + [Test] + [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/1104")] + public void GetCookiesInAFrame() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); + Cookie cookie1 = new Cookie("fish", "cod", "/common/animals"); + driver.Manage().Cookies.AddCookie(cookie1); - [Test] - [IgnoreBrowser(Browser.Firefox, "https://github.com/mozilla/geckodriver/issues/1104")] - public void GetCookiesInAFrame() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); - Cookie cookie1 = new Cookie("fish", "cod", "/common/animals"); - driver.Manage().Cookies.AddCookie(cookie1); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("frameWithAnimals.html"); + AssertCookieIsNotPresentWithName(cookie1.Name); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("frameWithAnimals.html"); - AssertCookieIsNotPresentWithName(cookie1.Name); + driver.SwitchTo().Frame("iframe1"); + AssertCookieIsPresentWithName(cookie1.Name); + } - driver.SwitchTo().Frame("iframe1"); - AssertCookieIsPresentWithName(cookie1.Name); + [Test] + public void CannotGetCookiesWithPathDifferingOnlyInCase() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void CannotGetCookiesWithPathDifferingOnlyInCase() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string cookieName = "fish"; + driver.Manage().Cookies.AddCookie(new Cookie(cookieName, "cod", "/Common/animals")); - string cookieName = "fish"; - driver.Manage().Cookies.AddCookie(new Cookie(cookieName, "cod", "/Common/animals")); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); + Assert.That(driver.Manage().Cookies.GetCookieNamed(cookieName), Is.Null); + } - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); - Assert.That(driver.Manage().Cookies.GetCookieNamed(cookieName), Is.Null); + [Test] + public void ShouldNotGetCookieOnDifferentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldNotGetCookieOnDifferentDomain() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string cookieName = "fish"; + driver.Manage().Cookies.AddCookie(new Cookie(cookieName, "cod")); + AssertCookieIsPresentWithName(cookieName); - string cookieName = "fish"; - driver.Manage().Cookies.AddCookie(new Cookie(cookieName, "cod")); - AssertCookieIsPresentWithName(cookieName); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("simpleTest.html"); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("simpleTest.html"); + AssertCookieIsNotPresentWithName(cookieName); + } - AssertCookieIsNotPresentWithName(cookieName); + [Test] + public void ShouldBeAbleToAddToADomainWhichIsRelatedToTheCurrentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldBeAbleToAddToADomainWhichIsRelatedToTheCurrentDomain() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); + } - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } + AssertCookieIsNotPresentWithName("name"); - AssertCookieIsNotPresentWithName("name"); + Regex replaceRegex = new Regex(".*?\\."); + string shorter = replaceRegex.Replace(this.hostname, ".", 1); + Cookie cookie = new Cookie("name", "value", shorter, "/", GetTimeInTheFuture()); - Regex replaceRegex = new Regex(".*?\\."); - string shorter = replaceRegex.Replace(this.hostname, ".", 1); - Cookie cookie = new Cookie("name", "value", shorter, "/", GetTimeInTheFuture()); + driver.Manage().Cookies.AddCookie(cookie); - driver.Manage().Cookies.AddCookie(cookie); + AssertCookieIsPresentWithName("name"); + } - AssertCookieIsPresentWithName("name"); + [Test] + [IgnoreBrowser(Browser.IE, "IE does not want to set cookie")] + public void ShouldNotGetCookiesRelatedToCurrentDomainWithoutLeadingPeriod() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - [IgnoreBrowser(Browser.IE, "IE does not want to set cookie")] - public void ShouldNotGetCookiesRelatedToCurrentDomainWithoutLeadingPeriod() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string cookieName = "name"; + AssertCookieIsNotPresentWithName(cookieName); - string cookieName = "name"; - AssertCookieIsNotPresentWithName(cookieName); + Regex replaceRegex = new Regex(".*?\\."); + string subdomain = replaceRegex.Replace(this.hostname, "subdomain.", 1); + Cookie cookie = new Cookie(cookieName, "value", subdomain, "/", GetTimeInTheFuture()); - Regex replaceRegex = new Regex(".*?\\."); - string subdomain = replaceRegex.Replace(this.hostname, "subdomain.", 1); - Cookie cookie = new Cookie(cookieName, "value", subdomain, "/", GetTimeInTheFuture()); + string originalUrl = driver.Url; + string subdomainUrl = originalUrl.Replace(this.hostname, subdomain); + driver.Url = subdomainUrl; + driver.Manage().Cookies.AddCookie(cookie); - string originalUrl = driver.Url; - string subdomainUrl = originalUrl.Replace(this.hostname, subdomain); - driver.Url = subdomainUrl; - driver.Manage().Cookies.AddCookie(cookie); + driver.Url = originalUrl; + AssertCookieIsNotPresentWithName(cookieName); + } - driver.Url = originalUrl; - AssertCookieIsNotPresentWithName(cookieName); + [Test] + public void ShouldBeAbleToIncludeLeadingPeriodInDomainName() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldBeAbleToIncludeLeadingPeriodInDomainName() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); + } - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } + AssertCookieIsNotPresentWithName("name"); - AssertCookieIsNotPresentWithName("name"); + // Replace the first part of the name with a period + Regex replaceRegex = new Regex(".*?\\."); + string shorter = replaceRegex.Replace(this.hostname, ".", 1); + Cookie cookie = new Cookie("name", "value", shorter, "/", DateTime.Now.AddSeconds(100000)); - // Replace the first part of the name with a period - Regex replaceRegex = new Regex(".*?\\."); - string shorter = replaceRegex.Replace(this.hostname, ".", 1); - Cookie cookie = new Cookie("name", "value", shorter, "/", DateTime.Now.AddSeconds(100000)); + driver.Manage().Cookies.AddCookie(cookie); - driver.Manage().Cookies.AddCookie(cookie); + AssertCookieIsPresentWithName("name"); + } - AssertCookieIsPresentWithName("name"); + [Test] + public void ShouldBeAbleToSetDomainToTheCurrentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldBeAbleToSetDomainToTheCurrentDomain() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); + } - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } + Uri url = new Uri(driver.Url); + String host = url.Host + ":" + url.Port.ToString(); - Uri url = new Uri(driver.Url); - String host = url.Host + ":" + url.Port.ToString(); + Cookie cookie1 = new Cookie("fish", "cod", host, "/", null); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie1); - Cookie cookie1 = new Cookie("fish", "cod", host, "/", null); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); + driver.Url = javascriptPage; + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Contain(cookie1)); + } - driver.Url = javascriptPage; - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Contain(cookie1)); + [Test] + public void ShouldWalkThePathToDeleteACookie() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldWalkThePathToDeleteACookie() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string basePath = EnvironmentManager.Instance.UrlBuilder.Path; - string basePath = EnvironmentManager.Instance.UrlBuilder.Path; + Cookie cookie1 = new Cookie("fish", "cod"); + driver.Manage().Cookies.AddCookie(cookie1); + int count = driver.Manage().Cookies.AllCookies.Count; - Cookie cookie1 = new Cookie("fish", "cod"); - driver.Manage().Cookies.AddCookie(cookie1); - int count = driver.Manage().Cookies.AllCookies.Count; + driver.Url = childPage; + Cookie cookie2 = new Cookie("rodent", "hamster", "/" + basePath + "/child"); + driver.Manage().Cookies.AddCookie(cookie2); + count = driver.Manage().Cookies.AllCookies.Count; - driver.Url = childPage; - Cookie cookie2 = new Cookie("rodent", "hamster", "/" + basePath + "/child"); - driver.Manage().Cookies.AddCookie(cookie2); - count = driver.Manage().Cookies.AllCookies.Count; + driver.Url = grandchildPage; + Cookie cookie3 = new Cookie("dog", "dalmation", "/" + basePath + "/child/grandchild/"); + driver.Manage().Cookies.AddCookie(cookie3); + count = driver.Manage().Cookies.AllCookies.Count; - driver.Url = grandchildPage; - Cookie cookie3 = new Cookie("dog", "dalmation", "/" + basePath + "/child/grandchild/"); - driver.Manage().Cookies.AddCookie(cookie3); - count = driver.Manage().Cookies.AllCookies.Count; + driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("child/grandchild")); + driver.Manage().Cookies.DeleteCookieNamed("rodent"); + count = driver.Manage().Cookies.AllCookies.Count; - driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("child/grandchild")); - driver.Manage().Cookies.DeleteCookieNamed("rodent"); - count = driver.Manage().Cookies.AllCookies.Count; + Assert.That(driver.Manage().Cookies.GetCookieNamed("rodent"), Is.Null); - Assert.That(driver.Manage().Cookies.GetCookieNamed("rodent"), Is.Null); + ReadOnlyCollection cookies = driver.Manage().Cookies.AllCookies; + Assert.That(cookies, Has.Exactly(2).Items); + Assert.That(cookies, Does.Contain(cookie1)); + Assert.That(cookies, Does.Contain(cookie3)); - ReadOnlyCollection cookies = driver.Manage().Cookies.AllCookies; - Assert.That(cookies, Has.Exactly(2).Items); - Assert.That(cookies, Does.Contain(cookie1)); - Assert.That(cookies, Does.Contain(cookie3)); + driver.Manage().Cookies.DeleteAllCookies(); + driver.Url = grandchildPage; + AssertNoCookiesArePresent(); + } - driver.Manage().Cookies.DeleteAllCookies(); - driver.Url = grandchildPage; - AssertNoCookiesArePresent(); + [Test] + public void ShouldIgnoreThePortNumberOfTheHostWhenSettingTheCookie() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldIgnoreThePortNumberOfTheHostWhenSettingTheCookie() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } - - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } - - Uri uri = new Uri(driver.Url); - string host = string.Format("{0}:{1}", uri.Host, uri.Port); - string cookieName = "name"; - AssertCookieIsNotPresentWithName(cookieName); - Cookie cookie = new Cookie(cookieName, "value", host, "/", null); - driver.Manage().Cookies.AddCookie(cookie); - AssertCookieIsPresentWithName(cookieName); + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); } - [Test] - public void CookieEqualityAfterSetAndGet() + Uri uri = new Uri(driver.Url); + string host = string.Format("{0}:{1}", uri.Host, uri.Port); + string cookieName = "name"; + AssertCookieIsNotPresentWithName(cookieName); + Cookie cookie = new Cookie(cookieName, "value", host, "/", null); + driver.Manage().Cookies.AddCookie(cookie); + AssertCookieIsPresentWithName(cookieName); + } + + [Test] + public void CookieEqualityAfterSetAndGet() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - DateTime time = DateTime.Now.AddDays(1); - Cookie cookie1 = new Cookie("fish", "cod", null, "/common/animals", time); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); + DateTime time = DateTime.Now.AddDays(1); + Cookie cookie1 = new Cookie("fish", "cod", null, "/common/animals", time); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie1); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Cookie retrievedCookie = null; - foreach (Cookie tempCookie in cookies) + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Cookie retrievedCookie = null; + foreach (Cookie tempCookie in cookies) + { + if (cookie1.Equals(tempCookie)) { - if (cookie1.Equals(tempCookie)) - { - retrievedCookie = tempCookie; - break; - } + retrievedCookie = tempCookie; + break; } - - Assert.That(retrievedCookie, Is.Not.Null); - //Cookie.equals only compares name, domain and path - Assert.That(retrievedCookie, Is.EqualTo(cookie1)); } - [Test] - public void ShouldRetainCookieExpiry() + Assert.That(retrievedCookie, Is.Not.Null); + //Cookie.equals only compares name, domain and path + Assert.That(retrievedCookie, Is.EqualTo(cookie1)); + } + + [Test] + public void ShouldRetainCookieExpiry() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - // DateTime.Now contains milliseconds; the returned cookie expire date - // will not. So we need to truncate the milliseconds. - DateTime current = DateTime.Now; - DateTime expireDate = new DateTime(current.Year, current.Month, current.Day, current.Hour, current.Minute, current.Second, DateTimeKind.Local).AddDays(1); + // DateTime.Now contains milliseconds; the returned cookie expire date + // will not. So we need to truncate the milliseconds. + DateTime current = DateTime.Now; + DateTime expireDate = new DateTime(current.Year, current.Month, current.Day, current.Hour, current.Minute, current.Second, DateTimeKind.Local).AddDays(1); - Cookie addCookie = new Cookie("fish", "cod", "/common/animals", expireDate); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(addCookie); + Cookie addCookie = new Cookie("fish", "cod", "/common/animals", expireDate); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(addCookie); - Cookie retrieved = options.Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Not.Null); - Assert.That(retrieved.Expiry, Is.EqualTo(addCookie.Expiry), "Cookies are not equal"); - } + Cookie retrieved = options.Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Not.Null); + Assert.That(retrieved.Expiry, Is.EqualTo(addCookie.Expiry), "Cookies are not equal"); + } - [Test] - [Ignore("Unable to open secure url")] - [IgnoreBrowser(Browser.IE, "Browser does not handle untrusted SSL certificates.")] - public void CanHandleSecureCookie() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("animals"); + [Test] + [Ignore("Unable to open secure url")] + [IgnoreBrowser(Browser.IE, "Browser does not handle untrusted SSL certificates.")] + public void CanHandleSecureCookie() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("animals"); - Cookie addedCookie = new ReturnedCookie("fish", "cod", null, "/common/animals", null, true, false, null); - driver.Manage().Cookies.AddCookie(addedCookie); + Cookie addedCookie = new ReturnedCookie("fish", "cod", null, "/common/animals", null, true, false, null); + driver.Manage().Cookies.AddCookie(addedCookie); - driver.Navigate().Refresh(); + driver.Navigate().Refresh(); - Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Not.Null); - } + Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Not.Null); + } - [Test] - [Ignore("Unable to open secure url")] - [IgnoreBrowser(Browser.IE, "Browser does not handle untrusted SSL certificates.")] - public void ShouldRetainCookieSecure() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("animals"); + [Test] + [Ignore("Unable to open secure url")] + [IgnoreBrowser(Browser.IE, "Browser does not handle untrusted SSL certificates.")] + public void ShouldRetainCookieSecure() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("animals"); - ReturnedCookie addedCookie = new ReturnedCookie("fish", "cod", string.Empty, "/common/animals", null, true, false, null); + ReturnedCookie addedCookie = new ReturnedCookie("fish", "cod", string.Empty, "/common/animals", null, true, false, null); - driver.Manage().Cookies.AddCookie(addedCookie); + driver.Manage().Cookies.AddCookie(addedCookie); - driver.Navigate().Refresh(); + driver.Navigate().Refresh(); - Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Not.Null); - Assert.That(retrieved.Secure, "Secure attribute not set to true"); - } + Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Not.Null); + Assert.That(retrieved.Secure, "Secure attribute not set to true"); + } - [Test] - public void CanHandleHttpOnlyCookie() - { - StringBuilder url = new StringBuilder(EnvironmentManager.Instance.UrlBuilder.WhereIs("cookie")); - url.Append("?action=add"); - url.Append("&name=").Append("fish"); - url.Append("&value=").Append("cod"); - url.Append("&path=").Append("/common/animals"); - url.Append("&httpOnly=").Append("true"); + [Test] + public void CanHandleHttpOnlyCookie() + { + StringBuilder url = new StringBuilder(EnvironmentManager.Instance.UrlBuilder.WhereIs("cookie")); + url.Append("?action=add"); + url.Append("&name=").Append("fish"); + url.Append("&value=").Append("cod"); + url.Append("&path=").Append("/common/animals"); + url.Append("&httpOnly=").Append("true"); + + driver.Url = url.ToString(); + + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); + Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Not.Null); + } - driver.Url = url.ToString(); + [Test] + public void ShouldRetainHttpOnlyFlag() + { + StringBuilder url = new StringBuilder(EnvironmentManager.Instance.UrlBuilder.WhereElseIs("cookie")); + url.Append("?action=add"); + url.Append("&name=").Append("fish"); + url.Append("&value=").Append("cod"); + url.Append("&path=").Append("/common/animals"); + url.Append("&httpOnly=").Append("true"); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); - Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Not.Null); - } + driver.Url = url.ToString(); - [Test] - public void ShouldRetainHttpOnlyFlag() - { - StringBuilder url = new StringBuilder(EnvironmentManager.Instance.UrlBuilder.WhereElseIs("cookie")); - url.Append("?action=add"); - url.Append("&name=").Append("fish"); - url.Append("&value=").Append("cod"); - url.Append("&path=").Append("/common/animals"); - url.Append("&httpOnly=").Append("true"); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("animals"); - driver.Url = url.ToString(); + Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Not.Null); + Assert.That(retrieved.IsHttpOnly, "HttpOnly attribute not set to true"); + } - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("animals"); + [Test] + public void SettingACookieThatExpiredInThePast() + { + DateTime expires = DateTime.Now.AddSeconds(-1000); + Cookie cookie = new Cookie("expired", "yes", "/common/animals", expires); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie); - Cookie retrieved = driver.Manage().Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Not.Null); - Assert.That(retrieved.IsHttpOnly, "HttpOnly attribute not set to true"); - } + cookie = options.Cookies.GetCookieNamed("expired"); + Assert.That(cookie, Is.Null, "Cookie expired before it was set, so nothing should be returned: " + cookie); + } - [Test] - public void SettingACookieThatExpiredInThePast() + [Test] + public void CanSetCookieWithoutOptionalFieldsSet() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - DateTime expires = DateTime.Now.AddSeconds(-1000); - Cookie cookie = new Cookie("expired", "yes", "/common/animals", expires); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie); - - cookie = options.Cookies.GetCookieNamed("expired"); - Assert.That(cookie, Is.Null, "Cookie expired before it was set, so nothing should be returned: " + cookie); + return; } - [Test] - public void CanSetCookieWithoutOptionalFieldsSet() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + string key = GenerateUniqueKey(); + string value = "foo"; + Cookie cookie = new Cookie(key, value); + AssertCookieIsNotPresentWithName(key); - string key = GenerateUniqueKey(); - string value = "foo"; - Cookie cookie = new Cookie(key, value); - AssertCookieIsNotPresentWithName(key); + driver.Manage().Cookies.AddCookie(cookie); - driver.Manage().Cookies.AddCookie(cookie); - - AssertCookieHasValue(key, value); - } + AssertCookieHasValue(key, value); + } - [Test] - public void DeleteNotExistedCookie() - { - String key = GenerateUniqueKey(); - AssertCookieIsNotPresentWithName(key); + [Test] + public void DeleteNotExistedCookie() + { + String key = GenerateUniqueKey(); + AssertCookieIsNotPresentWithName(key); - driver.Manage().Cookies.DeleteCookieNamed(key); - } + driver.Manage().Cookies.DeleteCookieNamed(key); + } - [Test] - [IgnoreBrowser(Browser.IE, "IE does not want to set cookie")] - public void DeleteAllCookiesDifferentUrls() + [Test] + [IgnoreBrowser(Browser.IE, "IE does not want to set cookie")] + public void DeleteAllCookiesDifferentUrls() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - Cookie cookie1 = new Cookie("fish1", "cod", EnvironmentManager.Instance.UrlBuilder.HostName, null, null); - Cookie cookie2 = new Cookie("fish2", "tune", EnvironmentManager.Instance.UrlBuilder.AlternateHostName, null, null); + Cookie cookie1 = new Cookie("fish1", "cod", EnvironmentManager.Instance.UrlBuilder.HostName, null, null); + Cookie cookie2 = new Cookie("fish2", "tune", EnvironmentManager.Instance.UrlBuilder.AlternateHostName, null, null); - string url1 = EnvironmentManager.Instance.UrlBuilder.WhereIs(""); - string url2 = EnvironmentManager.Instance.UrlBuilder.WhereElseIs(""); + string url1 = EnvironmentManager.Instance.UrlBuilder.WhereIs(""); + string url2 = EnvironmentManager.Instance.UrlBuilder.WhereElseIs(""); - IOptions options = driver.Manage(); + IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); - AssertCookieIsPresentWithName(cookie1.Name); + options.Cookies.AddCookie(cookie1); + AssertCookieIsPresentWithName(cookie1.Name); - driver.Url = url2; - options.Cookies.AddCookie(cookie2); - AssertCookieIsNotPresentWithName(cookie1.Name); - AssertCookieIsPresentWithName(cookie2.Name); + driver.Url = url2; + options.Cookies.AddCookie(cookie2); + AssertCookieIsNotPresentWithName(cookie1.Name); + AssertCookieIsPresentWithName(cookie2.Name); - driver.Url = url1; - AssertCookieIsPresentWithName(cookie1.Name); - AssertCookieIsNotPresentWithName(cookie2.Name); + driver.Url = url1; + AssertCookieIsPresentWithName(cookie1.Name); + AssertCookieIsNotPresentWithName(cookie2.Name); - options.Cookies.DeleteAllCookies(); - AssertCookieIsNotPresentWithName(cookie1.Name); + options.Cookies.DeleteAllCookies(); + AssertCookieIsNotPresentWithName(cookie1.Name); - driver.Url = url2; - AssertCookieIsPresentWithName(cookie2.Name); - } + driver.Url = url2; + AssertCookieIsPresentWithName(cookie2.Name); + } - [Test] - [TestCase(null)] - [TestCase("")] - [TestCase(" ")] - public void ShouldThrowWhenGetInvalidCookieByName(string cookieName) - { - var getCookieAction = () => driver.Manage().Cookies.GetCookieNamed(cookieName); + [Test] + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ShouldThrowWhenGetInvalidCookieByName(string cookieName) + { + var getCookieAction = () => driver.Manage().Cookies.GetCookieNamed(cookieName); - Assert.That(getCookieAction, Throws.ArgumentException); - } + Assert.That(getCookieAction, Throws.ArgumentException); + } - [Test] - [TestCase(null)] - [TestCase("")] - [TestCase(" ")] - public void ShouldThrowWhenDeleteInvalidCookieByName(string cookieName) - { - var deleteCookieAction = () => driver.Manage().Cookies.DeleteCookieNamed(cookieName); + [Test] + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ShouldThrowWhenDeleteInvalidCookieByName(string cookieName) + { + var deleteCookieAction = () => driver.Manage().Cookies.DeleteCookieNamed(cookieName); - Assert.That(deleteCookieAction, Throws.ArgumentException); - } + Assert.That(deleteCookieAction, Throws.ArgumentException); + } - //------------------------------------------------------------------ - // Tests below here are not included in the Java test suite - //------------------------------------------------------------------ - [Test] - public void CanSetCookiesOnADifferentPathOfTheSameHost() + //------------------------------------------------------------------ + // Tests below here are not included in the Java test suite + //------------------------------------------------------------------ + [Test] + public void CanSetCookiesOnADifferentPathOfTheSameHost() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - string basePath = EnvironmentManager.Instance.UrlBuilder.Path; - Cookie cookie1 = new Cookie("fish", "cod", "/" + basePath + "/animals"); - Cookie cookie2 = new Cookie("planet", "earth", "/" + basePath + "/galaxy"); + string basePath = EnvironmentManager.Instance.UrlBuilder.Path; + Cookie cookie1 = new Cookie("fish", "cod", "/" + basePath + "/animals"); + Cookie cookie2 = new Cookie("planet", "earth", "/" + basePath + "/galaxy"); - IOptions options = driver.Manage(); - ReadOnlyCollection count = options.Cookies.AllCookies; + IOptions options = driver.Manage(); + ReadOnlyCollection count = options.Cookies.AllCookies; - options.Cookies.AddCookie(cookie1); - options.Cookies.AddCookie(cookie2); + options.Cookies.AddCookie(cookie1); + options.Cookies.AddCookie(cookie2); - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); - driver.Url = url; - ReadOnlyCollection cookies = options.Cookies.AllCookies; + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("animals"); + driver.Url = url; + ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Contain(cookie1)); - Assert.That(cookies, Does.Not.Contain(cookie2)); + Assert.That(cookies, Does.Contain(cookie1)); + Assert.That(cookies, Does.Not.Contain(cookie2)); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("galaxy"); - cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Not.Contain(cookie1)); - Assert.That(cookies, Does.Contain(cookie2)); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("galaxy"); + cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Not.Contain(cookie1)); + Assert.That(cookies, Does.Contain(cookie2)); + } - [Test] - public void ShouldNotBeAbleToSetDomainToSomethingThatIsUnrelatedToTheCurrentDomain() + [Test] + public void ShouldNotBeAbleToSetDomainToSomethingThatIsUnrelatedToTheCurrentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - Cookie cookie1 = new Cookie("fish", "cod"); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); + Cookie cookie1 = new Cookie("fish", "cod"); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie1); - string url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("simpleTest.html"); - driver.Url = url; + string url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("simpleTest.html"); + driver.Url = url; - Assert.That(options.Cookies.GetCookieNamed("fish"), Is.Null); - } + Assert.That(options.Cookies.GetCookieNamed("fish"), Is.Null); + } - [Test] - public void GetCookieDoesNotRetrieveBeyondCurrentDomain() + [Test] + public void GetCookieDoesNotRetrieveBeyondCurrentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + return; + } - Cookie cookie1 = new Cookie("fish", "cod"); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(cookie1); + Cookie cookie1 = new Cookie("fish", "cod"); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(cookie1); - String url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs(""); - driver.Url = url; + String url = EnvironmentManager.Instance.UrlBuilder.WhereElseIs(""); + driver.Url = url; - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Not.Contain(cookie1)); - } + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Not.Contain(cookie1)); + } - [Test] - public void ShouldAddCookieToCurrentDomainAndPath() + [Test] + public void ShouldAddCookieToCurrentDomainAndPath() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } - - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } - - driver.Url = macbethPage; - IOptions options = driver.Manage(); - Cookie cookie = new Cookie("Homer", "Simpson", this.hostname, "/" + EnvironmentManager.Instance.UrlBuilder.Path, null); - options.Cookies.AddCookie(cookie); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Contain(cookie), "Valid cookie was not returned"); + return; } - [Test] - public void ShouldNotShowCookieAddedToDifferentDomain() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - Assert.Ignore("Not on a standard domain for cookies (localhost doesn't count)."); - } + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); + } - driver.Url = macbethPage; - IOptions options = driver.Manage(); - Cookie cookie = new Cookie("Bart", "Simpson", EnvironmentManager.Instance.UrlBuilder.HostName + ".com", EnvironmentManager.Instance.UrlBuilder.Path, null); - Assert.That( - () => options.Cookies.AddCookie(cookie), - Throws.InstanceOf().Or.InstanceOf()); + driver.Url = macbethPage; + IOptions options = driver.Manage(); + Cookie cookie = new Cookie("Homer", "Simpson", this.hostname, "/" + EnvironmentManager.Instance.UrlBuilder.Path, null); + options.Cookies.AddCookie(cookie); + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Contain(cookie), "Valid cookie was not returned"); + } - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Not.Contain(cookie), "Invalid cookie was returned"); + [Test] + public void ShouldNotShowCookieAddedToDifferentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + Assert.Ignore("Not on a standard domain for cookies (localhost doesn't count)."); } - [Test] - public void ShouldNotShowCookieAddedToDifferentPath() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + driver.Url = macbethPage; + IOptions options = driver.Manage(); + Cookie cookie = new Cookie("Bart", "Simpson", EnvironmentManager.Instance.UrlBuilder.HostName + ".com", EnvironmentManager.Instance.UrlBuilder.Path, null); + Assert.That( + () => options.Cookies.AddCookie(cookie), + Throws.InstanceOf().Or.InstanceOf()); - // Cookies cannot be set on domain names with less than 2 dots, so - // localhost is out. If we are in that boat, bail the test. - string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; - string[] hostNameParts = hostName.Split(new char[] { '.' }); - if (hostNameParts.Length < 3) - { - Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); - } + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Not.Contain(cookie), "Invalid cookie was returned"); + } - driver.Url = macbethPage; - IOptions options = driver.Manage(); - Cookie cookie = new Cookie("Lisa", "Simpson", EnvironmentManager.Instance.UrlBuilder.HostName, "/" + EnvironmentManager.Instance.UrlBuilder.Path + "IDoNotExist", null); - options.Cookies.AddCookie(cookie); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Not.Contain(cookie), "Invalid cookie was returned"); + [Test] + public void ShouldNotShowCookieAddedToDifferentPath() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldThrowExceptionWhenAddingCookieToCookieAverseDocument() + // Cookies cannot be set on domain names with less than 2 dots, so + // localhost is out. If we are in that boat, bail the test. + string hostName = EnvironmentManager.Instance.UrlBuilder.HostName; + string[] hostNameParts = hostName.Split(new char[] { '.' }); + if (hostNameParts.Length < 3) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + Assert.Ignore("Skipping test: Cookies can only be set on fully-qualified domain names."); + } - // URLs using a non-network scheme (like "about:" or "data:") are - // averse to cookies, and should throw an InvalidCookieDomainException. - driver.Url = "about:blank"; + driver.Url = macbethPage; + IOptions options = driver.Manage(); + Cookie cookie = new Cookie("Lisa", "Simpson", EnvironmentManager.Instance.UrlBuilder.HostName, "/" + EnvironmentManager.Instance.UrlBuilder.Path + "IDoNotExist", null); + options.Cookies.AddCookie(cookie); + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Not.Contain(cookie), "Invalid cookie was returned"); + } - IOptions options = driver.Manage(); - Cookie cookie = new Cookie("question", "dunno"); - Assert.That( - () => options.Cookies.AddCookie(cookie), - Throws.InstanceOf().Or.InstanceOf()); + [Test] + public void ShouldThrowExceptionWhenAddingCookieToCookieAverseDocument() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldReturnNullBecauseCookieRetainsExpiry() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + // URLs using a non-network scheme (like "about:" or "data:") are + // averse to cookies, and should throw an InvalidCookieDomainException. + driver.Url = "about:blank"; - Cookie addCookie = new Cookie("fish", "cod", "/common/animals", DateTime.Now.AddHours(-1)); - IOptions options = driver.Manage(); - options.Cookies.AddCookie(addCookie); + IOptions options = driver.Manage(); + Cookie cookie = new Cookie("question", "dunno"); + Assert.That( + () => options.Cookies.AddCookie(cookie), + Throws.InstanceOf().Or.InstanceOf()); + } - Cookie retrieved = options.Cookies.GetCookieNamed("fish"); - Assert.That(retrieved, Is.Null); + [Test] + public void ShouldReturnNullBecauseCookieRetainsExpiry() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; } - [Test] - public void ShouldAddCookieToCurrentDomain() - { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } + Cookie addCookie = new Cookie("fish", "cod", "/common/animals", DateTime.Now.AddHours(-1)); + IOptions options = driver.Manage(); + options.Cookies.AddCookie(addCookie); - driver.Url = macbethPage; - IOptions options = driver.Manage(); - Cookie cookie = new Cookie("Marge", "Simpson", "/"); - options.Cookies.AddCookie(cookie); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - Assert.That(cookies, Does.Contain(cookie), "Valid cookie was not returned"); - } + Cookie retrieved = options.Cookies.GetCookieNamed("fish"); + Assert.That(retrieved, Is.Null); + } - [Test] - public void ShouldDeleteCookie() + [Test] + public void ShouldAddCookieToCurrentDomain() + { + if (!CheckIsOnValidHostNameForCookieTests()) { - if (!CheckIsOnValidHostNameForCookieTests()) - { - return; - } - - driver.Url = macbethPage; - IOptions options = driver.Manage(); - Cookie cookieToDelete = new Cookie("answer", "42"); - Cookie cookieToKeep = new Cookie("canIHaz", "Cheeseburguer"); - options.Cookies.AddCookie(cookieToDelete); - options.Cookies.AddCookie(cookieToKeep); - ReadOnlyCollection cookies = options.Cookies.AllCookies; - options.Cookies.DeleteCookie(cookieToDelete); - ReadOnlyCollection cookies2 = options.Cookies.AllCookies; - Assert.That(cookies2, Does.Not.Contain(cookieToDelete), "Cookie was not deleted successfully"); - Assert.That(cookies2, Does.Contain(cookieToKeep), "Valid cookie was not returned"); + return; } - ////////////////////////////////////////////// - // Support functions - ////////////////////////////////////////////// - - private void GotoValidDomainAndClearCookies(string page) - { - this.hostname = null; - String hostname = EnvironmentManager.Instance.UrlBuilder.HostName; - if (IsValidHostNameForCookieTests(hostname)) - { - this.isOnAlternativeHostName = false; - this.hostname = hostname; - } + driver.Url = macbethPage; + IOptions options = driver.Manage(); + Cookie cookie = new Cookie("Marge", "Simpson", "/"); + options.Cookies.AddCookie(cookie); + ReadOnlyCollection cookies = options.Cookies.AllCookies; + Assert.That(cookies, Does.Contain(cookie), "Valid cookie was not returned"); + } - hostname = EnvironmentManager.Instance.UrlBuilder.AlternateHostName; - if (this.hostname == null && IsValidHostNameForCookieTests(hostname)) - { - this.isOnAlternativeHostName = true; - this.hostname = hostname; - } + [Test] + public void ShouldDeleteCookie() + { + if (!CheckIsOnValidHostNameForCookieTests()) + { + return; + } + + driver.Url = macbethPage; + IOptions options = driver.Manage(); + Cookie cookieToDelete = new Cookie("answer", "42"); + Cookie cookieToKeep = new Cookie("canIHaz", "Cheeseburguer"); + options.Cookies.AddCookie(cookieToDelete); + options.Cookies.AddCookie(cookieToKeep); + ReadOnlyCollection cookies = options.Cookies.AllCookies; + options.Cookies.DeleteCookie(cookieToDelete); + ReadOnlyCollection cookies2 = options.Cookies.AllCookies; + Assert.That(cookies2, Does.Not.Contain(cookieToDelete), "Cookie was not deleted successfully"); + Assert.That(cookies2, Does.Contain(cookieToKeep), "Valid cookie was not returned"); + } - GoToPage(page); + ////////////////////////////////////////////// + // Support functions + ////////////////////////////////////////////// - driver.Manage().Cookies.DeleteAllCookies(); - if (driver.Manage().Cookies.AllCookies.Count != 0) - { - // If cookies are still present, restart the driver and try again. - // This may mask some errors, where DeleteAllCookies doesn't fully - // delete all it should, but that's a tradeoff we need to be willing - // to make. - driver = EnvironmentManager.Instance.CreateFreshDriver(); - GoToPage(page); - } + private void GotoValidDomainAndClearCookies(string page) + { + this.hostname = null; + String hostname = EnvironmentManager.Instance.UrlBuilder.HostName; + if (IsValidHostNameForCookieTests(hostname)) + { + this.isOnAlternativeHostName = false; + this.hostname = hostname; } - private bool CheckIsOnValidHostNameForCookieTests() + hostname = EnvironmentManager.Instance.UrlBuilder.AlternateHostName; + if (this.hostname == null && IsValidHostNameForCookieTests(hostname)) { - bool correct = this.hostname != null && IsValidHostNameForCookieTests(this.hostname); - if (!correct) - { - System.Console.WriteLine("Skipping test: unable to find domain name to use"); - } - - return correct; + this.isOnAlternativeHostName = true; + this.hostname = hostname; } - private void GoToPage(String pageName) + GoToPage(page); + + driver.Manage().Cookies.DeleteAllCookies(); + if (driver.Manage().Cookies.AllCookies.Count != 0) { - driver.Url = this.isOnAlternativeHostName ? EnvironmentManager.Instance.UrlBuilder.WhereElseIs(pageName) : EnvironmentManager.Instance.UrlBuilder.WhereIs(pageName); + // If cookies are still present, restart the driver and try again. + // This may mask some errors, where DeleteAllCookies doesn't fully + // delete all it should, but that's a tradeoff we need to be willing + // to make. + driver = EnvironmentManager.Instance.CreateFreshDriver(); + GoToPage(page); } + } - private void GoToOtherPage(String pageName) + private bool CheckIsOnValidHostNameForCookieTests() + { + bool correct = this.hostname != null && IsValidHostNameForCookieTests(this.hostname); + if (!correct) { - driver.Url = this.isOnAlternativeHostName ? EnvironmentManager.Instance.UrlBuilder.WhereIs(pageName) : EnvironmentManager.Instance.UrlBuilder.WhereElseIs(pageName); + System.Console.WriteLine("Skipping test: unable to find domain name to use"); } - private bool IsValidHostNameForCookieTests(string hostname) - { - // TODO(JimEvan): Some coverage is better than none, so we - // need to ignore the fact that localhost cookies are problematic. - // Reenable this when we have a better solution per DanielWagnerHall. - // ChromeDriver2 has trouble with localhost. IE and Firefox don't. - // return !IsIpv4Address(hostname) && "localhost" != hostname; - bool isLocalHostOkay = !("localhost" == hostname && !TestUtilities.IsInternetExplorer(driver)); + return correct; + } - return !IsIpv4Address(hostname) && isLocalHostOkay; - } + private void GoToPage(String pageName) + { + driver.Url = this.isOnAlternativeHostName ? EnvironmentManager.Instance.UrlBuilder.WhereElseIs(pageName) : EnvironmentManager.Instance.UrlBuilder.WhereIs(pageName); + } - private static bool IsIpv4Address(string addrString) - { - return Regex.IsMatch(addrString, "\\d{1,3}(?:\\.\\d{1,3}){3}"); - } + private void GoToOtherPage(String pageName) + { + driver.Url = this.isOnAlternativeHostName ? EnvironmentManager.Instance.UrlBuilder.WhereIs(pageName) : EnvironmentManager.Instance.UrlBuilder.WhereElseIs(pageName); + } + + private bool IsValidHostNameForCookieTests(string hostname) + { + // TODO(JimEvan): Some coverage is better than none, so we + // need to ignore the fact that localhost cookies are problematic. + // Reenable this when we have a better solution per DanielWagnerHall. + // ChromeDriver2 has trouble with localhost. IE and Firefox don't. + // return !IsIpv4Address(hostname) && "localhost" != hostname; + bool isLocalHostOkay = !("localhost" == hostname && !TestUtilities.IsInternetExplorer(driver)); + + return !IsIpv4Address(hostname) && isLocalHostOkay; + } + + private static bool IsIpv4Address(string addrString) + { + return Regex.IsMatch(addrString, "\\d{1,3}(?:\\.\\d{1,3}){3}"); + } + + private string GenerateUniqueKey() + { + return string.Format("key_{0}", random.Next()); + } - private string GenerateUniqueKey() + private string GetDocumentCookieOrNull() + { + IJavaScriptExecutor jsDriver = driver as IJavaScriptExecutor; + if (jsDriver == null) { - return string.Format("key_{0}", random.Next()); + return null; } - - private string GetDocumentCookieOrNull() + try { - IJavaScriptExecutor jsDriver = driver as IJavaScriptExecutor; - if (jsDriver == null) - { - return null; - } - try - { - return (string)jsDriver.ExecuteScript("return document.cookie"); - } - catch (InvalidOperationException) - { - return null; - } + return (string)jsDriver.ExecuteScript("return document.cookie"); } - - private void AssertNoCookiesArePresent() + catch (InvalidOperationException) { - Assert.That(driver.Manage().Cookies.AllCookies, Is.Empty, "Cookies were not empty"); - string documentCookie = GetDocumentCookieOrNull(); - if (documentCookie != null) - { - Assert.That(documentCookie, Is.Empty, "Cookies were not empty"); - } + return null; } + } - private void AssertSomeCookiesArePresent() + private void AssertNoCookiesArePresent() + { + Assert.That(driver.Manage().Cookies.AllCookies, Is.Empty, "Cookies were not empty"); + string documentCookie = GetDocumentCookieOrNull(); + if (documentCookie != null) { - Assert.That(driver.Manage().Cookies.AllCookies, Is.Not.Empty, "Cookies were empty"); - String documentCookie = GetDocumentCookieOrNull(); - if (documentCookie != null) - { - Assert.That(documentCookie, Is.Not.Empty, "Cookies were empty"); - } + Assert.That(documentCookie, Is.Empty, "Cookies were not empty"); } + } - private void AssertCookieIsNotPresentWithName(string key) + private void AssertSomeCookiesArePresent() + { + Assert.That(driver.Manage().Cookies.AllCookies, Is.Not.Empty, "Cookies were empty"); + String documentCookie = GetDocumentCookieOrNull(); + if (documentCookie != null) { - Assert.That(driver.Manage().Cookies.GetCookieNamed(key), Is.Null, "Cookie was present with name " + key); - string documentCookie = GetDocumentCookieOrNull(); - if (documentCookie != null) - { - Assert.That(documentCookie, Does.Not.Contain(key + "=")); - } + Assert.That(documentCookie, Is.Not.Empty, "Cookies were empty"); } + } - private void AssertCookieIsPresentWithName(string key) + private void AssertCookieIsNotPresentWithName(string key) + { + Assert.That(driver.Manage().Cookies.GetCookieNamed(key), Is.Null, "Cookie was present with name " + key); + string documentCookie = GetDocumentCookieOrNull(); + if (documentCookie != null) { - Assert.That(driver.Manage().Cookies.GetCookieNamed(key), Is.Not.Null, "Cookie was present with name " + key); - string documentCookie = GetDocumentCookieOrNull(); - if (documentCookie != null) - { - Assert.That(documentCookie, Does.Contain(key + "=")); - } + Assert.That(documentCookie, Does.Not.Contain(key + "=")); } + } - private void AssertCookieHasValue(string key, string value) + private void AssertCookieIsPresentWithName(string key) + { + Assert.That(driver.Manage().Cookies.GetCookieNamed(key), Is.Not.Null, "Cookie was present with name " + key); + string documentCookie = GetDocumentCookieOrNull(); + if (documentCookie != null) { - Assert.That(driver.Manage().Cookies.GetCookieNamed(key).Value, Is.EqualTo(value), "Cookie had wrong value"); - string documentCookie = GetDocumentCookieOrNull(); - if (documentCookie != null) - { - Assert.That(documentCookie, Does.Contain(key + "=" + value)); - } + Assert.That(documentCookie, Does.Contain(key + "=")); } + } - private DateTime GetTimeInTheFuture() + private void AssertCookieHasValue(string key, string value) + { + Assert.That(driver.Manage().Cookies.GetCookieNamed(key).Value, Is.EqualTo(value), "Cookie had wrong value"); + string documentCookie = GetDocumentCookieOrNull(); + if (documentCookie != null) { - return DateTime.Now.Add(TimeSpan.FromMilliseconds(100000)); + Assert.That(documentCookie, Does.Contain(key + "=" + value)); } } + + private DateTime GetTimeInTheFuture() + { + return DateTime.Now.Add(TimeSpan.FromMilliseconds(100000)); + } } diff --git a/dotnet/test/common/CookieTest.cs b/dotnet/test/common/CookieTest.cs index de33df64a6b4d..73bccb19b1fa3 100644 --- a/dotnet/test/common/CookieTest.cs +++ b/dotnet/test/common/CookieTest.cs @@ -21,94 +21,93 @@ using OpenQA.Selenium.Internal; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class CookieTest { - [TestFixture] - public class CookieTest + [Test] + public void CanCreateAWellFormedCookie() { - [Test] - public void CanCreateAWellFormedCookie() - { - new ReturnedCookie("Fish", "cod", "", "", DateTime.Now, false, false); - } + new ReturnedCookie("Fish", "cod", "", "", DateTime.Now, false, false); + } - [Test] - public void ShouldThrowAnExceptionWhenSemiColonExistsInTheCookieAttribute() - { - Assert.That(() => new ReturnedCookie("hi;hi", "value", null, null, DateTime.Now, false, false), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowAnExceptionWhenSemiColonExistsInTheCookieAttribute() + { + Assert.That(() => new ReturnedCookie("hi;hi", "value", null, null, DateTime.Now, false, false), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowAnExceptionWhenNameAndValueAreEmpty() - { - Assert.That(() => new ReturnedCookie("", "", null, null, DateTime.Now, false, false), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowAnExceptionWhenNameAndValueAreEmpty() + { + Assert.That(() => new ReturnedCookie("", "", null, null, DateTime.Now, false, false), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowAnExceptionWhenTheNameIsNull() - { - Assert.That(() => new ReturnedCookie(null, "value", null, null, DateTime.Now, false, false), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowAnExceptionWhenTheNameIsNull() + { + Assert.That(() => new ReturnedCookie(null, "value", null, null, DateTime.Now, false, false), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowAnExceptionWhenSameSiteIsWrong() - { - Assert.That(() => new ReturnedCookie("name", "value", "", "/", DateTime.Now, true, true, "Wrong"), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowAnExceptionWhenSameSiteIsWrong() + { + Assert.That(() => new ReturnedCookie("name", "value", "", "/", DateTime.Now, true, true, "Wrong"), Throws.InstanceOf()); + } - [Test] - public void CookiesShouldAllowOptionalParametersToBeSet() - { - DateTime expiry = DateTime.Now; - Cookie cookie = new Cookie("name", "value", "test.com", "/", expiry, true, true, "None"); - Assert.That(cookie.Domain, Is.EqualTo("test.com")); - Assert.That(cookie.Path, Is.EqualTo("/")); - Assert.That(cookie.IsHttpOnly, Is.True); - Assert.That(cookie.Secure, Is.True); - Assert.That(cookie.SameSite, Is.EqualTo("None")); - } + [Test] + public void CookiesShouldAllowOptionalParametersToBeSet() + { + DateTime expiry = DateTime.Now; + Cookie cookie = new Cookie("name", "value", "test.com", "/", expiry, true, true, "None"); + Assert.That(cookie.Domain, Is.EqualTo("test.com")); + Assert.That(cookie.Path, Is.EqualTo("/")); + Assert.That(cookie.IsHttpOnly, Is.True); + Assert.That(cookie.Secure, Is.True); + Assert.That(cookie.SameSite, Is.EqualTo("None")); + } - [Test] - public void CookiesShouldAllowSecureToBeSet() - { - Cookie cookie = new ReturnedCookie("name", "value", "", "/", DateTime.Now, true, false); - Assert.That(cookie.Secure, Is.True); - } + [Test] + public void CookiesShouldAllowSecureToBeSet() + { + Cookie cookie = new ReturnedCookie("name", "value", "", "/", DateTime.Now, true, false); + Assert.That(cookie.Secure, Is.True); + } - [Test] - public void SecureDefaultsToFalse() - { - Cookie cookie = new Cookie("name", "value"); - Assert.That(cookie.Secure, Is.False); - } + [Test] + public void SecureDefaultsToFalse() + { + Cookie cookie = new Cookie("name", "value"); + Assert.That(cookie.Secure, Is.False); + } - [Test] - public void CookiesShouldAllowHttpOnlyToBeSet() - { - Cookie cookie = new ReturnedCookie("name", "value", "", "/", DateTime.Now, false, true); - Assert.That(cookie.IsHttpOnly, Is.True); - } + [Test] + public void CookiesShouldAllowHttpOnlyToBeSet() + { + Cookie cookie = new ReturnedCookie("name", "value", "", "/", DateTime.Now, false, true); + Assert.That(cookie.IsHttpOnly, Is.True); + } - [Test] - public void HttpOnlyDefaultsToFalse() - { - Cookie cookie = new Cookie("name", "value"); - Assert.That(cookie.IsHttpOnly, Is.False); - } + [Test] + public void HttpOnlyDefaultsToFalse() + { + Cookie cookie = new Cookie("name", "value"); + Assert.That(cookie.IsHttpOnly, Is.False); + } - //------------------------------------------------------------------ - // Tests below here are not included in the Java test suite - //------------------------------------------------------------------ - [Test] - public void ShouldThrowAnExceptionWhenTheValueIsNull() - { - Assert.That(() => new ReturnedCookie("name", null, null, null, DateTime.Now, false, false), Throws.InstanceOf()); - } + //------------------------------------------------------------------ + // Tests below here are not included in the Java test suite + //------------------------------------------------------------------ + [Test] + public void ShouldThrowAnExceptionWhenTheValueIsNull() + { + Assert.That(() => new ReturnedCookie("name", null, null, null, DateTime.Now, false, false), Throws.InstanceOf()); + } - [Test] - public void ShouldAllowExpiryToBeNull() - { - Cookie cookie = new ReturnedCookie("name", "value", "", "/", null, false, false); - } + [Test] + public void ShouldAllowExpiryToBeNull() + { + Cookie cookie = new ReturnedCookie("name", "value", "", "/", null, false, false); } } diff --git a/dotnet/test/common/CorrectEventFiringTest.cs b/dotnet/test/common/CorrectEventFiringTest.cs index 6e1da8e0313bd..e540df5dbae7b 100644 --- a/dotnet/test/common/CorrectEventFiringTest.cs +++ b/dotnet/test/common/CorrectEventFiringTest.cs @@ -25,294 +25,282 @@ using System.Collections.ObjectModel; using System.Text; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class CorrectEventFiringTest : DriverTestFixture { - [TestFixture] - public class CorrectEventFiringTest : DriverTestFixture + [Test] + public void ShouldFireFocusEventWhenClicking() { - [Test] - public void ShouldFireFocusEventWhenClicking() - { - driver.Url = javascriptPage; + driver.Url = javascriptPage; - ClickOnElementWhichRecordsEvents(driver); + ClickOnElementWhichRecordsEvents(driver); - AssertEventFired("focus", driver); - } + AssertEventFired("focus", driver); + } - [Test] - [NeedsFreshDriver(IsCreatedBeforeTest = true, IsCreatedAfterTest = true)] - [IgnoreBrowser(Browser.Safari, "Safari driver does not support multiple instances")] - public void ShouldFireFocusEventInNonTopmostWindow() + [Test] + [NeedsFreshDriver(IsCreatedBeforeTest = true, IsCreatedAfterTest = true)] + [IgnoreBrowser(Browser.Safari, "Safari driver does not support multiple instances")] + public void ShouldFireFocusEventInNonTopmostWindow() + { + IWebDriver driver2 = EnvironmentManager.Instance.CreateDriverInstance(); + try { - IWebDriver driver2 = EnvironmentManager.Instance.CreateDriverInstance(); - try - { - // topmost - driver2.Url = javascriptPage; - ClickOnElementWhichRecordsEvents(driver2); - AssertEventFired("focus", driver2); - - // non-topmost - driver.Url = javascriptPage; - ClickOnElementWhichRecordsEvents(driver); - AssertEventFired("focus", driver); + // topmost + driver2.Url = javascriptPage; + ClickOnElementWhichRecordsEvents(driver2); + AssertEventFired("focus", driver2); - } - finally - { - driver2.Quit(); - } - } - - [Test] - public void ShouldFireClickEventWhenClicking() - { + // non-topmost driver.Url = javascriptPage; - ClickOnElementWhichRecordsEvents(driver); + AssertEventFired("focus", driver); - AssertEventFired("click", driver); } - - [Test] - public void ShouldFireMouseDownEventWhenClicking() + finally { - driver.Url = javascriptPage; + driver2.Quit(); + } + } - ClickOnElementWhichRecordsEvents(driver); + [Test] + public void ShouldFireClickEventWhenClicking() + { + driver.Url = javascriptPage; - AssertEventFired("mousedown", driver); - } + ClickOnElementWhichRecordsEvents(driver); - [Test] - public void ShouldFireMouseUpEventWhenClicking() - { - driver.Url = javascriptPage; + AssertEventFired("click", driver); + } - ClickOnElementWhichRecordsEvents(driver); + [Test] + public void ShouldFireMouseDownEventWhenClicking() + { + driver.Url = javascriptPage; - AssertEventFired("mouseup", driver); - } + ClickOnElementWhichRecordsEvents(driver); - [Test] - public void ShouldFireMouseOverEventWhenClicking() - { - driver.Url = javascriptPage; + AssertEventFired("mousedown", driver); + } - ClickOnElementWhichRecordsEvents(driver); + [Test] + public void ShouldFireMouseUpEventWhenClicking() + { + driver.Url = javascriptPage; - AssertEventFired("mouseover", driver); - } + ClickOnElementWhichRecordsEvents(driver); - [Test] - [IgnoreBrowser(Browser.Firefox, "Firefox does not report mouse move event when clicking")] - public void ShouldFireMouseMoveEventWhenClicking() - { - driver.Url = javascriptPage; + AssertEventFired("mouseup", driver); + } - // This bears some explanation. In certain cases, if the prior test - // leaves the mouse cursor immediately over the wrong element, then - // the mousemove event may not get fired, because the mouse does not - // actually move. Prevent this situation by forcing the mouse to move - // to the origin. - new Actions(driver).MoveToElement(driver.FindElement(By.TagName("body"))).Perform(); + [Test] + public void ShouldFireMouseOverEventWhenClicking() + { + driver.Url = javascriptPage; - ClickOnElementWhichRecordsEvents(driver); + ClickOnElementWhichRecordsEvents(driver); - AssertEventFired("mousemove", driver); - } + AssertEventFired("mouseover", driver); + } - [Test] - public void ShouldNotThrowIfEventHandlerThrows() - { - driver.Url = javascriptPage; - driver.FindElement(By.Id("throwing-mouseover")).Click(); - } + [Test] + [IgnoreBrowser(Browser.Firefox, "Firefox does not report mouse move event when clicking")] + public void ShouldFireMouseMoveEventWhenClicking() + { + driver.Url = javascriptPage; - [Test] - public void ShouldFireEventsInTheRightOrder() - { - driver.Url = javascriptPage; + // This bears some explanation. In certain cases, if the prior test + // leaves the mouse cursor immediately over the wrong element, then + // the mousemove event may not get fired, because the mouse does not + // actually move. Prevent this situation by forcing the mouse to move + // to the origin. + new Actions(driver).MoveToElement(driver.FindElement(By.TagName("body"))).Perform(); - ClickOnElementWhichRecordsEvents(driver); + ClickOnElementWhichRecordsEvents(driver); - string text = driver.FindElement(By.Id("result")).Text; + AssertEventFired("mousemove", driver); + } - int lastIndex = -1; - List eventList = new List() { "mousedown", "focus", "mouseup", "click" }; - foreach (string eventName in eventList) - { - int index = text.IndexOf(eventName); + [Test] + public void ShouldNotThrowIfEventHandlerThrows() + { + driver.Url = javascriptPage; + driver.FindElement(By.Id("throwing-mouseover")).Click(); + } - Assert.That(text, Does.Contain(eventName), eventName + " did not fire at all. Text is " + text); - Assert.That(index, Is.GreaterThan(lastIndex), eventName + " did not fire in the correct order. Text is " + text); - lastIndex = index; - } - } + [Test] + public void ShouldFireEventsInTheRightOrder() + { + driver.Url = javascriptPage; - [Test] - public void ShouldIssueMouseDownEvents() - { - driver.Url = javascriptPage; - driver.FindElement(By.Id("mousedown")).Click(); + ClickOnElementWhichRecordsEvents(driver); - String result = driver.FindElement(By.Id("result")).Text; - Assert.That(result, Is.EqualTo("mouse down")); - } + string text = driver.FindElement(By.Id("result")).Text; - [Test] - public void ShouldIssueClickEvents() + int lastIndex = -1; + List eventList = new List() { "mousedown", "focus", "mouseup", "click" }; + foreach (string eventName in eventList) { - driver.Url = javascriptPage; - driver.FindElement(By.Id("mouseclick")).Click(); + int index = text.IndexOf(eventName); - String result = driver.FindElement(By.Id("result")).Text; - Assert.That(result, Is.EqualTo("mouse click")); + Assert.That(text, Does.Contain(eventName), eventName + " did not fire at all. Text is " + text); + Assert.That(index, Is.GreaterThan(lastIndex), eventName + " did not fire in the correct order. Text is " + text); + lastIndex = index; } + } - [Test] - public void ShouldIssueMouseUpEvents() - { - driver.Url = javascriptPage; - driver.FindElement(By.Id("mouseup")).Click(); + [Test] + public void ShouldIssueMouseDownEvents() + { + driver.Url = javascriptPage; + driver.FindElement(By.Id("mousedown")).Click(); - String result = driver.FindElement(By.Id("result")).Text; - Assert.That(result, Is.EqualTo("mouse up")); - } + String result = driver.FindElement(By.Id("result")).Text; + Assert.That(result, Is.EqualTo("mouse down")); + } - [Test] - public void MouseEventsShouldBubbleUpToContainingElements() - { - driver.Url = javascriptPage; - driver.FindElement(By.Id("child")).Click(); + [Test] + public void ShouldIssueClickEvents() + { + driver.Url = javascriptPage; + driver.FindElement(By.Id("mouseclick")).Click(); - String result = driver.FindElement(By.Id("result")).Text; - Assert.That(result, Is.EqualTo("mouse down")); - } + String result = driver.FindElement(By.Id("result")).Text; + Assert.That(result, Is.EqualTo("mouse click")); + } - [Test] - [IgnoreBrowser(Browser.Firefox)] - public void ShouldEmitOnChangeEventsWhenSelectingElements() - { - driver.Url = javascriptPage; - //Intentionally not looking up the select tag. See selenium r7937 for details. - ReadOnlyCollection allOptions = driver.FindElements(By.XPath("//select[@id='selector']//option")); + [Test] + public void ShouldIssueMouseUpEvents() + { + driver.Url = javascriptPage; + driver.FindElement(By.Id("mouseup")).Click(); - String initialTextValue = driver.FindElement(By.Id("result")).Text; + String result = driver.FindElement(By.Id("result")).Text; + Assert.That(result, Is.EqualTo("mouse up")); + } - IWebElement foo = allOptions[0]; - IWebElement bar = allOptions[1]; + [Test] + public void MouseEventsShouldBubbleUpToContainingElements() + { + driver.Url = javascriptPage; + driver.FindElement(By.Id("child")).Click(); - foo.Click(); - Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo(initialTextValue)); - bar.Click(); - Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("bar")); - } + String result = driver.FindElement(By.Id("result")).Text; + Assert.That(result, Is.EqualTo("mouse down")); + } - [Test] - public void ShouldEmitOnClickEventsWhenSelectingElements() - { - driver.Url = javascriptPage; - // Intentionally not looking up the select tag. See selenium r7937 for details. - ReadOnlyCollection allOptions = driver.FindElements(By.XPath("//select[@id='selector2']//option")); + [Test] + [IgnoreBrowser(Browser.Firefox)] + public void ShouldEmitOnChangeEventsWhenSelectingElements() + { + driver.Url = javascriptPage; + //Intentionally not looking up the select tag. See selenium r7937 for details. + ReadOnlyCollection allOptions = driver.FindElements(By.XPath("//select[@id='selector']//option")); - IWebElement foo = allOptions[0]; - IWebElement bar = allOptions[1]; + String initialTextValue = driver.FindElement(By.Id("result")).Text; - foo.Click(); - Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("foo")); - bar.Click(); - Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("bar")); - } + IWebElement foo = allOptions[0]; + IWebElement bar = allOptions[1]; - [Test] - [IgnoreBrowser(Browser.IE, "IE does not fire change event when clicking on checkbox")] - public void ShouldEmitOnChangeEventsWhenChangingTheStateOfACheckbox() - { - driver.Url = javascriptPage; - IWebElement checkbox = driver.FindElement(By.Id("checkbox")); + foo.Click(); + Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo(initialTextValue)); + bar.Click(); + Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("bar")); + } - checkbox.Click(); - Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("checkbox thing")); - } + [Test] + public void ShouldEmitOnClickEventsWhenSelectingElements() + { + driver.Url = javascriptPage; + // Intentionally not looking up the select tag. See selenium r7937 for details. + ReadOnlyCollection allOptions = driver.FindElements(By.XPath("//select[@id='selector2']//option")); - [Test] - public void ShouldEmitClickEventWhenClickingOnATextInputElement() - { - driver.Url = javascriptPage; + IWebElement foo = allOptions[0]; + IWebElement bar = allOptions[1]; - IWebElement clicker = driver.FindElement(By.Id("clickField")); - clicker.Click(); + foo.Click(); + Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("foo")); + bar.Click(); + Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("bar")); + } - Assert.That(clicker.GetAttribute("value"), Is.EqualTo("Clicked")); - } + [Test] + [IgnoreBrowser(Browser.IE, "IE does not fire change event when clicking on checkbox")] + public void ShouldEmitOnChangeEventsWhenChangingTheStateOfACheckbox() + { + driver.Url = javascriptPage; + IWebElement checkbox = driver.FindElement(By.Id("checkbox")); - [Test] - public void ShouldFireTwoClickEventsWhenClickingOnALabel() - { - driver.Url = javascriptPage; + checkbox.Click(); + Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo("checkbox thing")); + } - driver.FindElement(By.Id("labelForCheckbox")).Click(); + [Test] + public void ShouldEmitClickEventWhenClickingOnATextInputElement() + { + driver.Url = javascriptPage; - IWebElement result = driver.FindElement(By.Id("result")); - Assert.That(WaitFor(() => { return result.Text.Contains("labelclick chboxclick"); }, "Did not find text: " + result.Text), Is.True); - } + IWebElement clicker = driver.FindElement(By.Id("clickField")); + clicker.Click(); + Assert.That(clicker.GetAttribute("value"), Is.EqualTo("Clicked")); + } - [Test] - public void ClearingAnElementShouldCauseTheOnChangeHandlerToFire() - { - driver.Url = javascriptPage; + [Test] + public void ShouldFireTwoClickEventsWhenClickingOnALabel() + { + driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("clearMe")); - element.Clear(); + driver.FindElement(By.Id("labelForCheckbox")).Click(); - IWebElement result = driver.FindElement(By.Id("result")); - Assert.That(result.Text.Trim(), Is.EqualTo("Cleared")); - } + IWebElement result = driver.FindElement(By.Id("result")); + Assert.That(WaitFor(() => { return result.Text.Contains("labelclick chboxclick"); }, "Did not find text: " + result.Text), Is.True); + } - [Test] - public void SendingKeysToAnotherElementShouldCauseTheBlurEventToFire() - { - driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("theworks")); + + [Test] + public void ClearingAnElementShouldCauseTheOnChangeHandlerToFire() + { + driver.Url = javascriptPage; + + IWebElement element = driver.FindElement(By.Id("clearMe")); + element.Clear(); + + IWebElement result = driver.FindElement(By.Id("result")); + Assert.That(result.Text.Trim(), Is.EqualTo("Cleared")); + } + + [Test] + public void SendingKeysToAnotherElementShouldCauseTheBlurEventToFire() + { + driver.Url = javascriptPage; + IWebElement element = driver.FindElement(By.Id("theworks")); + element.SendKeys("foo"); + IWebElement element2 = driver.FindElement(By.Id("changeable")); + element2.SendKeys("bar"); + AssertEventFired("blur", driver); + } + + [Test] + [IgnoreBrowser(Browser.Safari, "Safari driver does not support multiple instances")] + public void SendingKeysToAnotherElementShouldCauseTheBlurEventToFireInNonTopmostWindow() + { + IWebElement element = null; + IWebElement element2 = null; + IWebDriver driver2 = EnvironmentManager.Instance.CreateDriverInstance(); + try + { + // topmost + driver2.Url = javascriptPage; + element = driver2.FindElement(By.Id("theworks")); element.SendKeys("foo"); - IWebElement element2 = driver.FindElement(By.Id("changeable")); + element2 = driver2.FindElement(By.Id("changeable")); element2.SendKeys("bar"); - AssertEventFired("blur", driver); - } - - [Test] - [IgnoreBrowser(Browser.Safari, "Safari driver does not support multiple instances")] - public void SendingKeysToAnotherElementShouldCauseTheBlurEventToFireInNonTopmostWindow() - { - IWebElement element = null; - IWebElement element2 = null; - IWebDriver driver2 = EnvironmentManager.Instance.CreateDriverInstance(); - try - { - // topmost - driver2.Url = javascriptPage; - element = driver2.FindElement(By.Id("theworks")); - element.SendKeys("foo"); - element2 = driver2.FindElement(By.Id("changeable")); - element2.SendKeys("bar"); - AssertEventFired("blur", driver2); - - // non-topmost - driver.Url = javascriptPage; - element = driver.FindElement(By.Id("theworks")); - element.SendKeys("foo"); - element2 = driver.FindElement(By.Id("changeable")); - element2.SendKeys("bar"); - AssertEventFired("blur", driver); - } - finally - { - driver2.Quit(); - } + AssertEventFired("blur", driver2); + // non-topmost driver.Url = javascriptPage; element = driver.FindElement(By.Id("theworks")); element.SendKeys("foo"); @@ -320,193 +308,204 @@ public void SendingKeysToAnotherElementShouldCauseTheBlurEventToFireInNonTopmost element2.SendKeys("bar"); AssertEventFired("blur", driver); } - - [Test] - public void SendingKeysToAnElementShouldCauseTheFocusEventToFire() + finally { - driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("theworks")); - element.SendKeys("foo"); - AssertEventFired("focus", driver); + driver2.Quit(); } - [Test] - public void SendingKeysToAFocusedElementShouldNotBlurThatElement() - { - driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("theworks")); - element.Click(); - - //Wait until focused - bool focused = false; - IWebElement result = driver.FindElement(By.Id("result")); - for (int i = 0; i < 5; ++i) - { - string fired = result.Text; - if (fired.Contains("focus")) - { - focused = true; - break; - } - - System.Threading.Thread.Sleep(200); - } + driver.Url = javascriptPage; + element = driver.FindElement(By.Id("theworks")); + element.SendKeys("foo"); + element2 = driver.FindElement(By.Id("changeable")); + element2.SendKeys("bar"); + AssertEventFired("blur", driver); + } - Assert.That(focused, Is.True, "Clicking on element didn't focus it in time - can't proceed so failing"); + [Test] + public void SendingKeysToAnElementShouldCauseTheFocusEventToFire() + { + driver.Url = javascriptPage; + IWebElement element = driver.FindElement(By.Id("theworks")); + element.SendKeys("foo"); + AssertEventFired("focus", driver); + } - element.SendKeys("a"); - AssertEventNotFired("blur"); - } + [Test] + public void SendingKeysToAFocusedElementShouldNotBlurThatElement() + { + driver.Url = javascriptPage; + IWebElement element = driver.FindElement(By.Id("theworks")); + element.Click(); - [Test] - [IgnoreBrowser(Browser.IE, "Clicking on child does blur parent, whether focused or not.")] - public void ClickingAnUnfocusableChildShouldNotBlurTheParent() + //Wait until focused + bool focused = false; + IWebElement result = driver.FindElement(By.Id("result")); + for (int i = 0; i < 5; ++i) { - if (TestUtilities.IsOldIE(driver)) + string fired = result.Text; + if (fired.Contains("focus")) { - return; + focused = true; + break; } - driver.Url = javascriptPage; - // Click on parent, giving it the focus. - IWebElement parent = driver.FindElement(By.Id("hideOnBlur")); - parent.Click(); - AssertEventNotFired("blur"); - // Click on child. It is not focusable, so focus should stay on the parent. - driver.FindElement(By.Id("hideOnBlurChild")).Click(); - System.Threading.Thread.Sleep(2000); - Assert.That(parent.Displayed, Is.True, "#hideOnBlur should still be displayed after click"); - AssertEventNotFired("blur"); - // Click elsewhere, and let the element disappear. - driver.FindElement(By.Id("result")).Click(); - AssertEventFired("blur", driver); - } - - [Test] - public void SubmittingFormFromFormElementShouldFireOnSubmitForThatForm() - { - driver.Url = javascriptPage; - IWebElement formElement = driver.FindElement(By.Id("submitListeningForm")); - formElement.Submit(); - AssertEventFired("form-onsubmit", driver); - } - - [Test] - public void SubmittingFormFromFormInputSubmitElementShouldFireOnSubmitForThatForm() - { - driver.Url = javascriptPage; - IWebElement submit = driver.FindElement(By.Id("submitListeningForm-submit")); - submit.Submit(); - AssertEventFired("form-onsubmit", driver); + System.Threading.Thread.Sleep(200); } - [Test] - public void SubmittingFormFromFormInputTextElementShouldFireOnSubmitForThatFormAndNotClickOnThatInput() - { - driver.Url = javascriptPage; - IWebElement submit = driver.FindElement(By.Id("submitListeningForm-submit")); - submit.Submit(); - AssertEventFired("form-onsubmit", driver); - AssertEventNotFired("text-onclick"); - } + Assert.That(focused, Is.True, "Clicking on element didn't focus it in time - can't proceed so failing"); - [Test] - public void UploadingFileShouldFireOnChangeEvent() - { - driver.Url = formsPage; - IWebElement uploadElement = driver.FindElement(By.Id("upload")); - IWebElement result = driver.FindElement(By.Id("fileResults")); - Assert.That(result.Text, Is.Empty); - - string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, "test.txt"); - System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); - System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); - inputFileWriter.WriteLine("Hello world"); - inputFileWriter.Close(); - - uploadElement.SendKeys(inputFile.FullName); - // Shift focus to something else because send key doesn't make the focus leave - driver.FindElement(By.Id("id-name1")).Click(); - - inputFile.Delete(); - Assert.That(result.Text, Is.EqualTo("changed")); - } + element.SendKeys("a"); + AssertEventNotFired("blur"); + } - [Test] - public void ShouldReportTheXAndYCoordinatesWhenClicking() - { - driver.Url = clickEventPage; + [Test] + [IgnoreBrowser(Browser.IE, "Clicking on child does blur parent, whether focused or not.")] + public void ClickingAnUnfocusableChildShouldNotBlurTheParent() + { + if (TestUtilities.IsOldIE(driver)) + { + return; + } + + driver.Url = javascriptPage; + // Click on parent, giving it the focus. + IWebElement parent = driver.FindElement(By.Id("hideOnBlur")); + parent.Click(); + AssertEventNotFired("blur"); + // Click on child. It is not focusable, so focus should stay on the parent. + driver.FindElement(By.Id("hideOnBlurChild")).Click(); + System.Threading.Thread.Sleep(2000); + Assert.That(parent.Displayed, Is.True, "#hideOnBlur should still be displayed after click"); + AssertEventNotFired("blur"); + // Click elsewhere, and let the element disappear. + driver.FindElement(By.Id("result")).Click(); + AssertEventFired("blur", driver); + } - IWebElement element = driver.FindElement(By.Id("eventish")); - element.Click(); + [Test] + public void SubmittingFormFromFormElementShouldFireOnSubmitForThatForm() + { + driver.Url = javascriptPage; + IWebElement formElement = driver.FindElement(By.Id("submitListeningForm")); + formElement.Submit(); + AssertEventFired("form-onsubmit", driver); + } - driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(2); - string clientX = driver.FindElement(By.Id("clientX")).Text; - string clientY = driver.FindElement(By.Id("clientY")).Text; - driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0); + [Test] + public void SubmittingFormFromFormInputSubmitElementShouldFireOnSubmitForThatForm() + { + driver.Url = javascriptPage; + IWebElement submit = driver.FindElement(By.Id("submitListeningForm-submit")); + submit.Submit(); + AssertEventFired("form-onsubmit", driver); + } - Assert.That(clientX, Is.Not.EqualTo("0")); - Assert.That(clientY, Is.Not.EqualTo("0")); - } + [Test] + public void SubmittingFormFromFormInputTextElementShouldFireOnSubmitForThatFormAndNotClickOnThatInput() + { + driver.Url = javascriptPage; + IWebElement submit = driver.FindElement(By.Id("submitListeningForm-submit")); + submit.Submit(); + AssertEventFired("form-onsubmit", driver); + AssertEventNotFired("text-onclick"); + } - [Test] - public void ClickEventsShouldBubble() - { - driver.Url = clicksPage; - driver.FindElement(By.Id("bubblesFrom")).Click(); - bool eventBubbled = (bool)((IJavaScriptExecutor)driver).ExecuteScript("return !!window.bubbledClick;"); - Assert.That(eventBubbled, Is.True, "Event didn't bubble up"); - } + [Test] + public void UploadingFileShouldFireOnChangeEvent() + { + driver.Url = formsPage; + IWebElement uploadElement = driver.FindElement(By.Id("upload")); + IWebElement result = driver.FindElement(By.Id("fileResults")); + Assert.That(result.Text, Is.Empty); + + string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, "test.txt"); + System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); + System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); + inputFileWriter.WriteLine("Hello world"); + inputFileWriter.Close(); + + uploadElement.SendKeys(inputFile.FullName); + // Shift focus to something else because send key doesn't make the focus leave + driver.FindElement(By.Id("id-name1")).Click(); + + inputFile.Delete(); + Assert.That(result.Text, Is.EqualTo("changed")); + } - [Test] - public void ClickOverlappingElements() - { - if (TestUtilities.IsOldIE(driver)) - { - Assert.Ignore("Not supported on IE < 9"); - } + [Test] + public void ShouldReportTheXAndYCoordinatesWhenClicking() + { + driver.Url = clickEventPage; - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/overlapping_elements.html"); - Assert.That(() => driver.FindElement(By.Id("under")).Click(), Throws.InstanceOf().Or.InstanceOf().With.Message.Contains("Other element would receive the click")); - } + IWebElement element = driver.FindElement(By.Id("eventish")); + element.Click(); - [Test] - public void ClickAnElementThatDisappear() - { - if (TestUtilities.IsOldIE(driver)) - { - Assert.Ignore("Not supported on IE < 9"); - } + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(2); + string clientX = driver.FindElement(By.Id("clientX")).Text; + string clientY = driver.FindElement(By.Id("clientY")).Text; + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0); - StringBuilder expectedLogBuilder = new StringBuilder(); - expectedLogBuilder.AppendLine("Log:"); - expectedLogBuilder.AppendLine("mousedown in over (handled by over)"); - expectedLogBuilder.AppendLine("mousedown in over (handled by body)"); - expectedLogBuilder.AppendLine("mouseup in under (handled by under)"); - expectedLogBuilder.Append("mouseup in under (handled by body)"); + Assert.That(clientX, Is.Not.EqualTo("0")); + Assert.That(clientY, Is.Not.EqualTo("0")); + } - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/disappearing_element.html"); - driver.FindElement(By.Id("over")).Click(); - Assert.That(driver.FindElement(By.Id("log")).Text, Does.StartWith(expectedLogBuilder.ToString())); - } + [Test] + public void ClickEventsShouldBubble() + { + driver.Url = clicksPage; + driver.FindElement(By.Id("bubblesFrom")).Click(); + bool eventBubbled = (bool)((IJavaScriptExecutor)driver).ExecuteScript("return !!window.bubbledClick;"); + Assert.That(eventBubbled, Is.True, "Event didn't bubble up"); + } - private void AssertEventNotFired(string eventName) + [Test] + public void ClickOverlappingElements() + { + if (TestUtilities.IsOldIE(driver)) { - IWebElement result = driver.FindElement(By.Id("result")); - string text = result.Text; - Assert.That(text, Does.Not.Contain(eventName)); + Assert.Ignore("Not supported on IE < 9"); } - private void ClickOnElementWhichRecordsEvents(IWebDriver focusedDriver) - { - focusedDriver.FindElement(By.Id("plainButton")).Click(); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/overlapping_elements.html"); + Assert.That(() => driver.FindElement(By.Id("under")).Click(), Throws.InstanceOf().Or.InstanceOf().With.Message.Contains("Other element would receive the click")); + } - private void AssertEventFired(string eventName, IWebDriver focusedDriver) + [Test] + public void ClickAnElementThatDisappear() + { + if (TestUtilities.IsOldIE(driver)) { - IWebElement result = focusedDriver.FindElement(By.Id("result")); - string text = result.Text; - Assert.That(text, Does.Contain(eventName)); + Assert.Ignore("Not supported on IE < 9"); } + + StringBuilder expectedLogBuilder = new StringBuilder(); + expectedLogBuilder.AppendLine("Log:"); + expectedLogBuilder.AppendLine("mousedown in over (handled by over)"); + expectedLogBuilder.AppendLine("mousedown in over (handled by body)"); + expectedLogBuilder.AppendLine("mouseup in under (handled by under)"); + expectedLogBuilder.Append("mouseup in under (handled by body)"); + + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_tests/disappearing_element.html"); + driver.FindElement(By.Id("over")).Click(); + Assert.That(driver.FindElement(By.Id("log")).Text, Does.StartWith(expectedLogBuilder.ToString())); + } + + private void AssertEventNotFired(string eventName) + { + IWebElement result = driver.FindElement(By.Id("result")); + string text = result.Text; + Assert.That(text, Does.Not.Contain(eventName)); + } + + private void ClickOnElementWhichRecordsEvents(IWebDriver focusedDriver) + { + focusedDriver.FindElement(By.Id("plainButton")).Click(); + } + + private void AssertEventFired(string eventName, IWebDriver focusedDriver) + { + IWebElement result = focusedDriver.FindElement(By.Id("result")); + string text = result.Text; + Assert.That(text, Does.Contain(eventName)); } } diff --git a/dotnet/test/common/CssValueTest.cs b/dotnet/test/common/CssValueTest.cs index d885a6eb8ba1d..c290e9cf70367 100644 --- a/dotnet/test/common/CssValueTest.cs +++ b/dotnet/test/common/CssValueTest.cs @@ -20,50 +20,49 @@ using NUnit.Framework; using OpenQA.Selenium.Environment; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class CssValueTest : DriverTestFixture { - [TestFixture] - public class CssValueTest : DriverTestFixture + [Test] + public void ShouldPickUpStyleOfAnElement() { - [Test] - public void ShouldPickUpStyleOfAnElement() - { - driver.Url = javascriptPage; + driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("green-parent")); - string backgroundColour = element.GetCssValue("background-color"); + IWebElement element = driver.FindElement(By.Id("green-parent")); + string backgroundColour = element.GetCssValue("background-color"); - Assert.That(backgroundColour, Is.EqualTo("#008000").Or.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); + Assert.That(backgroundColour, Is.EqualTo("#008000").Or.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); - element = driver.FindElement(By.Id("red-item")); - backgroundColour = element.GetCssValue("background-color"); + element = driver.FindElement(By.Id("red-item")); + backgroundColour = element.GetCssValue("background-color"); - Assert.That(backgroundColour, Is.EqualTo("#ff0000").Or.EqualTo("rgba(255, 0, 0, 1)").Or.EqualTo("rgb(255, 0, 0)")); - } + Assert.That(backgroundColour, Is.EqualTo("#ff0000").Or.EqualTo("rgba(255, 0, 0, 1)").Or.EqualTo("rgb(255, 0, 0)")); + } - [Test] - public void GetCssValueShouldReturnStandardizedColour() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("colorPage.html"); + [Test] + public void GetCssValueShouldReturnStandardizedColour() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("colorPage.html"); - IWebElement element = driver.FindElement(By.Id("namedColor")); - string backgroundColour = element.GetCssValue("background-color"); - Assert.That(backgroundColour, Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); + IWebElement element = driver.FindElement(By.Id("namedColor")); + string backgroundColour = element.GetCssValue("background-color"); + Assert.That(backgroundColour, Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); - element = driver.FindElement(By.Id("rgb")); - backgroundColour = element.GetCssValue("background-color"); - Assert.That(backgroundColour, Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); - } + element = driver.FindElement(By.Id("rgb")); + backgroundColour = element.GetCssValue("background-color"); + Assert.That(backgroundColour, Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); + } - [Test] - public void ShouldAllowInheritedStylesToBeUsed() - { - driver.Url = javascriptPage; + [Test] + public void ShouldAllowInheritedStylesToBeUsed() + { + driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.Id("green-item")); - string backgroundColour = element.GetCssValue("background-color"); + IWebElement element = driver.FindElement(By.Id("green-item")); + string backgroundColour = element.GetCssValue("background-color"); - Assert.That(backgroundColour, Is.EqualTo("transparent").Or.EqualTo("rgba(0, 0, 0, 0)")); - } + Assert.That(backgroundColour, Is.EqualTo("transparent").Or.EqualTo("rgba(0, 0, 0, 0)")); } } diff --git a/dotnet/test/common/CustomDriverConfigs/DefaultSafariDriver.cs b/dotnet/test/common/CustomDriverConfigs/DefaultSafariDriver.cs index 311a2832a8e2c..7a66e56993b68 100644 --- a/dotnet/test/common/CustomDriverConfigs/DefaultSafariDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/DefaultSafariDriver.cs @@ -17,22 +17,21 @@ // under the License. // -namespace OpenQA.Selenium.Safari +namespace OpenQA.Selenium.Safari; + +// This is a simple wrapper class to create a SafariDriver that +// uses the technology preview implementation and has no parameters in the +// constructor. +public class DefaultSafariDriver : SafariDriver { - // This is a simple wrapper class to create a SafariDriver that - // uses the technology preview implementation and has no parameters in the - // constructor. - public class DefaultSafariDriver : SafariDriver + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public DefaultSafariDriver(SafariOptions options) + : base(options) { - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public DefaultSafariDriver(SafariOptions options) - : base(options) - { - } + } - public DefaultSafariDriver(SafariDriverService service, SafariOptions options) - : base(service, options) - { - } + public DefaultSafariDriver(SafariDriverService service, SafariOptions options) + : base(service, options) + { } } diff --git a/dotnet/test/common/CustomDriverConfigs/DevChannelChromeDriver.cs b/dotnet/test/common/CustomDriverConfigs/DevChannelChromeDriver.cs index 1f0a17e677583..2c25a9af0e7b1 100644 --- a/dotnet/test/common/CustomDriverConfigs/DevChannelChromeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/DevChannelChromeDriver.cs @@ -17,29 +17,28 @@ // under the License. // -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +public class DevChannelChromeDriver : ChromeDriver { - public class DevChannelChromeDriver : ChromeDriver + public DevChannelChromeDriver() + : base(DefaultOptions) { - public DevChannelChromeDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public DevChannelChromeDriver(ChromeOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public DevChannelChromeDriver(ChromeOptions options) + : base(options) + { + } - public DevChannelChromeDriver(ChromeDriverService service, ChromeOptions options) - : base(service, options) - { - } + public DevChannelChromeDriver(ChromeDriverService service, ChromeOptions options) + : base(service, options) + { + } - public static ChromeOptions DefaultOptions - { - get { return new ChromeOptions() { BrowserVersion = "dev" }; } - } + public static ChromeOptions DefaultOptions + { + get { return new ChromeOptions() { BrowserVersion = "dev" }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/DevChannelEdgeDriver.cs b/dotnet/test/common/CustomDriverConfigs/DevChannelEdgeDriver.cs index 0fbcbf0189093..f29fffae47267 100644 --- a/dotnet/test/common/CustomDriverConfigs/DevChannelEdgeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/DevChannelEdgeDriver.cs @@ -17,29 +17,28 @@ // under the License. // -namespace OpenQA.Selenium.Edge +namespace OpenQA.Selenium.Edge; + +public class DevChannelEdgeDriver : EdgeDriver { - public class DevChannelEdgeDriver : EdgeDriver + public DevChannelEdgeDriver() + : base(DefaultOptions) { - public DevChannelEdgeDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public DevChannelEdgeDriver(EdgeOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public DevChannelEdgeDriver(EdgeOptions options) + : base(options) + { + } - public DevChannelEdgeDriver(EdgeDriverService service, EdgeOptions options) - : base(service, options) - { - } + public DevChannelEdgeDriver(EdgeDriverService service, EdgeOptions options) + : base(service, options) + { + } - public static EdgeOptions DefaultOptions - { - get { return new EdgeOptions() { BrowserVersion = "dev" }; } - } + public static EdgeOptions DefaultOptions + { + get { return new EdgeOptions() { BrowserVersion = "dev" }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/EdgeInternetExplorerModeDriver.cs b/dotnet/test/common/CustomDriverConfigs/EdgeInternetExplorerModeDriver.cs index 09c97ba2ea82c..a5f5c7788d97f 100644 --- a/dotnet/test/common/CustomDriverConfigs/EdgeInternetExplorerModeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/EdgeInternetExplorerModeDriver.cs @@ -17,32 +17,31 @@ // under the License. // -namespace OpenQA.Selenium.IE +namespace OpenQA.Selenium.IE; + +// This is a simple wrapper class to create an InternetExplorerDriver that +// uses the enables RequireWindowFocus as the default input simplation. +public class EdgeInternetExplorerModeDriver : InternetExplorerDriver { - // This is a simple wrapper class to create an InternetExplorerDriver that - // uses the enables RequireWindowFocus as the default input simplation. - public class EdgeInternetExplorerModeDriver : InternetExplorerDriver - { - public EdgeInternetExplorerModeDriver() - : base(DefaultOptions) - { - } + public EdgeInternetExplorerModeDriver() + : base(DefaultOptions) + { + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public EdgeInternetExplorerModeDriver(InternetExplorerOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public EdgeInternetExplorerModeDriver(InternetExplorerOptions options) + : base(options) + { + } - public EdgeInternetExplorerModeDriver(InternetExplorerDriverService service, InternetExplorerOptions options) - : base(service, options) - { - } + public EdgeInternetExplorerModeDriver(InternetExplorerDriverService service, InternetExplorerOptions options) + : base(service, options) + { + } - public static InternetExplorerOptions DefaultOptions - { - get { return new InternetExplorerOptions() { RequireWindowFocus = true, UsePerProcessProxy = true, AttachToEdgeChrome = true }; } - } + public static InternetExplorerOptions DefaultOptions + { + get { return new InternetExplorerOptions() { RequireWindowFocus = true, UsePerProcessProxy = true, AttachToEdgeChrome = true }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/NightlyChannelFirefoxDriver.cs b/dotnet/test/common/CustomDriverConfigs/NightlyChannelFirefoxDriver.cs index a551057fa4b92..0d54b31414e23 100644 --- a/dotnet/test/common/CustomDriverConfigs/NightlyChannelFirefoxDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/NightlyChannelFirefoxDriver.cs @@ -17,32 +17,31 @@ // under the License. // -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +// This is a simple wrapper class to create a FirefoxDriver that +// uses the Marionette implementation and has no parameters in the +// constructor. +public class NightlyChannelFirefoxDriver : FirefoxDriver { - // This is a simple wrapper class to create a FirefoxDriver that - // uses the Marionette implementation and has no parameters in the - // constructor. - public class NightlyChannelFirefoxDriver : FirefoxDriver + public NightlyChannelFirefoxDriver() + : base(DefaultOptions) { - public NightlyChannelFirefoxDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public NightlyChannelFirefoxDriver(FirefoxOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public NightlyChannelFirefoxDriver(FirefoxOptions options) + : base(options) + { + } - public NightlyChannelFirefoxDriver(FirefoxDriverService service, FirefoxOptions options) - : base(service, options) - { - } + public NightlyChannelFirefoxDriver(FirefoxDriverService service, FirefoxOptions options) + : base(service, options) + { + } - public static FirefoxOptions DefaultOptions - { - get { return new FirefoxOptions() { BrowserVersion = "nightly", AcceptInsecureCertificates = true, EnableDevToolsProtocol = true }; } - } + public static FirefoxOptions DefaultOptions + { + get { return new FirefoxOptions() { BrowserVersion = "nightly", AcceptInsecureCertificates = true, EnableDevToolsProtocol = true }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/SafariTechnologyPreviewDriver.cs b/dotnet/test/common/CustomDriverConfigs/SafariTechnologyPreviewDriver.cs index 5143122fb416b..0566a4ee08e98 100644 --- a/dotnet/test/common/CustomDriverConfigs/SafariTechnologyPreviewDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/SafariTechnologyPreviewDriver.cs @@ -17,37 +17,36 @@ // under the License. // -namespace OpenQA.Selenium.Safari +namespace OpenQA.Selenium.Safari; + +// This is a simple wrapper class to create a SafariDriver that +// uses the technology preview implementation and has no parameters in the +// constructor. +public class SafariTechnologyPreviewDriver : SafariDriver { - // This is a simple wrapper class to create a SafariDriver that - // uses the technology preview implementation and has no parameters in the - // constructor. - public class SafariTechnologyPreviewDriver : SafariDriver + public SafariTechnologyPreviewDriver() + : base(DefaultOptions) { - public SafariTechnologyPreviewDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public SafariTechnologyPreviewDriver(SafariOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public SafariTechnologyPreviewDriver(SafariOptions options) + : base(options) + { + } - public SafariTechnologyPreviewDriver(SafariDriverService service, SafariOptions options) - : base(service, options) - { - } + public SafariTechnologyPreviewDriver(SafariDriverService service, SafariOptions options) + : base(service, options) + { + } - public static SafariOptions DefaultOptions + public static SafariOptions DefaultOptions + { + get { - get - { - SafariOptions options = new SafariOptions(); - options.UseTechnologyPreview(); - return options; - } + SafariOptions options = new SafariOptions(); + options.UseTechnologyPreview(); + return options; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs b/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs index 53f13d6120179..0d44039aa12a3 100644 --- a/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs @@ -17,29 +17,28 @@ // under the License. // -namespace OpenQA.Selenium.Chrome +namespace OpenQA.Selenium.Chrome; + +public class StableChannelChromeDriver : ChromeDriver { - public class StableChannelChromeDriver : ChromeDriver + public StableChannelChromeDriver() + : base(DefaultOptions) { - public StableChannelChromeDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public StableChannelChromeDriver(ChromeOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public StableChannelChromeDriver(ChromeOptions options) + : base(options) + { + } - public StableChannelChromeDriver(ChromeDriverService service, ChromeOptions options) - : base(service, options) - { - } + public StableChannelChromeDriver(ChromeDriverService service, ChromeOptions options) + : base(service, options) + { + } - public static ChromeOptions DefaultOptions - { - get { return new ChromeOptions() { BrowserVersion = "135" }; } - } + public static ChromeOptions DefaultOptions + { + get { return new ChromeOptions() { BrowserVersion = "135" }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/StableChannelEdgeDriver.cs b/dotnet/test/common/CustomDriverConfigs/StableChannelEdgeDriver.cs index f73a1c14faae0..64f8e58f59668 100644 --- a/dotnet/test/common/CustomDriverConfigs/StableChannelEdgeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/StableChannelEdgeDriver.cs @@ -17,29 +17,28 @@ // under the License. // -namespace OpenQA.Selenium.Edge +namespace OpenQA.Selenium.Edge; + +public class StableChannelEdgeDriver : EdgeDriver { - public class StableChannelEdgeDriver : EdgeDriver - { - public StableChannelEdgeDriver() - : base(DefaultOptions) - { - } + public StableChannelEdgeDriver() + : base(DefaultOptions) + { + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public StableChannelEdgeDriver(EdgeOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public StableChannelEdgeDriver(EdgeOptions options) + : base(options) + { + } - public StableChannelEdgeDriver(EdgeDriverService service, EdgeOptions options) - : base(service, options) - { - } - public static EdgeOptions DefaultOptions - { - get { return new EdgeOptions(); } - } + public StableChannelEdgeDriver(EdgeDriverService service, EdgeOptions options) + : base(service, options) + { + } + public static EdgeOptions DefaultOptions + { + get { return new EdgeOptions(); } } } diff --git a/dotnet/test/common/CustomDriverConfigs/StableChannelFirefoxDriver.cs b/dotnet/test/common/CustomDriverConfigs/StableChannelFirefoxDriver.cs index ae487e6e3de35..800e8d9cd172c 100644 --- a/dotnet/test/common/CustomDriverConfigs/StableChannelFirefoxDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/StableChannelFirefoxDriver.cs @@ -17,32 +17,31 @@ // under the License. // -namespace OpenQA.Selenium.Firefox +namespace OpenQA.Selenium.Firefox; + +// This is a simple wrapper class to create a FirefoxDriver that +// uses the Marionette implementation and has no parameters in the +// constructor. +public class StableChannelFirefoxDriver : FirefoxDriver { - // This is a simple wrapper class to create a FirefoxDriver that - // uses the Marionette implementation and has no parameters in the - // constructor. - public class StableChannelFirefoxDriver : FirefoxDriver + public StableChannelFirefoxDriver() + : base(DefaultOptions) { - public StableChannelFirefoxDriver() - : base(DefaultOptions) - { - } + } - // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` - public StableChannelFirefoxDriver(FirefoxOptions options) - : base(options) - { - } + // Required for dynamic setting with `EnvironmentManager.Instance.CreateDriverInstance(options)` + public StableChannelFirefoxDriver(FirefoxOptions options) + : base(options) + { + } - public StableChannelFirefoxDriver(FirefoxDriverService service, FirefoxOptions options) - : base(service, options) - { - } + public StableChannelFirefoxDriver(FirefoxDriverService service, FirefoxOptions options) + : base(service, options) + { + } - public static FirefoxOptions DefaultOptions - { - get { return new FirefoxOptions() { AcceptInsecureCertificates = true, EnableDevToolsProtocol = true }; } - } + public static FirefoxOptions DefaultOptions + { + get { return new FirefoxOptions() { AcceptInsecureCertificates = true, EnableDevToolsProtocol = true }; } } } diff --git a/dotnet/test/common/CustomDriverConfigs/StableChannelRemoteChromeDriver.cs b/dotnet/test/common/CustomDriverConfigs/StableChannelRemoteChromeDriver.cs index 1fb8520f6ad5a..4f236f8a8ed6f 100644 --- a/dotnet/test/common/CustomDriverConfigs/StableChannelRemoteChromeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/StableChannelRemoteChromeDriver.cs @@ -20,14 +20,13 @@ using OpenQA.Selenium.Chrome; using System; -namespace OpenQA.Selenium.Remote +namespace OpenQA.Selenium.Remote; + +public class StableChannelRemoteChromeDriver : RemoteWebDriver { - public class StableChannelRemoteChromeDriver : RemoteWebDriver + public StableChannelRemoteChromeDriver() + : base(new Uri("http://127.0.0.1:6000/wd/hub/"), new ChromeOptions()) { - public StableChannelRemoteChromeDriver() - : base(new Uri("http://127.0.0.1:6000/wd/hub/"), new ChromeOptions()) - { - this.FileDetector = new LocalFileDetector(); - } + this.FileDetector = new LocalFileDetector(); } } diff --git a/dotnet/test/common/CustomTestAttributes/IgnoreBrowserAttribute.cs b/dotnet/test/common/CustomTestAttributes/IgnoreBrowserAttribute.cs index ff3bc71d50522..9d5b1e6485e41 100644 --- a/dotnet/test/common/CustomTestAttributes/IgnoreBrowserAttribute.cs +++ b/dotnet/test/common/CustomTestAttributes/IgnoreBrowserAttribute.cs @@ -24,113 +24,112 @@ using System; using System.Collections.Generic; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] +public class IgnoreBrowserAttribute : NUnitAttribute, IApplyToTest { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class IgnoreBrowserAttribute : NUnitAttribute, IApplyToTest - { - private readonly Browser browser; - private readonly string ignoreReason = string.Empty; + private readonly Browser browser; + private readonly string ignoreReason = string.Empty; - public IgnoreBrowserAttribute(Browser browser) - { - this.browser = browser; - } + public IgnoreBrowserAttribute(Browser browser) + { + this.browser = browser; + } - public IgnoreBrowserAttribute(Browser browser, string reason) - : this(browser) - { - this.ignoreReason = reason; - } + public IgnoreBrowserAttribute(Browser browser, string reason) + : this(browser) + { + this.ignoreReason = reason; + } - public Browser Value - { - get { return browser; } - } + public Browser Value + { + get { return browser; } + } - public string Reason - { - get { return ignoreReason; } - } + public string Reason + { + get { return ignoreReason; } + } - public void ApplyToTest(Test test) + public void ApplyToTest(Test test) + { + if (test.RunState != RunState.NotRunnable) { - if (test.RunState != RunState.NotRunnable) + List ignoreAttributes = new List(); + if (test.IsSuite) { - List ignoreAttributes = new List(); - if (test.IsSuite) + Attribute[] ignoreClassAttributes = test.TypeInfo.GetCustomAttributes(true); + if (ignoreClassAttributes.Length > 0) { - Attribute[] ignoreClassAttributes = test.TypeInfo.GetCustomAttributes(true); - if (ignoreClassAttributes.Length > 0) - { - ignoreAttributes.AddRange(ignoreClassAttributes); - } + ignoreAttributes.AddRange(ignoreClassAttributes); } - else + } + else + { + IgnoreBrowserAttribute[] ignoreMethodAttributes = test.Method.GetCustomAttributes(true); + if (ignoreMethodAttributes.Length > 0) { - IgnoreBrowserAttribute[] ignoreMethodAttributes = test.Method.GetCustomAttributes(true); - if (ignoreMethodAttributes.Length > 0) - { - ignoreAttributes.AddRange(ignoreMethodAttributes); - } + ignoreAttributes.AddRange(ignoreMethodAttributes); } + } - foreach (Attribute attr in ignoreAttributes) + foreach (Attribute attr in ignoreAttributes) + { + IgnoreBrowserAttribute browserToIgnoreAttr = attr as IgnoreBrowserAttribute; + if (browserToIgnoreAttr != null && IgnoreTestForBrowser(browserToIgnoreAttr.Value)) { - IgnoreBrowserAttribute browserToIgnoreAttr = attr as IgnoreBrowserAttribute; - if (browserToIgnoreAttr != null && IgnoreTestForBrowser(browserToIgnoreAttr.Value)) + string ignoreReason = "Ignoring browser " + EnvironmentManager.Instance.Browser.ToString() + "."; + if (!string.IsNullOrEmpty(browserToIgnoreAttr.Reason)) { - string ignoreReason = "Ignoring browser " + EnvironmentManager.Instance.Browser.ToString() + "."; - if (!string.IsNullOrEmpty(browserToIgnoreAttr.Reason)) - { - ignoreReason = ignoreReason + " " + browserToIgnoreAttr.Reason; - } - - test.RunState = RunState.Ignored; - test.Properties.Set(PropertyNames.SkipReason, browserToIgnoreAttr.Reason); + ignoreReason = ignoreReason + " " + browserToIgnoreAttr.Reason; } + + test.RunState = RunState.Ignored; + test.Properties.Set(PropertyNames.SkipReason, browserToIgnoreAttr.Reason); } } } + } - private bool IgnoreTestForBrowser(Browser browserToIgnore) - { - return browserToIgnore.Equals(EnvironmentManager.Instance.Browser) || browserToIgnore.Equals(Browser.All) || IsRemoteInstanceOfBrowser(browserToIgnore); - } + private bool IgnoreTestForBrowser(Browser browserToIgnore) + { + return browserToIgnore.Equals(EnvironmentManager.Instance.Browser) || browserToIgnore.Equals(Browser.All) || IsRemoteInstanceOfBrowser(browserToIgnore); + } - private bool IsRemoteInstanceOfBrowser(Browser desiredBrowser) + private bool IsRemoteInstanceOfBrowser(Browser desiredBrowser) + { + bool isRemoteInstance = false; + switch (desiredBrowser) { - bool isRemoteInstance = false; - switch (desiredBrowser) - { - case Browser.IE: - if (EnvironmentManager.Instance.RemoteCapabilities == "internet explorer") - { - isRemoteInstance = true; - } - break; + case Browser.IE: + if (EnvironmentManager.Instance.RemoteCapabilities == "internet explorer") + { + isRemoteInstance = true; + } + break; - case Browser.Firefox: - if (EnvironmentManager.Instance.RemoteCapabilities == "firefox") - { - isRemoteInstance = true; - } - break; + case Browser.Firefox: + if (EnvironmentManager.Instance.RemoteCapabilities == "firefox") + { + isRemoteInstance = true; + } + break; - case Browser.Chrome: - if (EnvironmentManager.Instance.RemoteCapabilities == "chrome") - { - isRemoteInstance = true; - } - break; - case Browser.Edge: - if (EnvironmentManager.Instance.RemoteCapabilities == "MicrosoftEdge") - { - isRemoteInstance = true; - } - break; - } - return isRemoteInstance; + case Browser.Chrome: + if (EnvironmentManager.Instance.RemoteCapabilities == "chrome") + { + isRemoteInstance = true; + } + break; + case Browser.Edge: + if (EnvironmentManager.Instance.RemoteCapabilities == "MicrosoftEdge") + { + isRemoteInstance = true; + } + break; } + return isRemoteInstance; } } diff --git a/dotnet/test/common/CustomTestAttributes/IgnorePlatformAttribute.cs b/dotnet/test/common/CustomTestAttributes/IgnorePlatformAttribute.cs index 0e6622ebc4ade..73de23fc43c36 100644 --- a/dotnet/test/common/CustomTestAttributes/IgnorePlatformAttribute.cs +++ b/dotnet/test/common/CustomTestAttributes/IgnorePlatformAttribute.cs @@ -27,101 +27,100 @@ using OSPlatform = System.Runtime.InteropServices.OSPlatform; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] +public class IgnorePlatformAttribute : NUnitAttribute, IApplyToTest { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class IgnorePlatformAttribute : NUnitAttribute, IApplyToTest - { - private readonly String platform; - private readonly string ignoreReason = string.Empty; + private readonly String platform; + private readonly string ignoreReason = string.Empty; - public IgnorePlatformAttribute(string platform) - { - this.platform = platform.ToLower(); - } + public IgnorePlatformAttribute(string platform) + { + this.platform = platform.ToLower(); + } - public IgnorePlatformAttribute(string platform, string reason) - : this(platform) - { - this.ignoreReason = reason; - } + public IgnorePlatformAttribute(string platform, string reason) + : this(platform) + { + this.ignoreReason = reason; + } - public string Value - { - get { return platform; } - } + public string Value + { + get { return platform; } + } - public string Reason - { - get { return ignoreReason; } - } + public string Reason + { + get { return ignoreReason; } + } - public void ApplyToTest(Test test) + public void ApplyToTest(Test test) + { + if (test.RunState != RunState.NotRunnable) { - if (test.RunState != RunState.NotRunnable) + List ignoreAttributes = new List(); + if (test.IsSuite) { - List ignoreAttributes = new List(); - if (test.IsSuite) + Attribute[] ignoreClassAttributes = + test.TypeInfo.GetCustomAttributes(true); + if (ignoreClassAttributes.Length > 0) { - Attribute[] ignoreClassAttributes = - test.TypeInfo.GetCustomAttributes(true); - if (ignoreClassAttributes.Length > 0) - { - ignoreAttributes.AddRange(ignoreClassAttributes); - } + ignoreAttributes.AddRange(ignoreClassAttributes); } - else + } + else + { + IgnorePlatformAttribute[] ignoreMethodAttributes = + test.Method.GetCustomAttributes(true); + if (ignoreMethodAttributes.Length > 0) { - IgnorePlatformAttribute[] ignoreMethodAttributes = - test.Method.GetCustomAttributes(true); - if (ignoreMethodAttributes.Length > 0) - { - ignoreAttributes.AddRange(ignoreMethodAttributes); - } + ignoreAttributes.AddRange(ignoreMethodAttributes); } + } - foreach (Attribute attr in ignoreAttributes) + foreach (Attribute attr in ignoreAttributes) + { + IgnorePlatformAttribute platformToIgnoreAttr = attr as IgnorePlatformAttribute; + if (platformToIgnoreAttr != null && IgnoreTestForPlatform(platformToIgnoreAttr.Value)) { - IgnorePlatformAttribute platformToIgnoreAttr = attr as IgnorePlatformAttribute; - if (platformToIgnoreAttr != null && IgnoreTestForPlatform(platformToIgnoreAttr.Value)) + string ignoreReason = + "Ignoring platform " + EnvironmentManager.Instance.Browser.ToString() + "."; + if (!string.IsNullOrEmpty(platformToIgnoreAttr.Reason)) { - string ignoreReason = - "Ignoring platform " + EnvironmentManager.Instance.Browser.ToString() + "."; - if (!string.IsNullOrEmpty(platformToIgnoreAttr.Reason)) - { - ignoreReason = ignoreReason + " " + platformToIgnoreAttr.Reason; - } - - test.RunState = RunState.Ignored; - test.Properties.Set(PropertyNames.SkipReason, platformToIgnoreAttr.Reason); + ignoreReason = ignoreReason + " " + platformToIgnoreAttr.Reason; } + + test.RunState = RunState.Ignored; + test.Properties.Set(PropertyNames.SkipReason, platformToIgnoreAttr.Reason); } } } + } - private bool IgnoreTestForPlatform(string platformToIgnore) + private bool IgnoreTestForPlatform(string platformToIgnore) + { + return CurrentPlatform() != null && platformToIgnore.Equals(CurrentPlatform()); + } + + private string CurrentPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return CurrentPlatform() != null && platformToIgnore.Equals(CurrentPlatform()); + return "windows"; } - - private string CurrentPlatform() + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "windows"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return "linux"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "mac"; - } - else - { - throw new WebDriverException("Selenium Manager did not find supported operating system"); - } + return "linux"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return "mac"; + } + else + { + throw new WebDriverException("Selenium Manager did not find supported operating system"); } } } diff --git a/dotnet/test/common/CustomTestAttributes/IgnoreTargetAttribute.cs b/dotnet/test/common/CustomTestAttributes/IgnoreTargetAttribute.cs index 152e71cc946bf..8b75e6913a04a 100644 --- a/dotnet/test/common/CustomTestAttributes/IgnoreTargetAttribute.cs +++ b/dotnet/test/common/CustomTestAttributes/IgnoreTargetAttribute.cs @@ -25,71 +25,70 @@ #nullable enable -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] +public class IgnoreTargetAttribute : NUnitAttribute, IApplyToTest { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] - public class IgnoreTargetAttribute : NUnitAttribute, IApplyToTest + public IgnoreTargetAttribute(string target) { - public IgnoreTargetAttribute(string target) - { - this.Value = target.ToLower(); - } + this.Value = target.ToLower(); + } - public IgnoreTargetAttribute(string target, string reason) - : this(target) - { - this.Reason = reason; - } + public IgnoreTargetAttribute(string target, string reason) + : this(target) + { + this.Reason = reason; + } - public string Value { get; } + public string Value { get; } - public string Reason { get; } = string.Empty; + public string Reason { get; } = string.Empty; - public void ApplyToTest(Test test) + public void ApplyToTest(Test test) + { + if (test.RunState is RunState.NotRunnable) { - if (test.RunState is RunState.NotRunnable) - { - return; - } - IgnoreTargetAttribute[] ignoreAttributes; - if (test.IsSuite) - { - ignoreAttributes = test.TypeInfo!.GetCustomAttributes(true); - } - else - { - ignoreAttributes = test.Method!.GetCustomAttributes(true); - } + return; + } + IgnoreTargetAttribute[] ignoreAttributes; + if (test.IsSuite) + { + ignoreAttributes = test.TypeInfo!.GetCustomAttributes(true); + } + else + { + ignoreAttributes = test.Method!.GetCustomAttributes(true); + } - foreach (IgnoreTargetAttribute platformToIgnoreAttr in ignoreAttributes) + foreach (IgnoreTargetAttribute platformToIgnoreAttr in ignoreAttributes) + { + if (IgnoreTestForPlatform(platformToIgnoreAttr.Value)) { - if (IgnoreTestForPlatform(platformToIgnoreAttr.Value)) + string ignoreReason = $"Ignoring target {EnvironmentManager.Instance.Browser}"; + if (!string.IsNullOrEmpty(platformToIgnoreAttr.Reason)) { - string ignoreReason = $"Ignoring target {EnvironmentManager.Instance.Browser}"; - if (!string.IsNullOrEmpty(platformToIgnoreAttr.Reason)) - { - ignoreReason = ignoreReason + ": " + platformToIgnoreAttr.Reason; - } + ignoreReason = ignoreReason + ": " + platformToIgnoreAttr.Reason; + } - test.RunState = RunState.Ignored; - test.Properties.Set(PropertyNames.SkipReason, ignoreReason); + test.RunState = RunState.Ignored; + test.Properties.Set(PropertyNames.SkipReason, ignoreReason); - } } } + } - private static bool IgnoreTestForPlatform(string platformToIgnore) - { - return CurrentPlatform().Equals(platformToIgnore, StringComparison.OrdinalIgnoreCase); - } + private static bool IgnoreTestForPlatform(string platformToIgnore) + { + return CurrentPlatform().Equals(platformToIgnore, StringComparison.OrdinalIgnoreCase); + } - private static string CurrentPlatform() - { + private static string CurrentPlatform() + { #if NET8_0 - return "net8"; + return "net8"; #else #error Update IgnoreTargetAttribute.CurrentPlatform to the current TFM #endif - } } } diff --git a/dotnet/test/common/CustomTestAttributes/NeedsFreshDriverAttribute.cs b/dotnet/test/common/CustomTestAttributes/NeedsFreshDriverAttribute.cs index 669dd82d01db4..a5115175147b4 100644 --- a/dotnet/test/common/CustomTestAttributes/NeedsFreshDriverAttribute.cs +++ b/dotnet/test/common/CustomTestAttributes/NeedsFreshDriverAttribute.cs @@ -21,44 +21,43 @@ using NUnit.Framework.Interfaces; using OpenQA.Selenium.Environment; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +public class NeedsFreshDriverAttribute : TestActionAttribute { - public class NeedsFreshDriverAttribute : TestActionAttribute - { - private bool isCreatedBeforeTest = false; - private bool isCreatedAfterTest = false; + private bool isCreatedBeforeTest = false; + private bool isCreatedAfterTest = false; - public bool IsCreatedBeforeTest - { - get { return isCreatedBeforeTest; } - set { isCreatedBeforeTest = value; } - } + public bool IsCreatedBeforeTest + { + get { return isCreatedBeforeTest; } + set { isCreatedBeforeTest = value; } + } - public bool IsCreatedAfterTest - { - get { return isCreatedAfterTest; } - set { isCreatedAfterTest = value; } - } + public bool IsCreatedAfterTest + { + get { return isCreatedAfterTest; } + set { isCreatedAfterTest = value; } + } - public override void BeforeTest(ITest test) + public override void BeforeTest(ITest test) + { + DriverTestFixture fixtureInstance = test.Fixture as DriverTestFixture; + if (fixtureInstance != null && this.isCreatedBeforeTest) { - DriverTestFixture fixtureInstance = test.Fixture as DriverTestFixture; - if (fixtureInstance != null && this.isCreatedBeforeTest) - { - EnvironmentManager.Instance.CreateFreshDriver(); - fixtureInstance.DriverInstance = EnvironmentManager.Instance.GetCurrentDriver(); - } - base.BeforeTest(test); + EnvironmentManager.Instance.CreateFreshDriver(); + fixtureInstance.DriverInstance = EnvironmentManager.Instance.GetCurrentDriver(); } + base.BeforeTest(test); + } - public override void AfterTest(ITest test) + public override void AfterTest(ITest test) + { + DriverTestFixture fixtureInstance = test.Fixture as DriverTestFixture; + if (fixtureInstance != null && this.isCreatedAfterTest) { - DriverTestFixture fixtureInstance = test.Fixture as DriverTestFixture; - if (fixtureInstance != null && this.isCreatedAfterTest) - { - EnvironmentManager.Instance.CreateFreshDriver(); - fixtureInstance.DriverInstance = EnvironmentManager.Instance.GetCurrentDriver(); - } + EnvironmentManager.Instance.CreateFreshDriver(); + fixtureInstance.DriverInstance = EnvironmentManager.Instance.GetCurrentDriver(); } } } diff --git a/dotnet/test/common/DevTools/DevToolsConsoleTest.cs b/dotnet/test/common/DevTools/DevToolsConsoleTest.cs index 01699778a288c..b6f16d392a13c 100644 --- a/dotnet/test/common/DevTools/DevToolsConsoleTest.cs +++ b/dotnet/test/common/DevTools/DevToolsConsoleTest.cs @@ -23,39 +23,38 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsConsoleTest : DevToolsTestFixture +[TestFixture] +public class DevToolsConsoleTest : DevToolsTestFixture +{ + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyMessageAdded() { - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyMessageAdded() - { - var domains = session.GetVersionSpecificDomains(); - string consoleMessage = "Hello Selenium"; + var domains = session.GetVersionSpecificDomains(); + string consoleMessage = "Hello Selenium"; - ManualResetEventSlim sync = new ManualResetEventSlim(false); - EventHandler messageAddedHandler = (sender, e) => - { - Assert.That(e.Message.Text, Is.EqualTo(consoleMessage)); - sync.Set(); - }; + ManualResetEventSlim sync = new ManualResetEventSlim(false); + EventHandler messageAddedHandler = (sender, e) => + { + Assert.That(e.Message.Text, Is.EqualTo(consoleMessage)); + sync.Set(); + }; - domains.Console.MessageAdded += messageAddedHandler; + domains.Console.MessageAdded += messageAddedHandler; - await domains.Console.Enable(); + await domains.Console.Enable(); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); - ((IJavaScriptExecutor)driver).ExecuteScript("console.log('" + consoleMessage + "');"); - sync.Wait(TimeSpan.FromSeconds(5)); - domains.Console.MessageAdded -= messageAddedHandler; + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); + ((IJavaScriptExecutor)driver).ExecuteScript("console.log('" + consoleMessage + "');"); + sync.Wait(TimeSpan.FromSeconds(5)); + domains.Console.MessageAdded -= messageAddedHandler; - await domains.Console.Disable(); - } + await domains.Console.Disable(); } } diff --git a/dotnet/test/common/DevTools/DevToolsLogTest.cs b/dotnet/test/common/DevTools/DevToolsLogTest.cs index f8c05c79d2ba6..93a2e0daf45b1 100644 --- a/dotnet/test/common/DevTools/DevToolsLogTest.cs +++ b/dotnet/test/common/DevTools/DevToolsLogTest.cs @@ -23,39 +23,38 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsLogTest : DevToolsTestFixture +[TestFixture] +public class DevToolsLogTest : DevToolsTestFixture +{ + [Test] + [Ignore("Unable to open secure url")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyEntryAddedAndClearLog() { - [Test] - [Ignore("Unable to open secure url")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyEntryAddedAndClearLog() + var domains = session.GetVersionSpecificDomains(); + ManualResetEventSlim sync = new ManualResetEventSlim(false); + EventHandler entryAddedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - ManualResetEventSlim sync = new ManualResetEventSlim(false); - EventHandler entryAddedHandler = (sender, e) => - { - Assert.That(e.Entry.Text.Contains("404")); - Assert.That(e.Entry.Level == CurrentCdpVersion.Log.LogEntryLevelValues.Error); - sync.Set(); - }; - - await domains.Log.Enable(); - domains.Log.EntryAdded += entryAddedHandler; - - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("notValidPath"); - sync.Wait(TimeSpan.FromSeconds(5)); - - domains.Log.EntryAdded -= entryAddedHandler; - - await domains.Log.Clear(); - await domains.Log.Disable(); - } + Assert.That(e.Entry.Text.Contains("404")); + Assert.That(e.Entry.Level == CurrentCdpVersion.Log.LogEntryLevelValues.Error); + sync.Set(); + }; + + await domains.Log.Enable(); + domains.Log.EntryAdded += entryAddedHandler; + + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("notValidPath"); + sync.Wait(TimeSpan.FromSeconds(5)); + + domains.Log.EntryAdded -= entryAddedHandler; + + await domains.Log.Clear(); + await domains.Log.Disable(); } } diff --git a/dotnet/test/common/DevTools/DevToolsNetworkTest.cs b/dotnet/test/common/DevTools/DevToolsNetworkTest.cs index 408750e524df6..5ec3dd1532851 100644 --- a/dotnet/test/common/DevTools/DevToolsNetworkTest.cs +++ b/dotnet/test/common/DevTools/DevToolsNetworkTest.cs @@ -23,476 +23,475 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsNetworkTest : DevToolsTestFixture +[TestFixture] +public class DevToolsNetworkTest : DevToolsTestFixture +{ + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public void GetSetDeleteAndClearAllCookies() { - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public void GetSetDeleteAndClearAllCookies() - { - //var domains = session.GetVersionSpecificDomains(); - //await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + //var domains = session.GetVersionSpecificDomains(); + //await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - //var allCookieResponse = await domains.Network.GetAllCookies(); - //ReadOnlyCollection seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); - //Assert.That(seleniumCookies.Count == 0); + //var allCookieResponse = await domains.Network.GetAllCookies(); + //ReadOnlyCollection seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); + //Assert.That(seleniumCookies.Count == 0); - //Cookie cookie = new ReturnedCookie("name", "value", EnvironmentManager.Instance.UrlBuilder.HostName, "/devtools/test", null, false, true); - //var setCookieResponse = await domains.Network.SetCookie(cookie.ToDevToolsSetCookieCommandSettings()); + //Cookie cookie = new ReturnedCookie("name", "value", EnvironmentManager.Instance.UrlBuilder.HostName, "/devtools/test", null, false, true); + //var setCookieResponse = await domains.Network.SetCookie(cookie.ToDevToolsSetCookieCommandSettings()); - //Assert.That(setCookieResponse.Success); + //Assert.That(setCookieResponse.Success); - //allCookieResponse = await domains.Network.GetAllCookies(); - //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); - //Assert.That(seleniumCookies.Count == 1); + //allCookieResponse = await domains.Network.GetAllCookies(); + //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); + //Assert.That(seleniumCookies.Count == 1); - //var cookieResponse = await domains.Network.GetCookies(new CurrentCdpVersion.Network.GetCookiesCommandSettings()); - //seleniumCookies = cookieResponse.Cookies.ToSeleniumCookies(); - //Assert.That(seleniumCookies.Count == 0); + //var cookieResponse = await domains.Network.GetCookies(new CurrentCdpVersion.Network.GetCookiesCommandSettings()); + //seleniumCookies = cookieResponse.Cookies.ToSeleniumCookies(); + //Assert.That(seleniumCookies.Count == 0); - //await domains.Network.DeleteCookies(new CurrentCdpVersion.Network.DeleteCookiesCommandSettings() - //{ - // Name = "name", - // Domain = EnvironmentManager.Instance.UrlBuilder.HostName, - // Path = "/devtools/test" - //}); + //await domains.Network.DeleteCookies(new CurrentCdpVersion.Network.DeleteCookiesCommandSettings() + //{ + // Name = "name", + // Domain = EnvironmentManager.Instance.UrlBuilder.HostName, + // Path = "/devtools/test" + //}); - //await domains.Network.ClearBrowserCookies(); + //await domains.Network.ClearBrowserCookies(); - //allCookieResponse = await domains.Network.GetAllCookies(); - //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); - //Assert.That(seleniumCookies.Count == 0); + //allCookieResponse = await domains.Network.GetAllCookies(); + //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); + //Assert.That(seleniumCookies.Count == 0); - //setCookieResponse = await domains.Network.SetCookie(cookie.ToDevToolsSetCookieCommandSettings()); - //Assert.That(setCookieResponse.Success); + //setCookieResponse = await domains.Network.SetCookie(cookie.ToDevToolsSetCookieCommandSettings()); + //Assert.That(setCookieResponse.Success); - //allCookieResponse = await domains.Network.GetAllCookies(); - //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); - //Assert.That(seleniumCookies.Count == 1); - } + //allCookieResponse = await domains.Network.GetAllCookies(); + //seleniumCookies = allCookieResponse.Cookies.ToSeleniumCookies(); + //Assert.That(seleniumCookies.Count == 1); + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SendRequestWithUrlFiltersAndExtraHeadersAndVerifyRequests() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SendRequestWithUrlFiltersAndExtraHeadersAndVerifyRequests() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + await domains.Network.SetBlockedURLs(new CurrentCdpVersion.Network.SetBlockedURLsCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - await domains.Network.SetBlockedURLs(new CurrentCdpVersion.Network.SetBlockedURLsCommandSettings() - { - Urls = new string[] { "*://*/*.gif" } - }); + Urls = new string[] { "*://*/*.gif" } + }); - var additionalHeaders = new CurrentCdpVersion.Network.Headers(); - additionalHeaders.Add("headerName", "headerValue"); - await domains.Network.SetExtraHTTPHeaders(new CurrentCdpVersion.Network.SetExtraHTTPHeadersCommandSettings() - { - Headers = additionalHeaders - }); + var additionalHeaders = new CurrentCdpVersion.Network.Headers(); + additionalHeaders.Add("headerName", "headerValue"); + await domains.Network.SetExtraHTTPHeaders(new CurrentCdpVersion.Network.SetExtraHTTPHeadersCommandSettings() + { + Headers = additionalHeaders + }); - ManualResetEventSlim loadingFailedSync = new ManualResetEventSlim(false); - EventHandler loadingFailedHandler = (sender, e) => + ManualResetEventSlim loadingFailedSync = new ManualResetEventSlim(false); + EventHandler loadingFailedHandler = (sender, e) => + { + if (e.Type == CurrentCdpVersion.Network.ResourceType.Image) { - if (e.Type == CurrentCdpVersion.Network.ResourceType.Image) - { - Assert.That(e.BlockedReason == CurrentCdpVersion.Network.BlockedReason.Inspector); - } - - loadingFailedSync.Set(); - }; - domains.Network.LoadingFailed += loadingFailedHandler; + Assert.That(e.BlockedReason == CurrentCdpVersion.Network.BlockedReason.Inspector); + } - ManualResetEventSlim requestSentSync = new ManualResetEventSlim(false); - EventHandler requestWillBeSentHandler = (sender, e) => - { - if (e.Type != CurrentCdpVersion.Network.ResourceType.Image) - { - Assert.That(e.Request.Headers.ContainsKey("headerName")); - Assert.That(e.Request.Headers["headerName"] == "headerValue"); - requestSentSync.Set(); - } - }; - domains.Network.RequestWillBeSent += requestWillBeSentHandler; - - ManualResetEventSlim dataSync = new ManualResetEventSlim(false); - EventHandler dataReceivedHandler = (sender, e) => - { - Assert.That(e.RequestId, Is.Not.Null); - dataSync.Set(); - }; - domains.Network.DataReceived += dataReceivedHandler; - - driver.Url = linkedImage; - Assert.That(loadingFailedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - Assert.That(requestSentSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - Assert.That(dataSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - } + loadingFailedSync.Set(); + }; + domains.Network.LoadingFailed += loadingFailedHandler; - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task EmulateNetworkConditionOffline() + ManualResetEventSlim requestSentSync = new ManualResetEventSlim(false); + EventHandler requestWillBeSentHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() + if (e.Type != CurrentCdpVersion.Network.ResourceType.Image) { - MaxTotalBufferSize = 100000000 - }); + Assert.That(e.Request.Headers.ContainsKey("headerName")); + Assert.That(e.Request.Headers["headerName"] == "headerValue"); + requestSentSync.Set(); + } + }; + domains.Network.RequestWillBeSent += requestWillBeSentHandler; - await domains.Network.EmulateNetworkConditions(new CurrentCdpVersion.Network.EmulateNetworkConditionsCommandSettings() - { - Offline = true, - Latency = 100, - DownloadThroughput = 1000, - UploadThroughput = 2000, - ConnectionType = CurrentCdpVersion.Network.ConnectionType.Cellular3g - }); + ManualResetEventSlim dataSync = new ManualResetEventSlim(false); + EventHandler dataReceivedHandler = (sender, e) => + { + Assert.That(e.RequestId, Is.Not.Null); + dataSync.Set(); + }; + domains.Network.DataReceived += dataReceivedHandler; + + driver.Url = linkedImage; + Assert.That(loadingFailedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + Assert.That(requestSentSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + Assert.That(dataSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + } - ManualResetEventSlim loadingFailedSync = new ManualResetEventSlim(false); - EventHandler loadingFailedHandler = (sender, e) => - { - Assert.That(e.ErrorText, Is.EqualTo("net::ERR_INTERNET_DISCONNECTED")); - loadingFailedSync.Set(); - }; - domains.Network.LoadingFailed += loadingFailedHandler; + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task EmulateNetworkConditionOffline() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() + { + MaxTotalBufferSize = 100000000 + }); - try - { - driver.Url = simpleTestPage; - } - catch (WebDriverException e) - { - Assert.That(e.Message.Contains("net::ERR_INTERNET_DISCONNECTED")); - } + await domains.Network.EmulateNetworkConditions(new CurrentCdpVersion.Network.EmulateNetworkConditionsCommandSettings() + { + Offline = true, + Latency = 100, + DownloadThroughput = 1000, + UploadThroughput = 2000, + ConnectionType = CurrentCdpVersion.Network.ConnectionType.Cellular3g + }); + + ManualResetEventSlim loadingFailedSync = new ManualResetEventSlim(false); + EventHandler loadingFailedHandler = (sender, e) => + { + Assert.That(e.ErrorText, Is.EqualTo("net::ERR_INTERNET_DISCONNECTED")); + loadingFailedSync.Set(); + }; + domains.Network.LoadingFailed += loadingFailedHandler; - Assert.That(loadingFailedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + try + { + driver.Url = simpleTestPage; } - - [Test] - [Ignore("The request ID is not getting added to cache")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyRequestReceivedFromCacheAndResponseBody() + catch (WebDriverException e) { - var domains = session.GetVersionSpecificDomains(); - string[] requestIdFromCache = new string[1]; + Assert.That(e.Message.Contains("net::ERR_INTERNET_DISCONNECTED")); + } - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() - { - MaxResourceBufferSize = 100000000 - }); + Assert.That(loadingFailedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + } - ManualResetEventSlim servedFromCacheSync = new ManualResetEventSlim(false); - EventHandler requestServedFromCacheHandler = (sender, e) => - { - Assert.That(e.RequestId, Is.Not.Null); - requestIdFromCache[0] = e.RequestId; - servedFromCacheSync.Set(); - }; - domains.Network.RequestServedFromCache += requestServedFromCacheHandler; - - ManualResetEventSlim loadingFinishedSync = new ManualResetEventSlim(false); - EventHandler loadingFinishedHandler = (sender, e) => - { - Assert.That(e.RequestId, Is.Not.Null); - loadingFinishedSync.Set(); - }; - domains.Network.LoadingFinished += loadingFinishedHandler; + [Test] + [Ignore("The request ID is not getting added to cache")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyRequestReceivedFromCacheAndResponseBody() + { + var domains = session.GetVersionSpecificDomains(); + string[] requestIdFromCache = new string[1]; - driver.Url = simpleTestPage; - driver.Url = simpleTestPage; - Assert.That(loadingFinishedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - Assert.That(servedFromCacheSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() + { + MaxResourceBufferSize = 100000000 + }); - var responseBody = await domains.Network.GetResponseBody(new CurrentCdpVersion.Network.GetResponseBodyCommandSettings() - { - RequestId = requestIdFromCache[0] - }); + ManualResetEventSlim servedFromCacheSync = new ManualResetEventSlim(false); + EventHandler requestServedFromCacheHandler = (sender, e) => + { + Assert.That(e.RequestId, Is.Not.Null); + requestIdFromCache[0] = e.RequestId; + servedFromCacheSync.Set(); + }; + domains.Network.RequestServedFromCache += requestServedFromCacheHandler; + + ManualResetEventSlim loadingFinishedSync = new ManualResetEventSlim(false); + EventHandler loadingFinishedHandler = (sender, e) => + { + Assert.That(e.RequestId, Is.Not.Null); + loadingFinishedSync.Set(); + }; + domains.Network.LoadingFinished += loadingFinishedHandler; - Assert.That(responseBody.Body, Is.Not.Null); - } + driver.Url = simpleTestPage; + driver.Url = simpleTestPage; + Assert.That(loadingFinishedSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + Assert.That(servedFromCacheSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifySearchInResponseBody() + var responseBody = await domains.Network.GetResponseBody(new CurrentCdpVersion.Network.GetResponseBodyCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - string[] requestIds = new string[1]; + RequestId = requestIdFromCache[0] + }); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() - { - MaxResourceBufferSize = 100000000 - }); + Assert.That(responseBody.Body, Is.Not.Null); + } - ManualResetEventSlim responseSync = new ManualResetEventSlim(false); - EventHandler responseReceivedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - requestIds[0] = e.RequestId; - responseSync.Set(); - }; - domains.Network.ResponseReceived += responseReceivedHandler; + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifySearchInResponseBody() + { + var domains = session.GetVersionSpecificDomains(); + string[] requestIds = new string[1]; - driver.Url = simpleTestPage; - Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() + { + MaxResourceBufferSize = 100000000 + }); - var searchResponse = await domains.Network.SearchInResponseBody(new CurrentCdpVersion.Network.SearchInResponseBodyCommandSettings() - { - RequestId = requestIds[0], - Query = ".*", - IsRegex = true - }); + ManualResetEventSlim responseSync = new ManualResetEventSlim(false); + EventHandler responseReceivedHandler = (sender, e) => + { + Assert.That(e, Is.Not.Null); + requestIds[0] = e.RequestId; + responseSync.Set(); + }; + domains.Network.ResponseReceived += responseReceivedHandler; - Assert.That(searchResponse.Result.Length > 0); - } + driver.Url = simpleTestPage; + Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyCacheDisabledAndClearCache() + var searchResponse = await domains.Network.SearchInResponseBody(new CurrentCdpVersion.Network.SearchInResponseBodyCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() - { - MaxPostDataSize = 100000000 - }); + RequestId = requestIds[0], + Query = ".*", + IsRegex = true + }); - ManualResetEventSlim responseSync = new ManualResetEventSlim(false); - EventHandler responseReceivedHandler = (sender, e) => - { - Assert.That(e.Response.FromDiskCache, Is.False); - responseSync.Set(); - }; - domains.Network.ResponseReceived += responseReceivedHandler; + Assert.That(searchResponse.Result.Length > 0); + } - driver.Url = simpleTestPage; - Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyCacheDisabledAndClearCache() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings() + { + MaxPostDataSize = 100000000 + }); - await domains.Network.SetCacheDisabled(new CurrentCdpVersion.Network.SetCacheDisabledCommandSettings() - { - CacheDisabled = true - }); + ManualResetEventSlim responseSync = new ManualResetEventSlim(false); + EventHandler responseReceivedHandler = (sender, e) => + { + Assert.That(e.Response.FromDiskCache, Is.False); + responseSync.Set(); + }; + domains.Network.ResponseReceived += responseReceivedHandler; - driver.Url = simpleTestPage; - await domains.Network.ClearBrowserCache(); - } + driver.Url = simpleTestPage; + Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - [Test] - [Ignore("Unable to open secure url")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyCertificatesAndOverrideUserAgent() + await domains.Network.SetCacheDisabled(new CurrentCdpVersion.Network.SetCacheDisabledCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + CacheDisabled = true + }); - await domains.Network.SetUserAgentOverride(new CurrentCdpVersion.Network.SetUserAgentOverrideCommandSettings() - { - UserAgent = "userAgent" - }); + driver.Url = simpleTestPage; + await domains.Network.ClearBrowserCache(); + } - ManualResetEventSlim requestSync = new ManualResetEventSlim(false); - EventHandler requestWillBeSentHandler = (sender, e) => - { - Assert.That(e.Request.Headers["User-Agent"], Is.EqualTo("userAgent")); - requestSync.Set(); - }; - domains.Network.RequestWillBeSent += requestWillBeSentHandler; + [Test] + [Ignore("Unable to open secure url")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyCertificatesAndOverrideUserAgent() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - string origin = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("simpleTest.html"); - driver.Url = origin; - Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + await domains.Network.SetUserAgentOverride(new CurrentCdpVersion.Network.SetUserAgentOverrideCommandSettings() + { + UserAgent = "userAgent" + }); - var result = await domains.Network.GetCertificate(new CurrentCdpVersion.Network.GetCertificateCommandSettings() - { - Origin = origin - }); + ManualResetEventSlim requestSync = new ManualResetEventSlim(false); + EventHandler requestWillBeSentHandler = (sender, e) => + { + Assert.That(e.Request.Headers["User-Agent"], Is.EqualTo("userAgent")); + requestSync.Set(); + }; + domains.Network.RequestWillBeSent += requestWillBeSentHandler; - Assert.That(result.TableNames.Length, Is.GreaterThan(0)); - } + string origin = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("simpleTest.html"); + driver.Url = origin; + Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyResponseReceivedEventAndNetworkDisable() + var result = await domains.Network.GetCertificate(new CurrentCdpVersion.Network.GetCertificateCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - ManualResetEventSlim responseSync = new ManualResetEventSlim(false); - EventHandler responseReceivedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - responseSync.Set(); - }; - domains.Network.ResponseReceived += responseReceivedHandler; + Origin = origin + }); - driver.Url = simpleTestPage; - Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - await domains.Network.Disable(); - } + Assert.That(result.TableNames.Length, Is.GreaterThan(0)); + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyWebSocketOperations() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyResponseReceivedEventAndNetworkDisable() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + ManualResetEventSlim responseSync = new ManualResetEventSlim(false); + EventHandler responseReceivedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - - EventHandler webSocketCreatedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - }; - domains.Network.WebSocketCreated += webSocketCreatedHandler; + Assert.That(e, Is.Not.Null); + responseSync.Set(); + }; + domains.Network.ResponseReceived += responseReceivedHandler; + + driver.Url = simpleTestPage; + Assert.That(responseSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + await domains.Network.Disable(); + } - EventHandler webSocketFrameReceivedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - }; - domains.Network.WebSocketFrameReceived += webSocketFrameReceivedHandler; + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyWebSocketOperations() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - EventHandler webSocketFrameErrorHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - }; - domains.Network.WebSocketFrameError += webSocketFrameErrorHandler; + EventHandler webSocketCreatedHandler = (sender, e) => + { + Assert.That(e, Is.Not.Null); + }; + domains.Network.WebSocketCreated += webSocketCreatedHandler; - EventHandler webSocketFrameSentHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - }; - domains.Network.WebSocketFrameSent += webSocketFrameSentHandler; + EventHandler webSocketFrameReceivedHandler = (sender, e) => + { + Assert.That(e, Is.Not.Null); + }; + domains.Network.WebSocketFrameReceived += webSocketFrameReceivedHandler; - EventHandler webSocketClosedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - }; - domains.Network.WebSocketClosed += webSocketClosedHandler; + EventHandler webSocketFrameErrorHandler = (sender, e) => + { + Assert.That(e, Is.Not.Null); + }; + domains.Network.WebSocketFrameError += webSocketFrameErrorHandler; - driver.Url = simpleTestPage; - } + EventHandler webSocketFrameSentHandler = (sender, e) => + { + Assert.That(e, Is.Not.Null); + }; + domains.Network.WebSocketFrameSent += webSocketFrameSentHandler; - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifyRequestPostData() + EventHandler webSocketClosedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + Assert.That(e, Is.Not.Null); + }; + domains.Network.WebSocketClosed += webSocketClosedHandler; - string[] requestIds = new string[1]; + driver.Url = simpleTestPage; + } - ManualResetEventSlim requestSync = new ManualResetEventSlim(false); - EventHandler requestWillBeSentHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - if (string.Compare(e.Request.Method, "post", StringComparison.OrdinalIgnoreCase) == 0) - { - requestIds[0] = e.RequestId; - requestSync.Set(); - } - }; - - domains.Network.RequestWillBeSent += requestWillBeSentHandler; - - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("postForm.html"); - driver.FindElement(By.XPath("//form/input")).Click(); - bool requestEventFired = requestSync.Wait(TimeSpan.FromSeconds(5)); - Assert.That(requestEventFired, Is.True); - - var response = await domains.Network.GetRequestPostData(new CurrentCdpVersion.Network.GetRequestPostDataCommandSettings() - { - RequestId = requestIds[0] - }); + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifyRequestPostData() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - Assert.That(response.PostData, Is.Not.Null); - } + string[] requestIds = new string[1]; - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task ByPassServiceWorker() + ManualResetEventSlim requestSync = new ManualResetEventSlim(false); + EventHandler requestWillBeSentHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - await domains.Network.SetBypassServiceWorker(new CurrentCdpVersion.Network.SetBypassServiceWorkerCommandSettings() + Assert.That(e, Is.Not.Null); + if (string.Compare(e.Request.Method, "post", StringComparison.OrdinalIgnoreCase) == 0) { - Bypass = true - }); - } + requestIds[0] = e.RequestId; + requestSync.Set(); + } + }; + + domains.Network.RequestWillBeSent += requestWillBeSentHandler; + + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("postForm.html"); + driver.FindElement(By.XPath("//form/input")).Click(); + bool requestEventFired = requestSync.Wait(TimeSpan.FromSeconds(5)); + Assert.That(requestEventFired, Is.True); - [Test] - [Ignore("Unable to open secure url")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task VerifySignedExchangeReceived() + var response = await domains.Network.GetRequestPostData(new CurrentCdpVersion.Network.GetRequestPostDataCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + RequestId = requestIds[0] + }); - ManualResetEventSlim requestSync = new ManualResetEventSlim(false); - EventHandler signedExchangeReceivedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - requestSync.Set(); - }; - domains.Network.SignedExchangeReceived += signedExchangeReceivedHandler; + Assert.That(response.PostData, Is.Not.Null); + } - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("simpleTest.html"); - Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - } + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task ByPassServiceWorker() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + await domains.Network.SetBypassServiceWorker(new CurrentCdpVersion.Network.SetBypassServiceWorkerCommandSettings() + { + Bypass = true + }); + } + + [Test] + [Ignore("Unable to open secure url")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task VerifySignedExchangeReceived() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task InterceptRequestAndContinue() + ManualResetEventSlim requestSync = new ManualResetEventSlim(false); + EventHandler signedExchangeReceivedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); + Assert.That(e, Is.Not.Null); + requestSync.Set(); + }; + domains.Network.SignedExchangeReceived += signedExchangeReceivedHandler; - ManualResetEventSlim requestSync = new ManualResetEventSlim(false); - EventHandler requestInterceptedHandler = (async (sender, e) => - { - await domains.Network.ContinueInterceptedRequest(new CurrentCdpVersion.Network.ContinueInterceptedRequestCommandSettings() - { - InterceptionId = e.InterceptionId - }); - requestSync.Set(); - }); - domains.Network.RequestIntercepted += requestInterceptedHandler; + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIsSecure("simpleTest.html"); + Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); + } - var pattern = new CurrentCdpVersion.Network.RequestPattern() - { - UrlPattern = "*.css", - InterceptionStage = CurrentCdpVersion.Network.InterceptionStage.HeadersReceived - }; + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task InterceptRequestAndContinue() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings()); - await domains.Network.SetRequestInterception(new CurrentCdpVersion.Network.SetRequestInterceptionCommandSettings() + ManualResetEventSlim requestSync = new ManualResetEventSlim(false); + EventHandler requestInterceptedHandler = (async (sender, e) => + { + await domains.Network.ContinueInterceptedRequest(new CurrentCdpVersion.Network.ContinueInterceptedRequestCommandSettings() { - Patterns = new CurrentCdpVersion.Network.RequestPattern[] { pattern } + InterceptionId = e.InterceptionId }); + requestSync.Set(); + }); + domains.Network.RequestIntercepted += requestInterceptedHandler; - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("js/skins/lightgray/content.min.css"); - Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); - } + var pattern = new CurrentCdpVersion.Network.RequestPattern() + { + UrlPattern = "*.css", + InterceptionStage = CurrentCdpVersion.Network.InterceptionStage.HeadersReceived + }; + + await domains.Network.SetRequestInterception(new CurrentCdpVersion.Network.SetRequestInterceptionCommandSettings() + { + Patterns = new CurrentCdpVersion.Network.RequestPattern[] { pattern } + }); + + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("js/skins/lightgray/content.min.css"); + Assert.That(requestSync.Wait(TimeSpan.FromSeconds(5)), Is.True); } } diff --git a/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs b/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs index 464b51acea71e..eee69d1c5c41b 100644 --- a/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs +++ b/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs @@ -20,111 +20,110 @@ using NUnit.Framework; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsPerformanceTest : DevToolsTestFixture +[TestFixture] +public class DevToolsPerformanceTest : DevToolsTestFixture +{ + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task EnableAndDisablePerformance() { - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task EnableAndDisablePerformance() - { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); - driver.Url = simpleTestPage; - await domains.Performance.Disable(); - } + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); + driver.Url = simpleTestPage; + await domains.Performance.Disable(); + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task DisablePerformance() - { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.Disable(); - driver.Url = simpleTestPage; - await domains.Performance.Disable(); - } + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task DisablePerformance() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.Disable(); + driver.Url = simpleTestPage; + await domains.Performance.Disable(); + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SetTimeDomainTimeTickPerformance() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SetTimeDomainTimeTickPerformance() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.Disable(); + await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.Disable(); - await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() - { - TimeDomain = "timeTicks" - }); - await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); - driver.Url = simpleTestPage; - await domains.Performance.Disable(); - } + TimeDomain = "timeTicks" + }); + await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); + driver.Url = simpleTestPage; + await domains.Performance.Disable(); + } - [Test] - [IgnorePlatform("Windows", "Thread time is not supported on this platform")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SetTimeDomainsThreadTicksPerformance() + [Test] + [IgnorePlatform("Windows", "Thread time is not supported on this platform")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SetTimeDomainsThreadTicksPerformance() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.Disable(); + await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.Disable(); - await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() - { - TimeDomain = "threadTicks" - }); - await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); - driver.Url = simpleTestPage; - await domains.Performance.Disable(); - } + TimeDomain = "threadTicks" + }); + await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); + driver.Url = simpleTestPage; + await domains.Performance.Disable(); + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task GetMetricsByTimeTicks() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task GetMetricsByTimeTicks() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() - { - TimeDomain = "timeTicks" - }); - await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); - driver.Url = simpleTestPage; - var response = await domains.Performance.GetMetrics(); - var metrics = response.Metrics; - Assert.That(metrics, Is.Not.Null); - Assert.That(metrics.Length, Is.GreaterThan(0)); - await domains.Performance.Disable(); - } + TimeDomain = "timeTicks" + }); + await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); + driver.Url = simpleTestPage; + var response = await domains.Performance.GetMetrics(); + var metrics = response.Metrics; + Assert.That(metrics, Is.Not.Null); + Assert.That(metrics.Length, Is.GreaterThan(0)); + await domains.Performance.Disable(); + } - [Test] - [IgnorePlatform("Windows", "Thread time is not supported on this platform")] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task GetMetricsByThreadTicks() + [Test] + [IgnorePlatform("Windows", "Thread time is not supported on this platform")] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task GetMetricsByThreadTicks() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.SetTimeDomain(new CurrentCdpVersion.Performance.SetTimeDomainCommandSettings() - { - TimeDomain = "threadTicks" - }); - await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); - driver.Url = simpleTestPage; - var response = await domains.Performance.GetMetrics(); - var metrics = response.Metrics; - Assert.That(metrics, Is.Not.Null); - Assert.That(metrics.Length, Is.GreaterThan(0)); - await domains.Performance.Disable(); - } + TimeDomain = "threadTicks" + }); + await domains.Performance.Enable(new CurrentCdpVersion.Performance.EnableCommandSettings()); + driver.Url = simpleTestPage; + var response = await domains.Performance.GetMetrics(); + var metrics = response.Metrics; + Assert.That(metrics, Is.Not.Null); + Assert.That(metrics.Length, Is.GreaterThan(0)); + await domains.Performance.Disable(); } } diff --git a/dotnet/test/common/DevTools/DevToolsProfilerTest.cs b/dotnet/test/common/DevTools/DevToolsProfilerTest.cs index 47f2241bf84b6..b1546f024e1af 100644 --- a/dotnet/test/common/DevTools/DevToolsProfilerTest.cs +++ b/dotnet/test/common/DevTools/DevToolsProfilerTest.cs @@ -22,128 +22,127 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; + +[TestFixture] +public class DevToolsProfilerTest : DevToolsTestFixture { - using CurrentCdpVersion = V135; + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SimpleStartStopAndGetProfilerTest() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Profiler.Enable(); + await domains.Profiler.Start(); + var response = await domains.Profiler.Stop(); + var profiler = response.Profile; + ValidateProfile(profiler); + await domains.Profiler.Disable(); + } - [TestFixture] - public class DevToolsProfilerTest : DevToolsTestFixture + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SampleGetBestEffortProfilerTest() { - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SimpleStartStopAndGetProfilerTest() + var domains = session.GetVersionSpecificDomains(); + await domains.Profiler.Enable(); + driver.Url = simpleTestPage; + await domains.Profiler.SetSamplingInterval(new CurrentCdpVersion.Profiler.SetSamplingIntervalCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Profiler.Enable(); - await domains.Profiler.Start(); - var response = await domains.Profiler.Stop(); - var profiler = response.Profile; - ValidateProfile(profiler); - await domains.Profiler.Disable(); - } + Interval = 30 + }); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SampleGetBestEffortProfilerTest() + var response = await domains.Profiler.GetBestEffortCoverage(); + var bestEffort = response.Result; + Assert.That(bestEffort, Is.Not.Null); + Assert.That(bestEffort.Length, Is.GreaterThan(0)); + await domains.Profiler.Disable(); + } + + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SampleSetStartPreciseCoverageTest() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Profiler.Enable(); + driver.Url = simpleTestPage; + await domains.Profiler.StartPreciseCoverage(new CurrentCdpVersion.Profiler.StartPreciseCoverageCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Profiler.Enable(); - driver.Url = simpleTestPage; - await domains.Profiler.SetSamplingInterval(new CurrentCdpVersion.Profiler.SetSamplingIntervalCommandSettings() - { - Interval = 30 - }); + CallCount = true, + Detailed = true + }); + await domains.Profiler.Start(); + var coverageResponse = await domains.Profiler.TakePreciseCoverage(); + var pc = coverageResponse.Result; + Assert.That(pc, Is.Not.Null); + var response = await domains.Profiler.Stop(); + var profiler = response.Profile; + ValidateProfile(profiler); + await domains.Profiler.Disable(); + } - var response = await domains.Profiler.GetBestEffortCoverage(); - var bestEffort = response.Result; - Assert.That(bestEffort, Is.Not.Null); - Assert.That(bestEffort.Length, Is.GreaterThan(0)); - await domains.Profiler.Disable(); - } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SampleSetStartPreciseCoverageTest() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task SampleProfileEvents() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Profiler.Enable(); + driver.Url = simpleTestPage; + ManualResetEventSlim startSync = new ManualResetEventSlim(false); + EventHandler consoleProfileStartedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Profiler.Enable(); - driver.Url = simpleTestPage; - await domains.Profiler.StartPreciseCoverage(new CurrentCdpVersion.Profiler.StartPreciseCoverageCommandSettings() - { - CallCount = true, - Detailed = true - }); - await domains.Profiler.Start(); - var coverageResponse = await domains.Profiler.TakePreciseCoverage(); - var pc = coverageResponse.Result; - Assert.That(pc, Is.Not.Null); - var response = await domains.Profiler.Stop(); - var profiler = response.Profile; - ValidateProfile(profiler); - await domains.Profiler.Disable(); - } + Assert.That(e, Is.Not.Null); + startSync.Set(); + }; + domains.Profiler.ConsoleProfileStarted += consoleProfileStartedHandler; + await domains.Profiler.Start(); + startSync.Wait(TimeSpan.FromSeconds(5)); + driver.Navigate().Refresh(); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task SampleProfileEvents() + ManualResetEventSlim finishSync = new ManualResetEventSlim(false); + EventHandler consoleProfileFinishedHandler = (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - await domains.Profiler.Enable(); - driver.Url = simpleTestPage; - ManualResetEventSlim startSync = new ManualResetEventSlim(false); - EventHandler consoleProfileStartedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - startSync.Set(); - }; - domains.Profiler.ConsoleProfileStarted += consoleProfileStartedHandler; - - await domains.Profiler.Start(); - startSync.Wait(TimeSpan.FromSeconds(5)); - driver.Navigate().Refresh(); + Assert.That(e, Is.Not.Null); + finishSync.Set(); + }; + domains.Profiler.ConsoleProfileFinished += consoleProfileFinishedHandler; - ManualResetEventSlim finishSync = new ManualResetEventSlim(false); - EventHandler consoleProfileFinishedHandler = (sender, e) => - { - Assert.That(e, Is.Not.Null); - finishSync.Set(); - }; - domains.Profiler.ConsoleProfileFinished += consoleProfileFinishedHandler; + var response = await domains.Profiler.Stop(); + finishSync.Wait(TimeSpan.FromSeconds(5)); - var response = await domains.Profiler.Stop(); - finishSync.Wait(TimeSpan.FromSeconds(5)); + var profiler = response.Profile; + ValidateProfile(profiler); + await domains.Profiler.Disable(); + } - var profiler = response.Profile; - ValidateProfile(profiler); - await domains.Profiler.Disable(); + private void ValidateProfile(CurrentCdpVersion.Profiler.Profile profiler) + { + Assert.That(profiler, Is.Not.Null); + Assert.That(profiler.Nodes, Is.Not.Null); + Assert.That(profiler.StartTime, Is.Not.Zero); + Assert.That(profiler.EndTime, Is.Not.Zero); + Assert.That(profiler.TimeDeltas, Is.Not.Null); + foreach (var delta in profiler.TimeDeltas) + { + Assert.That(delta, Is.Not.Zero); } - private void ValidateProfile(CurrentCdpVersion.Profiler.Profile profiler) + foreach (var node in profiler.Nodes) { - Assert.That(profiler, Is.Not.Null); - Assert.That(profiler.Nodes, Is.Not.Null); - Assert.That(profiler.StartTime, Is.Not.Zero); - Assert.That(profiler.EndTime, Is.Not.Zero); - Assert.That(profiler.TimeDeltas, Is.Not.Null); - foreach (var delta in profiler.TimeDeltas) - { - Assert.That(delta, Is.Not.Zero); - } - - foreach (var node in profiler.Nodes) - { - Assert.That(node, Is.Not.Null); - Assert.That(node.CallFrame, Is.Not.Null); - } + Assert.That(node, Is.Not.Null); + Assert.That(node.CallFrame, Is.Not.Null); } } } diff --git a/dotnet/test/common/DevTools/DevToolsSecurityTest.cs b/dotnet/test/common/DevTools/DevToolsSecurityTest.cs index 397657de4edd8..f7ee6f3b7253e 100644 --- a/dotnet/test/common/DevTools/DevToolsSecurityTest.cs +++ b/dotnet/test/common/DevTools/DevToolsSecurityTest.cs @@ -23,61 +23,60 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsSecurityTest : DevToolsTestFixture +[TestFixture] +public class DevToolsSecurityTest : DevToolsTestFixture +{ + //[Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task LoadInsecureWebsite() { - //[Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task LoadInsecureWebsite() + var domains = session.GetVersionSpecificDomains(); + await domains.Security.Enable(); + + await domains.Security.SetIgnoreCertificateErrors(new CurrentCdpVersion.Security.SetIgnoreCertificateErrorsCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Security.Enable(); + Ignore = false + }); - await domains.Security.SetIgnoreCertificateErrors(new CurrentCdpVersion.Security.SetIgnoreCertificateErrorsCommandSettings() - { - Ignore = false - }); + string summary = null; + ManualResetEventSlim sync = new ManualResetEventSlim(false); + EventHandler securityStateChangedHandler = (sender, e) => + { + summary = e.Summary; + sync.Set(); + }; + domains.Security.SecurityStateChanged += securityStateChangedHandler; - string summary = null; - ManualResetEventSlim sync = new ManualResetEventSlim(false); - EventHandler securityStateChangedHandler = (sender, e) => - { - summary = e.Summary; - sync.Set(); - }; - domains.Security.SecurityStateChanged += securityStateChangedHandler; + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsSecurityTest"); + sync.Wait(TimeSpan.FromSeconds(5)); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsSecurityTest"); - sync.Wait(TimeSpan.FromSeconds(5)); + await domains.Security.Disable(); - await domains.Security.Disable(); + Assert.That(driver.PageSource, Contains.Substring("Security Test")); + Assert.That(summary, Contains.Substring("This page has a non-HTTPS secure origin")); + } - Assert.That(driver.PageSource, Contains.Substring("Security Test")); - Assert.That(summary, Contains.Substring("This page has a non-HTTPS secure origin")); - } + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task LoadSecureWebsite() + { + var domains = session.GetVersionSpecificDomains(); + await domains.Security.Enable(); - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task LoadSecureWebsite() + await domains.Security.SetIgnoreCertificateErrors(new CurrentCdpVersion.Security.SetIgnoreCertificateErrorsCommandSettings() { - var domains = session.GetVersionSpecificDomains(); - await domains.Security.Enable(); - - await domains.Security.SetIgnoreCertificateErrors(new CurrentCdpVersion.Security.SetIgnoreCertificateErrorsCommandSettings() - { - Ignore = true - }); + Ignore = true + }); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsSecurityTest"); - Assert.That(driver.PageSource, Contains.Substring("Security Test")); - } + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsSecurityTest"); + Assert.That(driver.PageSource, Contains.Substring("Security Test")); } } diff --git a/dotnet/test/common/DevTools/DevToolsTabsTest.cs b/dotnet/test/common/DevTools/DevToolsTabsTest.cs index a6e498903b1ca..b94e418a87aad 100644 --- a/dotnet/test/common/DevTools/DevToolsTabsTest.cs +++ b/dotnet/test/common/DevTools/DevToolsTabsTest.cs @@ -20,33 +20,32 @@ using NUnit.Framework; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +using CurrentCdpVersion = V135; + +[TestFixture] +public class DevToolsTabsTest : DevToolsTestFixture { - using CurrentCdpVersion = V135; - [TestFixture] - public class DevToolsTabsTest : DevToolsTestFixture + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task ClosingTabDoesNotBreakDevToolsSession() { - - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task ClosingTabDoesNotBreakDevToolsSession() - { - var domains = session.GetVersionSpecificDomains(); - await domains.Console.Enable(); - var oldWindowHandle = driver.CurrentWindowHandle; - driver.SwitchTo().NewWindow(WindowType.Tab); - driver.SwitchTo().Window(oldWindowHandle); - driver.Close(); - Assert.That( - async () => - { - await domains.Console.Enable(); - }, - Throws.Nothing - ); - } + var domains = session.GetVersionSpecificDomains(); + await domains.Console.Enable(); + var oldWindowHandle = driver.CurrentWindowHandle; + driver.SwitchTo().NewWindow(WindowType.Tab); + driver.SwitchTo().Window(oldWindowHandle); + driver.Close(); + Assert.That( + async () => + { + await domains.Console.Enable(); + }, + Throws.Nothing + ); } } diff --git a/dotnet/test/common/DevTools/DevToolsTargetTest.cs b/dotnet/test/common/DevTools/DevToolsTargetTest.cs index 171ad859a2c05..ab3a91e433082 100644 --- a/dotnet/test/common/DevTools/DevToolsTargetTest.cs +++ b/dotnet/test/common/DevTools/DevToolsTargetTest.cs @@ -23,190 +23,189 @@ using System.Threading; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools -{ - using CurrentCdpVersion = V135; +namespace OpenQA.Selenium.DevTools; - [TestFixture] - public class DevToolsTargetTest : DevToolsTestFixture - { - private int id = 135; +using CurrentCdpVersion = V135; - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task GetTargetActivateAndAttach() - { - var domains = session.GetVersionSpecificDomains(); - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); - var response = await domains.Target.GetTargets(new CurrentCdpVersion.Target.GetTargetsCommandSettings()); - CurrentCdpVersion.Target.TargetInfo[] allTargets = response.TargetInfos; - foreach (CurrentCdpVersion.Target.TargetInfo targetInfo in allTargets) - { - ValidateTarget(targetInfo); - await domains.Target.ActivateTarget(new CurrentCdpVersion.Target.ActivateTargetCommandSettings() - { - TargetId = targetInfo.TargetId - }); - var attachResponse = await domains.Target.AttachToTarget(new CurrentCdpVersion.Target.AttachToTargetCommandSettings() - { - TargetId = targetInfo.TargetId, - Flatten = true - }); - ValidateSession(attachResponse.SessionId); - var getInfoResponse = await domains.Target.GetTargetInfo(new CurrentCdpVersion.Target.GetTargetInfoCommandSettings() - { - TargetId = targetInfo.TargetId - }); - ValidateTargetInfo(getInfoResponse.TargetInfo); - } - } +[TestFixture] +public class DevToolsTargetTest : DevToolsTestFixture +{ + private int id = 135; - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task GetTargetAndSendMessageToTarget() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task GetTargetActivateAndAttach() + { + var domains = session.GetVersionSpecificDomains(); + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); + var response = await domains.Target.GetTargets(new CurrentCdpVersion.Target.GetTargetsCommandSettings()); + CurrentCdpVersion.Target.TargetInfo[] allTargets = response.TargetInfos; + foreach (CurrentCdpVersion.Target.TargetInfo targetInfo in allTargets) { - var domains = session.GetVersionSpecificDomains(); - CurrentCdpVersion.Target.TargetInfo[] allTargets = null; - string sessionId = null; - CurrentCdpVersion.Target.TargetInfo targetInfo = null; - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); - ManualResetEventSlim sync = new ManualResetEventSlim(false); - domains.Target.ReceivedMessageFromTarget += (sender, e) => - { - ValidateMessage(e); - sync.Set(); - }; - var targetsResponse = await domains.Target.GetTargets(new CurrentCdpVersion.Target.GetTargetsCommandSettings()); - allTargets = targetsResponse.TargetInfos; - ValidateTargetsInfos(allTargets); - ValidateTarget(allTargets[0]); - targetInfo = allTargets[0]; + ValidateTarget(targetInfo); await domains.Target.ActivateTarget(new CurrentCdpVersion.Target.ActivateTargetCommandSettings() { TargetId = targetInfo.TargetId }); - var attachResponse = await domains.Target.AttachToTarget(new CurrentCdpVersion.Target.AttachToTargetCommandSettings() { TargetId = targetInfo.TargetId, - Flatten = false + Flatten = true }); - sessionId = attachResponse.SessionId; - ValidateSession(sessionId); - await domains.Target.SendMessageToTarget(new CurrentCdpVersion.Target.SendMessageToTargetCommandSettings() + ValidateSession(attachResponse.SessionId); + var getInfoResponse = await domains.Target.GetTargetInfo(new CurrentCdpVersion.Target.GetTargetInfoCommandSettings() { - Message = "{\"id\":" + id + ",\"method\":\"Page.bringToFront\"}", - SessionId = sessionId, TargetId = targetInfo.TargetId }); - sync.Wait(TimeSpan.FromSeconds(5)); + ValidateTargetInfo(getInfoResponse.TargetInfo); } + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task CreateAndContentLifeCycle() + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task GetTargetAndSendMessageToTarget() + { + var domains = session.GetVersionSpecificDomains(); + CurrentCdpVersion.Target.TargetInfo[] allTargets = null; + string sessionId = null; + CurrentCdpVersion.Target.TargetInfo targetInfo = null; + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"); + ManualResetEventSlim sync = new ManualResetEventSlim(false); + domains.Target.ReceivedMessageFromTarget += (sender, e) => { - var domains = session.GetVersionSpecificDomains(); - EventHandler targetCreatedHandler = (sender, e) => - { - ValidateTargetInfo(e.TargetInfo); - }; - domains.Target.TargetCreated += targetCreatedHandler; - - EventHandler targetCrashedHandler = (sender, e) => - { - ValidateTargetCrashed(e); - }; - domains.Target.TargetCrashed += targetCrashedHandler; - - EventHandler targetDestroyedHandler = (sender, e) => - { - ValidateTargetId(e.TargetId); - }; - domains.Target.TargetDestroyed += targetDestroyedHandler; - - EventHandler targetInfoChangedHandler = (sender, e) => - { - ValidateTargetInfo(e.TargetInfo); - }; - domains.Target.TargetInfoChanged += targetInfoChangedHandler; - - var response = await domains.Target.CreateTarget(new CurrentCdpVersion.Target.CreateTargetCommandSettings() - { - Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"), - NewWindow = true, - Background = false - }); - - ValidateTargetId(response.TargetId); - await domains.Target.SetDiscoverTargets(new CurrentCdpVersion.Target.SetDiscoverTargetsCommandSettings() - { - Discover = true - }); - - var closeResponse = await domains.Target.CloseTarget(new CurrentCdpVersion.Target.CloseTargetCommandSettings() - { - TargetId = response.TargetId - }); + ValidateMessage(e); + sync.Set(); + }; + var targetsResponse = await domains.Target.GetTargets(new CurrentCdpVersion.Target.GetTargetsCommandSettings()); + allTargets = targetsResponse.TargetInfos; + ValidateTargetsInfos(allTargets); + ValidateTarget(allTargets[0]); + targetInfo = allTargets[0]; + await domains.Target.ActivateTarget(new CurrentCdpVersion.Target.ActivateTargetCommandSettings() + { + TargetId = targetInfo.TargetId + }); - Assert.That(closeResponse, Is.Not.Null); - Assert.That(closeResponse.Success, Is.True); - } + var attachResponse = await domains.Target.AttachToTarget(new CurrentCdpVersion.Target.AttachToTargetCommandSettings() + { + TargetId = targetInfo.TargetId, + Flatten = false + }); + sessionId = attachResponse.SessionId; + ValidateSession(sessionId); + await domains.Target.SendMessageToTarget(new CurrentCdpVersion.Target.SendMessageToTargetCommandSettings() + { + Message = "{\"id\":" + id + ",\"method\":\"Page.bringToFront\"}", + SessionId = sessionId, + TargetId = targetInfo.TargetId + }); + sync.Wait(TimeSpan.FromSeconds(5)); + } - private void ValidateTargetCrashed(CurrentCdpVersion.Target.TargetCrashedEventArgs targetCrashed) + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task CreateAndContentLifeCycle() + { + var domains = session.GetVersionSpecificDomains(); + EventHandler targetCreatedHandler = (sender, e) => { - Assert.That(targetCrashed, Is.Not.Null); - Assert.That(targetCrashed.ErrorCode, Is.Not.Null); - Assert.That(targetCrashed.Status, Is.Not.Null); - Assert.That(targetCrashed.TargetId, Is.Not.Null); - } + ValidateTargetInfo(e.TargetInfo); + }; + domains.Target.TargetCreated += targetCreatedHandler; - private void ValidateTargetId(string targetId) + EventHandler targetCrashedHandler = (sender, e) => { - Assert.That(targetId, Is.Not.Null); - } + ValidateTargetCrashed(e); + }; + domains.Target.TargetCrashed += targetCrashedHandler; - private void ValidateMessage(CurrentCdpVersion.Target.ReceivedMessageFromTargetEventArgs messageFromTarget) + EventHandler targetDestroyedHandler = (sender, e) => { - Assert.That(messageFromTarget, Is.Not.Null); - Assert.That(messageFromTarget.Message, Is.Not.Null); - Assert.That(messageFromTarget.SessionId, Is.Not.Null); - Assert.That(messageFromTarget.Message, Is.EqualTo("{\"id\":" + id + ",\"result\":{}}")); - } + ValidateTargetId(e.TargetId); + }; + domains.Target.TargetDestroyed += targetDestroyedHandler; - private void ValidateTargetInfo(CurrentCdpVersion.Target.TargetInfo targetInfo) + EventHandler targetInfoChangedHandler = (sender, e) => { - Assert.That(targetInfo, Is.Not.Null); - Assert.That(targetInfo.TargetId, Is.Not.Null); - Assert.That(targetInfo.Title, Is.Not.Null); - Assert.That(targetInfo.Type, Is.Not.Null); - Assert.That(targetInfo.Url, Is.Not.Null); - } + ValidateTargetInfo(e.TargetInfo); + }; + domains.Target.TargetInfoChanged += targetInfoChangedHandler; - private void ValidateTargetsInfos(CurrentCdpVersion.Target.TargetInfo[] targets) + var response = await domains.Target.CreateTarget(new CurrentCdpVersion.Target.CreateTargetCommandSettings() { - Assert.That(targets, Is.Not.Null); - Assert.That(targets.Length, Is.GreaterThan(0)); - } + Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("devToolsConsoleTest.html"), + NewWindow = true, + Background = false + }); - private void ValidateTarget(CurrentCdpVersion.Target.TargetInfo targetInfo) + ValidateTargetId(response.TargetId); + await domains.Target.SetDiscoverTargets(new CurrentCdpVersion.Target.SetDiscoverTargetsCommandSettings() { - Assert.That(targetInfo, Is.Not.Null); - Assert.That(targetInfo.TargetId, Is.Not.Null); - Assert.That(targetInfo.Title, Is.Not.Null); - Assert.That(targetInfo.Type, Is.Not.Null); - Assert.That(targetInfo.Url, Is.Not.Null); - } + Discover = true + }); - private void ValidateSession(string sessionId) + var closeResponse = await domains.Target.CloseTarget(new CurrentCdpVersion.Target.CloseTargetCommandSettings() { - Assert.That(sessionId, Is.Not.Null); - } + TargetId = response.TargetId + }); + + Assert.That(closeResponse, Is.Not.Null); + Assert.That(closeResponse.Success, Is.True); + } + + private void ValidateTargetCrashed(CurrentCdpVersion.Target.TargetCrashedEventArgs targetCrashed) + { + Assert.That(targetCrashed, Is.Not.Null); + Assert.That(targetCrashed.ErrorCode, Is.Not.Null); + Assert.That(targetCrashed.Status, Is.Not.Null); + Assert.That(targetCrashed.TargetId, Is.Not.Null); + } + + private void ValidateTargetId(string targetId) + { + Assert.That(targetId, Is.Not.Null); + } + + private void ValidateMessage(CurrentCdpVersion.Target.ReceivedMessageFromTargetEventArgs messageFromTarget) + { + Assert.That(messageFromTarget, Is.Not.Null); + Assert.That(messageFromTarget.Message, Is.Not.Null); + Assert.That(messageFromTarget.SessionId, Is.Not.Null); + Assert.That(messageFromTarget.Message, Is.EqualTo("{\"id\":" + id + ",\"result\":{}}")); + } + + private void ValidateTargetInfo(CurrentCdpVersion.Target.TargetInfo targetInfo) + { + Assert.That(targetInfo, Is.Not.Null); + Assert.That(targetInfo.TargetId, Is.Not.Null); + Assert.That(targetInfo.Title, Is.Not.Null); + Assert.That(targetInfo.Type, Is.Not.Null); + Assert.That(targetInfo.Url, Is.Not.Null); + } + + private void ValidateTargetsInfos(CurrentCdpVersion.Target.TargetInfo[] targets) + { + Assert.That(targets, Is.Not.Null); + Assert.That(targets.Length, Is.GreaterThan(0)); + } + + private void ValidateTarget(CurrentCdpVersion.Target.TargetInfo targetInfo) + { + Assert.That(targetInfo, Is.Not.Null); + Assert.That(targetInfo.TargetId, Is.Not.Null); + Assert.That(targetInfo.Title, Is.Not.Null); + Assert.That(targetInfo.Type, Is.Not.Null); + Assert.That(targetInfo.Url, Is.Not.Null); + } + + private void ValidateSession(string sessionId) + { + Assert.That(sessionId, Is.Not.Null); } } diff --git a/dotnet/test/common/DevTools/DevToolsTestFixture.cs b/dotnet/test/common/DevTools/DevToolsTestFixture.cs index 44f2317370420..c5c1d7709f174 100644 --- a/dotnet/test/common/DevTools/DevToolsTestFixture.cs +++ b/dotnet/test/common/DevTools/DevToolsTestFixture.cs @@ -20,42 +20,41 @@ using NUnit.Framework; using OpenQA.Selenium.Environment; -namespace OpenQA.Selenium.DevTools +namespace OpenQA.Selenium.DevTools; + +public class DevToolsTestFixture : DriverTestFixture { - public class DevToolsTestFixture : DriverTestFixture + protected IDevTools devTools; + protected IDevToolsSession session; + + public bool IsDevToolsSupported { - protected IDevTools devTools; - protected IDevToolsSession session; + get { return devTools != null; } + } - public bool IsDevToolsSupported + [SetUp] + public void Setup() + { + driver = EnvironmentManager.Instance.GetCurrentDriver(); + devTools = driver as IDevTools; + if (devTools == null) { - get { return devTools != null; } + Assert.Ignore($"{EnvironmentManager.Instance.Browser} does not support Chrome DevTools Protocol"); + return; } - [SetUp] - public void Setup() - { - driver = EnvironmentManager.Instance.GetCurrentDriver(); - devTools = driver as IDevTools; - if (devTools == null) - { - Assert.Ignore($"{EnvironmentManager.Instance.Browser} does not support Chrome DevTools Protocol"); - return; - } - - session = devTools.GetDevToolsSession(); - } + session = devTools.GetDevToolsSession(); + } - [TearDown] - public void Teardown() + [TearDown] + public void Teardown() + { + if (session != null) { - if (session != null) - { - session.Dispose(); - EnvironmentManager.Instance.CloseCurrentDriver(); - session = null; - driver = null; - } + session.Dispose(); + EnvironmentManager.Instance.CloseCurrentDriver(); + session = null; + driver = null; } } } diff --git a/dotnet/test/common/DownloadsTest.cs b/dotnet/test/common/DownloadsTest.cs index 0043316b2025f..a3aec0534d72e 100644 --- a/dotnet/test/common/DownloadsTest.cs +++ b/dotnet/test/common/DownloadsTest.cs @@ -26,102 +26,101 @@ using System.IO; using System.Linq; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class DownLoadsTest : DriverTestFixture { - [TestFixture] - public class DownLoadsTest : DriverTestFixture + private IWebDriver localDriver; + + [SetUp] + public void ResetDriver() { - private IWebDriver localDriver; + EnvironmentManager.Instance.CloseCurrentDriver(); + InitLocalDriver(); + } - [SetUp] - public void ResetDriver() + [TearDown] + public void QuitAdditionalDriver() + { + if (localDriver != null) { - EnvironmentManager.Instance.CloseCurrentDriver(); - InitLocalDriver(); + localDriver.Quit(); + localDriver = null; } - [TearDown] - public void QuitAdditionalDriver() - { - if (localDriver != null) - { - localDriver.Quit(); - localDriver = null; - } + EnvironmentManager.Instance.CreateFreshDriver(); + } - EnvironmentManager.Instance.CreateFreshDriver(); - } + [Test] + [Ignore("Needs to run with Remote WebDriver")] + public void CanListDownloadableFiles() + { + DownloadWithBrowser(); - [Test] - [Ignore("Needs to run with Remote WebDriver")] - public void CanListDownloadableFiles() - { - DownloadWithBrowser(); + IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); + Assert.That(names, Contains.Item("file_1.txt")); + Assert.That(names, Contains.Item("file_2.jpg")); + } - IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); - Assert.That(names, Contains.Item("file_1.txt")); - Assert.That(names, Contains.Item("file_2.jpg")); - } + [Test] + [Ignore("Needs to run with Remote WebDriver")] + public void CanDownloadFile() + { + DownloadWithBrowser(); - [Test] - [Ignore("Needs to run with Remote WebDriver")] - public void CanDownloadFile() - { - DownloadWithBrowser(); + IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); + string fileName = names[0]; + string targetDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + ((RemoteWebDriver)driver).DownloadFile(fileName, targetDirectory); - IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); - string fileName = names[0]; - string targetDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string fileContent = File.ReadAllText(Path.Combine(targetDirectory, fileName)); + Assert.That(fileContent.Trim(), Is.EqualTo("Hello, World!")); - ((RemoteWebDriver)driver).DownloadFile(fileName, targetDirectory); + Directory.Delete(targetDirectory, recursive: true); + } - string fileContent = File.ReadAllText(Path.Combine(targetDirectory, fileName)); - Assert.That(fileContent.Trim(), Is.EqualTo("Hello, World!")); + [Test] + [Ignore("Needs to run with Remote WebDriver")] + public void CanDeleteFiles() + { + DownloadWithBrowser(); - Directory.Delete(targetDirectory, recursive: true); - } + ((RemoteWebDriver)driver).DeleteDownloadableFiles(); - [Test] - [Ignore("Needs to run with Remote WebDriver")] - public void CanDeleteFiles() - { - DownloadWithBrowser(); + IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); + Assert.That(names, Is.Empty, "The names list should be empty."); + } - ((RemoteWebDriver)driver).DeleteDownloadableFiles(); + private void DownloadWithBrowser() + { + string downloadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("downloads/download.html"); + localDriver.Url = downloadPage; + driver.FindElement(By.Id("file-1")).Click(); + driver.FindElement(By.Id("file-2")).Click(); - IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); - Assert.That(names, Is.Empty, "The names list should be empty."); - } + WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3)); + wait.Until(d => ((RemoteWebDriver)d).GetDownloadableFiles().Contains("file_2.jpg")); + } - private void DownloadWithBrowser() - { - string downloadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("downloads/download.html"); - localDriver.Url = downloadPage; - driver.FindElement(By.Id("file-1")).Click(); - driver.FindElement(By.Id("file-2")).Click(); + private void InitLocalDriver() + { + DownloadableFilesOptions options = new DownloadableFilesOptions(); + options.EnableDownloads = true; - WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3)); - wait.Until(d => ((RemoteWebDriver)d).GetDownloadableFiles().Contains("file_2.jpg")); - } + localDriver = EnvironmentManager.Instance.CreateDriverInstance(options); + } - private void InitLocalDriver() + public class DownloadableFilesOptions : DriverOptions + { + public override void AddAdditionalOption(string capabilityName, object capabilityValue) { - DownloadableFilesOptions options = new DownloadableFilesOptions(); - options.EnableDownloads = true; - - localDriver = EnvironmentManager.Instance.CreateDriverInstance(options); } - public class DownloadableFilesOptions : DriverOptions + public override ICapabilities ToCapabilities() { - public override void AddAdditionalOption(string capabilityName, object capabilityValue) - { - } - - public override ICapabilities ToCapabilities() - { - return null; - } + return null; } } } diff --git a/dotnet/test/common/DriverElementFindingTest.cs b/dotnet/test/common/DriverElementFindingTest.cs index 6c44b9a4602e9..5e66d9d77b4ba 100644 --- a/dotnet/test/common/DriverElementFindingTest.cs +++ b/dotnet/test/common/DriverElementFindingTest.cs @@ -20,129 +20,128 @@ using NUnit.Framework; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class DriverElementFindingTest : DriverTestFixture { - [TestFixture] - public class DriverElementFindingTest : DriverTestFixture + + #region FindElemement Tests + + [Test] + public void ShouldFindElementById() + { + driver.Url = simpleTestPage; + IWebElement e = driver.FindElement(By.Id("oneline")); + Assert.That(e.Text, Is.EqualTo("A single line of text")); + } + + [Test] + public void ShouldFindElementByLinkText() + { + driver.Url = simpleTestPage; + IWebElement e = driver.FindElement(By.LinkText("link with leading space")); + Assert.That(e.Text, Is.EqualTo("link with leading space")); + } + + [Test] + public void ShouldFindElementByName() + { + driver.Url = nestedPage; + IWebElement e = driver.FindElement(By.Name("div1")); + Assert.That(e.Text, Is.EqualTo("hello world hello world")); + } + + [Test] + public void ShouldFindElementByXPath() + { + driver.Url = simpleTestPage; + IWebElement e = driver.FindElement(By.XPath("/html/body/p[1]")); + Assert.That(e.Text, Is.EqualTo("A single line of text")); + } + + [Test] + public void ShouldFindElementByClassName() + { + driver.Url = nestedPage; + IWebElement e = driver.FindElement(By.ClassName("one")); + Assert.That(e.Text, Is.EqualTo("Span with class of one")); + } + + [Test] + public void ShouldFindElementByPartialLinkText() + { + driver.Url = simpleTestPage; + IWebElement e = driver.FindElement(By.PartialLinkText("leading space")); + Assert.That(e.Text, Is.EqualTo("link with leading space")); + } + + [Test] + public void ShouldFindElementByTagName() { + driver.Url = simpleTestPage; + IWebElement e = driver.FindElement(By.TagName("H1")); + Assert.That(e.Text, Is.EqualTo("Heading")); + } + #endregion + + //TODO(andre.nogueira): We're not checking the right elements are being returned! + #region FindElemements Tests + + [Test] + public void ShouldFindElementsById() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("test_id")); + Assert.That(elements, Has.Count.EqualTo(2)); + } + + [Test] + public void ShouldFindElementsByLinkText() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.LinkText("hello world")); + Assert.That(elements, Has.Count.EqualTo(12)); + } + + [Test] + public void ShouldFindElementsByName() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Name("form1")); + Assert.That(elements, Has.Count.EqualTo(4)); + } - #region FindElemement Tests - - [Test] - public void ShouldFindElementById() - { - driver.Url = simpleTestPage; - IWebElement e = driver.FindElement(By.Id("oneline")); - Assert.That(e.Text, Is.EqualTo("A single line of text")); - } - - [Test] - public void ShouldFindElementByLinkText() - { - driver.Url = simpleTestPage; - IWebElement e = driver.FindElement(By.LinkText("link with leading space")); - Assert.That(e.Text, Is.EqualTo("link with leading space")); - } - - [Test] - public void ShouldFindElementByName() - { - driver.Url = nestedPage; - IWebElement e = driver.FindElement(By.Name("div1")); - Assert.That(e.Text, Is.EqualTo("hello world hello world")); - } - - [Test] - public void ShouldFindElementByXPath() - { - driver.Url = simpleTestPage; - IWebElement e = driver.FindElement(By.XPath("/html/body/p[1]")); - Assert.That(e.Text, Is.EqualTo("A single line of text")); - } - - [Test] - public void ShouldFindElementByClassName() - { - driver.Url = nestedPage; - IWebElement e = driver.FindElement(By.ClassName("one")); - Assert.That(e.Text, Is.EqualTo("Span with class of one")); - } - - [Test] - public void ShouldFindElementByPartialLinkText() - { - driver.Url = simpleTestPage; - IWebElement e = driver.FindElement(By.PartialLinkText("leading space")); - Assert.That(e.Text, Is.EqualTo("link with leading space")); - } - - [Test] - public void ShouldFindElementByTagName() - { - driver.Url = simpleTestPage; - IWebElement e = driver.FindElement(By.TagName("H1")); - Assert.That(e.Text, Is.EqualTo("Heading")); - } - #endregion - - //TODO(andre.nogueira): We're not checking the right elements are being returned! - #region FindElemements Tests - - [Test] - public void ShouldFindElementsById() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("test_id")); - Assert.That(elements, Has.Count.EqualTo(2)); - } - - [Test] - public void ShouldFindElementsByLinkText() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.LinkText("hello world")); - Assert.That(elements, Has.Count.EqualTo(12)); - } - - [Test] - public void ShouldFindElementsByName() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Name("form1")); - Assert.That(elements, Has.Count.EqualTo(4)); - } - - [Test] - public void ShouldFindElementsByXPath() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.XPath("//a")); - Assert.That(elements, Has.Count.EqualTo(12)); - } - - [Test] - public void ShouldFindElementsByClassName() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.ClassName("one")); - Assert.That(elements, Has.Count.EqualTo(3)); - } - - [Test] - public void ShouldFindElementsByPartialLinkText() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("world")); - Assert.That(elements, Has.Count.EqualTo(12)); - } - - [Test] - public void ShouldFindElementsByTagName() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.TagName("a")); - Assert.That(elements, Has.Count.EqualTo(12)); - } - #endregion + [Test] + public void ShouldFindElementsByXPath() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.XPath("//a")); + Assert.That(elements, Has.Count.EqualTo(12)); + } + + [Test] + public void ShouldFindElementsByClassName() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.ClassName("one")); + Assert.That(elements, Has.Count.EqualTo(3)); + } + + [Test] + public void ShouldFindElementsByPartialLinkText() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("world")); + Assert.That(elements, Has.Count.EqualTo(12)); + } + + [Test] + public void ShouldFindElementsByTagName() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.TagName("a")); + Assert.That(elements, Has.Count.EqualTo(12)); } + #endregion } diff --git a/dotnet/test/common/DriverTestFixture.cs b/dotnet/test/common/DriverTestFixture.cs index 2f0eacf218f27..eaee68f811a0b 100644 --- a/dotnet/test/common/DriverTestFixture.cs +++ b/dotnet/test/common/DriverTestFixture.cs @@ -22,192 +22,191 @@ using System; using static NUnit.Framework.Interfaces.ResultState; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +public abstract class DriverTestFixture { - public abstract class DriverTestFixture + public string alertsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("alerts.html"); + public string blankPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("blank.html"); + public string macbethPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("macbeth.html"); + public string macbethTitle = "Macbeth: Entire Play"; + + public string simpleTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("simpleTest.html"); + public string simpleTestTitle = "Hello WebDriver"; + + public string framesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("win32frameset.html"); + public string framesTitle = "This page has frames"; + + public string iframesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("iframes.html"); + public string iframesTitle = "This page has iframes"; + + public string formsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("formPage.html"); + public string formsTitle = "We Leave From Here"; + + public string javascriptPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("javascriptPage.html"); + + public string loginPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("login.html"); + + public string clickEventPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("clickEventPage.html"); + + public string resultPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("resultPage.html"); + + public string nestedPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("nestedElements.html"); + + public string xhtmlTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("xhtmlTest.html"); + + public string richTextPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("rich_text.html"); + + public string dragAndDropPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dragAndDropTest.html"); + + public string framesetPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("frameset.html"); + public string iframePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("iframes.html"); + public string metaRedirectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("meta-redirect.html"); + public string redirectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("redirect"); + public string rectanglesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("rectangles.html"); + public string javascriptEnhancedForm = EnvironmentManager.Instance.UrlBuilder.WhereIs("javascriptEnhancedForm.html"); + public string uploadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("upload.html"); + public string transparentUploadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("transparentUpload.html"); + public string childPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("child/childPage.html"); + public string grandchildPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("child/grandchild/grandchildPage.html"); + public string documentWrite = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("document_write_in_onload.html"); + public string chinesePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("cn-test.html"); + public string svgPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("svgPiechart.xhtml"); + public string dynamicPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dynamic.html"); + public string tables = EnvironmentManager.Instance.UrlBuilder.WhereIs("tables.html"); + public string deletingFrame = EnvironmentManager.Instance.UrlBuilder.WhereIs("frame_switching_tests/deletingFrame.html"); + public string ajaxyPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("ajaxy_page.html"); + public string sleepingPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("sleep"); + public string slowIframes = EnvironmentManager.Instance.UrlBuilder.WhereIs("slow_loading_iframes.html"); + public string draggableLists = EnvironmentManager.Instance.UrlBuilder.WhereIs("draggableLists.html"); + public string droppableItems = EnvironmentManager.Instance.UrlBuilder.WhereIs("droppableItems.html"); + public string bodyTypingPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("bodyTypingTest.html"); + public string formSelectionPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("formSelectionPage.html"); + public string selectableItemsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("selectableItems.html"); + public string underscorePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("underscore.html"); + public string clickJackerPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_jacker.html"); + public string errorsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("errors.html"); + public string selectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("selectPage.html"); + public string simpleXmlDocument = EnvironmentManager.Instance.UrlBuilder.WhereIs("simple.xml"); + public string mapVisibilityPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("map_visibility.html"); + public string mouseTrackerPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mousePositionTracker.html"); + public string mouseOverPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mouseOver.html"); + public string mouseInteractionPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mouse_interaction.html"); + public string readOnlyPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("readOnlyPage.html"); + public string clicksPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("clicks.html"); + public string booleanAttributes = EnvironmentManager.Instance.UrlBuilder.WhereIs("booleanAttributes.html"); + public string linkedImage = EnvironmentManager.Instance.UrlBuilder.WhereIs("linked_image.html"); + public string xhtmlFormPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("xhtmlFormPage.xhtml"); + public string svgTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("svgTest.svg"); + public string slowLoadingAlertPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("slowLoadingAlert.html"); + public string dragDropOverflowPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dragDropOverflow.html"); + public string missedJsReferencePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("missedJsReference.html"); + public string authenticationPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("basicAuth"); + public string html5Page = EnvironmentManager.Instance.UrlBuilder.WhereIs("html5Page.html"); + public string shadowRootPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("shadowRootPage.html"); + public string scrollFrameOutOfViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html"); + public string scrollFrameInViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame.html"); + + public string printPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("printPage.html"); + + protected IWebDriver driver; + + public IWebDriver DriverInstance { - public string alertsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("alerts.html"); - public string blankPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("blank.html"); - public string macbethPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("macbeth.html"); - public string macbethTitle = "Macbeth: Entire Play"; - - public string simpleTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("simpleTest.html"); - public string simpleTestTitle = "Hello WebDriver"; - - public string framesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("win32frameset.html"); - public string framesTitle = "This page has frames"; - - public string iframesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("iframes.html"); - public string iframesTitle = "This page has iframes"; - - public string formsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("formPage.html"); - public string formsTitle = "We Leave From Here"; - - public string javascriptPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("javascriptPage.html"); - - public string loginPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("login.html"); - - public string clickEventPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("clickEventPage.html"); - - public string resultPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("resultPage.html"); - - public string nestedPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("nestedElements.html"); - - public string xhtmlTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("xhtmlTest.html"); - - public string richTextPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("rich_text.html"); - - public string dragAndDropPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dragAndDropTest.html"); - - public string framesetPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("frameset.html"); - public string iframePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("iframes.html"); - public string metaRedirectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("meta-redirect.html"); - public string redirectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("redirect"); - public string rectanglesPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("rectangles.html"); - public string javascriptEnhancedForm = EnvironmentManager.Instance.UrlBuilder.WhereIs("javascriptEnhancedForm.html"); - public string uploadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("upload.html"); - public string transparentUploadPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("transparentUpload.html"); - public string childPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("child/childPage.html"); - public string grandchildPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("child/grandchild/grandchildPage.html"); - public string documentWrite = EnvironmentManager.Instance.UrlBuilder.WhereElseIs("document_write_in_onload.html"); - public string chinesePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("cn-test.html"); - public string svgPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("svgPiechart.xhtml"); - public string dynamicPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dynamic.html"); - public string tables = EnvironmentManager.Instance.UrlBuilder.WhereIs("tables.html"); - public string deletingFrame = EnvironmentManager.Instance.UrlBuilder.WhereIs("frame_switching_tests/deletingFrame.html"); - public string ajaxyPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("ajaxy_page.html"); - public string sleepingPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("sleep"); - public string slowIframes = EnvironmentManager.Instance.UrlBuilder.WhereIs("slow_loading_iframes.html"); - public string draggableLists = EnvironmentManager.Instance.UrlBuilder.WhereIs("draggableLists.html"); - public string droppableItems = EnvironmentManager.Instance.UrlBuilder.WhereIs("droppableItems.html"); - public string bodyTypingPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("bodyTypingTest.html"); - public string formSelectionPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("formSelectionPage.html"); - public string selectableItemsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("selectableItems.html"); - public string underscorePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("underscore.html"); - public string clickJackerPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("click_jacker.html"); - public string errorsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("errors.html"); - public string selectPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("selectPage.html"); - public string simpleXmlDocument = EnvironmentManager.Instance.UrlBuilder.WhereIs("simple.xml"); - public string mapVisibilityPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("map_visibility.html"); - public string mouseTrackerPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mousePositionTracker.html"); - public string mouseOverPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mouseOver.html"); - public string mouseInteractionPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("mouse_interaction.html"); - public string readOnlyPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("readOnlyPage.html"); - public string clicksPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("clicks.html"); - public string booleanAttributes = EnvironmentManager.Instance.UrlBuilder.WhereIs("booleanAttributes.html"); - public string linkedImage = EnvironmentManager.Instance.UrlBuilder.WhereIs("linked_image.html"); - public string xhtmlFormPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("xhtmlFormPage.xhtml"); - public string svgTestPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("svgTest.svg"); - public string slowLoadingAlertPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("slowLoadingAlert.html"); - public string dragDropOverflowPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("dragDropOverflow.html"); - public string missedJsReferencePage = EnvironmentManager.Instance.UrlBuilder.WhereIs("missedJsReference.html"); - public string authenticationPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("basicAuth"); - public string html5Page = EnvironmentManager.Instance.UrlBuilder.WhereIs("html5Page.html"); - public string shadowRootPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("shadowRootPage.html"); - public string scrollFrameOutOfViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html"); - public string scrollFrameInViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame.html"); - - public string printPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("printPage.html"); - - protected IWebDriver driver; - - public IWebDriver DriverInstance - { - get { return driver; } - set { driver = value; } - } + get { return driver; } + set { driver = value; } + } - public bool IsNativeEventsEnabled + public bool IsNativeEventsEnabled + { + get { - get + IHasCapabilities capabilitiesDriver = driver as IHasCapabilities; + if (capabilitiesDriver != null && capabilitiesDriver.Capabilities.HasCapability(CapabilityType.HasNativeEvents) && (bool)capabilitiesDriver.Capabilities.GetCapability(CapabilityType.HasNativeEvents)) { - IHasCapabilities capabilitiesDriver = driver as IHasCapabilities; - if (capabilitiesDriver != null && capabilitiesDriver.Capabilities.HasCapability(CapabilityType.HasNativeEvents) && (bool)capabilitiesDriver.Capabilities.GetCapability(CapabilityType.HasNativeEvents)) - { - return true; - } - - return false; + return true; } - } - [OneTimeSetUp] - public void SetUp() - { - driver = EnvironmentManager.Instance.GetCurrentDriver(); + return false; } + } - [TearDown] - public void ResetOnError() - { - if (TestContext.CurrentContext.Result.Outcome == Error) - { - driver = EnvironmentManager.Instance.CreateFreshDriver(); - } - } + [OneTimeSetUp] + public void SetUp() + { + driver = EnvironmentManager.Instance.GetCurrentDriver(); + } - /* - * Exists because a given test might require a fresh driver - */ - protected void CreateFreshDriver() + [TearDown] + public void ResetOnError() + { + if (TestContext.CurrentContext.Result.Outcome == Error) { driver = EnvironmentManager.Instance.CreateFreshDriver(); } + } - protected bool IsIeDriverTimedOutException(Exception e) - { - // The IE driver may throw a timed out exception - return e.GetType().Name.Contains("TimedOutException"); - } + /* + * Exists because a given test might require a fresh driver + */ + protected void CreateFreshDriver() + { + driver = EnvironmentManager.Instance.CreateFreshDriver(); + } - protected bool WaitFor(Func waitFunction, string timeoutMessage) - { - return WaitFor(waitFunction, timeoutMessage); - } + protected bool IsIeDriverTimedOutException(Exception e) + { + // The IE driver may throw a timed out exception + return e.GetType().Name.Contains("TimedOutException"); + } - protected T WaitFor(Func waitFunction, string timeoutMessage) - { - return this.WaitFor(waitFunction, TimeSpan.FromSeconds(5), timeoutMessage); - } + protected bool WaitFor(Func waitFunction, string timeoutMessage) + { + return WaitFor(waitFunction, timeoutMessage); + } - protected T WaitFor(Func waitFunction, TimeSpan timeout, string timeoutMessage) + protected T WaitFor(Func waitFunction, string timeoutMessage) + { + return this.WaitFor(waitFunction, TimeSpan.FromSeconds(5), timeoutMessage); + } + + protected T WaitFor(Func waitFunction, TimeSpan timeout, string timeoutMessage) + { + DateTime endTime = DateTime.Now.Add(timeout); + T value = default(T); + Exception lastException = null; + while (DateTime.Now < endTime) { - DateTime endTime = DateTime.Now.Add(timeout); - T value = default(T); - Exception lastException = null; - while (DateTime.Now < endTime) + try { - try + value = waitFunction(); + if (typeof(T) == typeof(bool)) { - value = waitFunction(); - if (typeof(T) == typeof(bool)) - { - if ((bool)(object)value) - { - return value; - } - } - else if (value != null) + if ((bool)(object)value) { return value; } - - System.Threading.Thread.Sleep(100); } - catch (Exception e) + else if (value != null) { - // Swallow for later re-throwing - lastException = e; + return value; } - } - if (lastException != null) + System.Threading.Thread.Sleep(100); + } + catch (Exception e) { - throw new WebDriverException("Operation timed out", lastException); + // Swallow for later re-throwing + lastException = e; } + } - Assert.Fail("Condition timed out: " + timeoutMessage); - return default(T); + if (lastException != null) + { + throw new WebDriverException("Operation timed out", lastException); } + + Assert.Fail("Condition timed out: " + timeoutMessage); + return default(T); } } diff --git a/dotnet/test/common/ElementAttributeTest.cs b/dotnet/test/common/ElementAttributeTest.cs index 9eb59cfff2f5a..ff70603499456 100644 --- a/dotnet/test/common/ElementAttributeTest.cs +++ b/dotnet/test/common/ElementAttributeTest.cs @@ -23,424 +23,423 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ElementAttributeTest : DriverTestFixture { - [TestFixture] - public class ElementAttributeTest : DriverTestFixture + [Test] + public void ShouldReturnNullWhenGettingTheValueOfAnAttributeThatIsNotListed() { - [Test] - public void ShouldReturnNullWhenGettingTheValueOfAnAttributeThatIsNotListed() - { - driver.Url = simpleTestPage; - IWebElement head = driver.FindElement(By.XPath("/html")); - string attribute = head.GetAttribute("cheese"); - Assert.That(attribute, Is.Null); - } + driver.Url = simpleTestPage; + IWebElement head = driver.FindElement(By.XPath("/html")); + string attribute = head.GetAttribute("cheese"); + Assert.That(attribute, Is.Null); + } - [Test] - public void ShouldReturnNullWhenGettingSrcAttributeOfInvalidImgTag() - { - driver.Url = simpleTestPage; - IWebElement img = driver.FindElement(By.Id("invalidImgTag")); - string attribute = img.GetAttribute("src"); - Assert.That(attribute, Is.Null); - } + [Test] + public void ShouldReturnNullWhenGettingSrcAttributeOfInvalidImgTag() + { + driver.Url = simpleTestPage; + IWebElement img = driver.FindElement(By.Id("invalidImgTag")); + string attribute = img.GetAttribute("src"); + Assert.That(attribute, Is.Null); + } - [Test] - public void ShouldReturnAnAbsoluteUrlWhenGettingSrcAttributeOfAValidImgTag() - { - driver.Url = simpleTestPage; - IWebElement img = driver.FindElement(By.Id("validImgTag")); - string attribute = img.GetAttribute("src"); - Assert.That(attribute, Is.EqualTo(EnvironmentManager.Instance.UrlBuilder.WhereIs("icon.gif"))); - } + [Test] + public void ShouldReturnAnAbsoluteUrlWhenGettingSrcAttributeOfAValidImgTag() + { + driver.Url = simpleTestPage; + IWebElement img = driver.FindElement(By.Id("validImgTag")); + string attribute = img.GetAttribute("src"); + Assert.That(attribute, Is.EqualTo(EnvironmentManager.Instance.UrlBuilder.WhereIs("icon.gif"))); + } - [Test] - public void ShouldReturnAnAbsoluteUrlWhenGettingHrefAttributeOfAValidAnchorTag() - { - driver.Url = simpleTestPage; - IWebElement img = driver.FindElement(By.Id("validAnchorTag")); - string attribute = img.GetAttribute("href"); - Assert.That(attribute, Is.EqualTo(EnvironmentManager.Instance.UrlBuilder.WhereIs("icon.gif"))); - } + [Test] + public void ShouldReturnAnAbsoluteUrlWhenGettingHrefAttributeOfAValidAnchorTag() + { + driver.Url = simpleTestPage; + IWebElement img = driver.FindElement(By.Id("validAnchorTag")); + string attribute = img.GetAttribute("href"); + Assert.That(attribute, Is.EqualTo(EnvironmentManager.Instance.UrlBuilder.WhereIs("icon.gif"))); + } - [Test] - public void ShouldReturnEmptyAttributeValuesWhenPresentAndTheValueIsActuallyEmpty() - { - driver.Url = simpleTestPage; - IWebElement body = driver.FindElement(By.XPath("//body")); - Assert.That(body.GetAttribute("style"), Is.Empty); - } + [Test] + public void ShouldReturnEmptyAttributeValuesWhenPresentAndTheValueIsActuallyEmpty() + { + driver.Url = simpleTestPage; + IWebElement body = driver.FindElement(By.XPath("//body")); + Assert.That(body.GetAttribute("style"), Is.Empty); + } - [Test] - public void ShouldReturnTheValueOfTheDisabledAttributeAsNullIfNotSet() - { - driver.Url = formsPage; - IWebElement inputElement = driver.FindElement(By.XPath("//input[@id='working']")); - Assert.That(inputElement.GetAttribute("disabled"), Is.Null); - Assert.That(inputElement.Enabled, "Element is not enabled"); - - IWebElement pElement = driver.FindElement(By.Id("peas")); - Assert.That(inputElement.GetAttribute("disabled"), Is.Null); - Assert.That(inputElement.Enabled, "Element is not enabled"); - } + [Test] + public void ShouldReturnTheValueOfTheDisabledAttributeAsNullIfNotSet() + { + driver.Url = formsPage; + IWebElement inputElement = driver.FindElement(By.XPath("//input[@id='working']")); + Assert.That(inputElement.GetAttribute("disabled"), Is.Null); + Assert.That(inputElement.Enabled, "Element is not enabled"); + + IWebElement pElement = driver.FindElement(By.Id("peas")); + Assert.That(inputElement.GetAttribute("disabled"), Is.Null); + Assert.That(inputElement.Enabled, "Element is not enabled"); + } - [Test] - public void ShouldReturnTheValueOfTheIndexAttrbuteEvenIfItIsMissing() - { - driver.Url = formsPage; + [Test] + public void ShouldReturnTheValueOfTheIndexAttrbuteEvenIfItIsMissing() + { + driver.Url = formsPage; - IWebElement multiSelect = driver.FindElement(By.Id("multi")); - ReadOnlyCollection options = multiSelect.FindElements(By.TagName("option")); - Assert.That(options[1].GetAttribute("index"), Is.EqualTo("1")); - } + IWebElement multiSelect = driver.FindElement(By.Id("multi")); + ReadOnlyCollection options = multiSelect.FindElements(By.TagName("option")); + Assert.That(options[1].GetAttribute("index"), Is.EqualTo("1")); + } - [Test] - public void ShouldIndicateTheElementsThatAreDisabledAreNotEnabled() - { - driver.Url = formsPage; - IWebElement inputElement = driver.FindElement(By.XPath("//input[@id='notWorking']")); - Assert.That(inputElement.Enabled, Is.False, "Element should be disabled"); + [Test] + public void ShouldIndicateTheElementsThatAreDisabledAreNotEnabled() + { + driver.Url = formsPage; + IWebElement inputElement = driver.FindElement(By.XPath("//input[@id='notWorking']")); + Assert.That(inputElement.Enabled, Is.False, "Element should be disabled"); - inputElement = driver.FindElement(By.XPath("//input[@id='working']")); - Assert.That(inputElement.Enabled, Is.True, "Element should be enabled"); - } + inputElement = driver.FindElement(By.XPath("//input[@id='working']")); + Assert.That(inputElement.Enabled, Is.True, "Element should be enabled"); + } - [Test] - public void ElementsShouldBeDisabledIfTheyAreDisabledUsingRandomDisabledStrings() - { - driver.Url = formsPage; - IWebElement disabledTextElement1 = driver.FindElement(By.Id("disabledTextElement1")); - Assert.That(disabledTextElement1.Enabled, Is.False, "disabledTextElement1 should be disabled"); + [Test] + public void ElementsShouldBeDisabledIfTheyAreDisabledUsingRandomDisabledStrings() + { + driver.Url = formsPage; + IWebElement disabledTextElement1 = driver.FindElement(By.Id("disabledTextElement1")); + Assert.That(disabledTextElement1.Enabled, Is.False, "disabledTextElement1 should be disabled"); - IWebElement disabledTextElement2 = driver.FindElement(By.Id("disabledTextElement2")); - Assert.That(disabledTextElement2.Enabled, Is.False, "disabledTextElement2 should be disabled"); + IWebElement disabledTextElement2 = driver.FindElement(By.Id("disabledTextElement2")); + Assert.That(disabledTextElement2.Enabled, Is.False, "disabledTextElement2 should be disabled"); - IWebElement disabledSubmitElement = driver.FindElement(By.Id("disabledSubmitElement")); - Assert.That(disabledSubmitElement.Enabled, Is.False, "disabledSubmitElement should be disabled"); - } + IWebElement disabledSubmitElement = driver.FindElement(By.Id("disabledSubmitElement")); + Assert.That(disabledSubmitElement.Enabled, Is.False, "disabledSubmitElement should be disabled"); + } - [Test] - public void ShouldThrowExceptionIfSendingKeysToElementDisabledUsingRandomDisabledStrings() - { - driver.Url = formsPage; - IWebElement disabledTextElement1 = driver.FindElement(By.Id("disabledTextElement1")); + [Test] + public void ShouldThrowExceptionIfSendingKeysToElementDisabledUsingRandomDisabledStrings() + { + driver.Url = formsPage; + IWebElement disabledTextElement1 = driver.FindElement(By.Id("disabledTextElement1")); - Assert.That(() => - { - disabledTextElement1.SendKeys("foo"); - }, Throws.TypeOf()); + Assert.That(() => + { + disabledTextElement1.SendKeys("foo"); + }, Throws.TypeOf()); - Assert.That(disabledTextElement1.Text, Is.Empty); + Assert.That(disabledTextElement1.Text, Is.Empty); - IWebElement disabledTextElement2 = driver.FindElement(By.Id("disabledTextElement2")); + IWebElement disabledTextElement2 = driver.FindElement(By.Id("disabledTextElement2")); - Assert.That( - () => disabledTextElement2.SendKeys("bar"), - Throws.TypeOf()); + Assert.That( + () => disabledTextElement2.SendKeys("bar"), + Throws.TypeOf()); - Assert.That(disabledTextElement2.Text, Is.Empty); - } + Assert.That(disabledTextElement2.Text, Is.Empty); + } - [Test] - public void ShouldIndicateWhenATextAreaIsDisabled() - { - driver.Url = formsPage; - IWebElement textArea = driver.FindElement(By.XPath("//textarea[@id='notWorkingArea']")); - Assert.That(textArea.Enabled, Is.False); - } + [Test] + public void ShouldIndicateWhenATextAreaIsDisabled() + { + driver.Url = formsPage; + IWebElement textArea = driver.FindElement(By.XPath("//textarea[@id='notWorkingArea']")); + Assert.That(textArea.Enabled, Is.False); + } - [Test] - public void ShouldIndicateWhenASelectIsDisabled() - { - driver.Url = formsPage; + [Test] + public void ShouldIndicateWhenASelectIsDisabled() + { + driver.Url = formsPage; - IWebElement enabled = driver.FindElement(By.Name("selectomatic")); - IWebElement disabled = driver.FindElement(By.Name("no-select")); + IWebElement enabled = driver.FindElement(By.Name("selectomatic")); + IWebElement disabled = driver.FindElement(By.Name("no-select")); - Assert.That(enabled.Enabled, Is.True, "Expected select element to be enabled"); - Assert.That(disabled.Enabled, Is.False, "Expected select element to be disabled"); - } + Assert.That(enabled.Enabled, Is.True, "Expected select element to be enabled"); + Assert.That(disabled.Enabled, Is.False, "Expected select element to be disabled"); + } - [Test] - public void ShouldReturnTheValueOfCheckedForACheckboxOnlyIfItIsChecked() - { - driver.Url = formsPage; - IWebElement checkbox = driver.FindElement(By.XPath("//input[@id='checky']")); - Assert.That(checkbox.GetAttribute("checked"), Is.Null); - checkbox.Click(); - Assert.That(checkbox.GetAttribute("checked"), Is.EqualTo("true")); - } + [Test] + public void ShouldReturnTheValueOfCheckedForACheckboxOnlyIfItIsChecked() + { + driver.Url = formsPage; + IWebElement checkbox = driver.FindElement(By.XPath("//input[@id='checky']")); + Assert.That(checkbox.GetAttribute("checked"), Is.Null); + checkbox.Click(); + Assert.That(checkbox.GetAttribute("checked"), Is.EqualTo("true")); + } - [Test] - public void ShouldOnlyReturnTheValueOfSelectedForRadioButtonsIfItIsSet() - { - driver.Url = formsPage; - IWebElement neverSelected = driver.FindElement(By.Id("cheese")); - IWebElement initiallyNotSelected = driver.FindElement(By.Id("peas")); - IWebElement initiallySelected = driver.FindElement(By.Id("cheese_and_peas")); - - Assert.That(neverSelected.GetAttribute("selected"), Is.Null, "false"); - Assert.That(initiallyNotSelected.GetAttribute("selected"), Is.Null, "false"); - Assert.That(initiallySelected.GetAttribute("selected"), Is.EqualTo("true"), "true"); - - initiallyNotSelected.Click(); - Assert.That(neverSelected.GetAttribute("selected"), Is.Null); - Assert.That(initiallyNotSelected.GetAttribute("selected"), Is.EqualTo("true")); - Assert.That(initiallySelected.GetAttribute("selected"), Is.Null); - } + [Test] + public void ShouldOnlyReturnTheValueOfSelectedForRadioButtonsIfItIsSet() + { + driver.Url = formsPage; + IWebElement neverSelected = driver.FindElement(By.Id("cheese")); + IWebElement initiallyNotSelected = driver.FindElement(By.Id("peas")); + IWebElement initiallySelected = driver.FindElement(By.Id("cheese_and_peas")); + + Assert.That(neverSelected.GetAttribute("selected"), Is.Null, "false"); + Assert.That(initiallyNotSelected.GetAttribute("selected"), Is.Null, "false"); + Assert.That(initiallySelected.GetAttribute("selected"), Is.EqualTo("true"), "true"); + + initiallyNotSelected.Click(); + Assert.That(neverSelected.GetAttribute("selected"), Is.Null); + Assert.That(initiallyNotSelected.GetAttribute("selected"), Is.EqualTo("true")); + Assert.That(initiallySelected.GetAttribute("selected"), Is.Null); + } - [Test] - public void ShouldReturnTheValueOfSelectedForOptionsOnlyIfTheyAreSelected() - { - driver.Url = formsPage; - IWebElement selectBox = driver.FindElement(By.XPath("//select[@name='selectomatic']")); - ReadOnlyCollection options = selectBox.FindElements(By.TagName("option")); - IWebElement one = options[0]; - IWebElement two = options[1]; - Assert.That(one.Selected, Is.True); - Assert.That(two.Selected, Is.False); - Assert.That(one.GetAttribute("selected"), Is.EqualTo("true")); - Assert.That(two.GetAttribute("selected"), Is.Null); - } + [Test] + public void ShouldReturnTheValueOfSelectedForOptionsOnlyIfTheyAreSelected() + { + driver.Url = formsPage; + IWebElement selectBox = driver.FindElement(By.XPath("//select[@name='selectomatic']")); + ReadOnlyCollection options = selectBox.FindElements(By.TagName("option")); + IWebElement one = options[0]; + IWebElement two = options[1]; + Assert.That(one.Selected, Is.True); + Assert.That(two.Selected, Is.False); + Assert.That(one.GetAttribute("selected"), Is.EqualTo("true")); + Assert.That(two.GetAttribute("selected"), Is.Null); + } - [Test] - public void ShouldReturnValueOfClassAttributeOfAnElement() - { - driver.Url = xhtmlTestPage; + [Test] + public void ShouldReturnValueOfClassAttributeOfAnElement() + { + driver.Url = xhtmlTestPage; - IWebElement heading = driver.FindElement(By.XPath("//h1")); - String className = heading.GetAttribute("class"); + IWebElement heading = driver.FindElement(By.XPath("//h1")); + String className = heading.GetAttribute("class"); - Assert.That(className, Is.EqualTo("header")); - } + Assert.That(className, Is.EqualTo("header")); + } - [Test] - public void ShouldReturnTheContentsOfATextAreaAsItsValue() - { - driver.Url = formsPage; + [Test] + public void ShouldReturnTheContentsOfATextAreaAsItsValue() + { + driver.Url = formsPage; - String value = driver.FindElement(By.Id("withText")).GetAttribute("value"); + String value = driver.FindElement(By.Id("withText")).GetAttribute("value"); - Assert.That(value, Is.EqualTo("Example text")); - } + Assert.That(value, Is.EqualTo("Example text")); + } - [Test] - public void ShouldReturnInnerHtml() - { - driver.Url = simpleTestPage; + [Test] + public void ShouldReturnInnerHtml() + { + driver.Url = simpleTestPage; - string html = driver.FindElement(By.Id("wrappingtext")).GetAttribute("innerHTML"); - Assert.That(html, Does.Contain("")); - } + string html = driver.FindElement(By.Id("wrappingtext")).GetAttribute("innerHTML"); + Assert.That(html, Does.Contain("")); + } - [Test] - public void ShouldTreatReadonlyAsAValue() - { - driver.Url = formsPage; + [Test] + public void ShouldTreatReadonlyAsAValue() + { + driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Name("readonly")); - string readOnlyAttribute = element.GetAttribute("readonly"); + IWebElement element = driver.FindElement(By.Name("readonly")); + string readOnlyAttribute = element.GetAttribute("readonly"); - Assert.That(readOnlyAttribute, Is.Not.Null); + Assert.That(readOnlyAttribute, Is.Not.Null); - IWebElement textInput = driver.FindElement(By.Name("x")); - string notReadOnly = textInput.GetAttribute("readonly"); + IWebElement textInput = driver.FindElement(By.Name("x")); + string notReadOnly = textInput.GetAttribute("readonly"); - Assert.That(notReadOnly, Is.Null); - } + Assert.That(notReadOnly, Is.Null); + } - [Test] - public void ShouldReturnHiddenTextForTextContentAttribute() - { - driver.Url = simpleTestPage; + [Test] + public void ShouldReturnHiddenTextForTextContentAttribute() + { + driver.Url = simpleTestPage; - IWebElement element = driver.FindElement(By.Id("hiddenline")); - string textContent = element.GetAttribute("textContent"); + IWebElement element = driver.FindElement(By.Id("hiddenline")); + string textContent = element.GetAttribute("textContent"); - Assert.That(textContent, Is.EqualTo("A hidden line of text")); - } + Assert.That(textContent, Is.EqualTo("A hidden line of text")); + } - [Test] - public void ShouldGetNumericAtribute() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("withText")); - Assert.That(element.GetAttribute("rows"), Is.EqualTo("5")); - } + [Test] + public void ShouldGetNumericAtribute() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("withText")); + Assert.That(element.GetAttribute("rows"), Is.EqualTo("5")); + } - [Test] - public void CanReturnATextApproximationOfTheStyleAttribute() - { - driver.Url = javascriptPage; - string style = driver.FindElement(By.Id("red-item")).GetAttribute("style"); + [Test] + public void CanReturnATextApproximationOfTheStyleAttribute() + { + driver.Url = javascriptPage; + string style = driver.FindElement(By.Id("red-item")).GetAttribute("style"); - Assert.That(style.ToLower(), Does.Contain("background-color")); - } + Assert.That(style.ToLower(), Does.Contain("background-color")); + } - public void ShouldCorrectlyReportValueOfColspan() - { - driver.Url = tables; - System.Threading.Thread.Sleep(1000); + public void ShouldCorrectlyReportValueOfColspan() + { + driver.Url = tables; + System.Threading.Thread.Sleep(1000); - IWebElement th1 = driver.FindElement(By.Id("th1")); - IWebElement td2 = driver.FindElement(By.Id("td2")); + IWebElement th1 = driver.FindElement(By.Id("th1")); + IWebElement td2 = driver.FindElement(By.Id("td2")); - Assert.That(th1.GetAttribute("id"), Is.EqualTo("th1"), "th1 id"); - Assert.That(th1.GetAttribute("colspan"), Is.EqualTo("3"), "th1 colspan should be 3"); + Assert.That(th1.GetAttribute("id"), Is.EqualTo("th1"), "th1 id"); + Assert.That(th1.GetAttribute("colspan"), Is.EqualTo("3"), "th1 colspan should be 3"); - Assert.That(td2.GetAttribute("id"), Is.EqualTo("td2"), "td2 id"); - Assert.That(td2.GetAttribute("colspan"), Is.EqualTo("2"), "td2 colspan should be 2"); - } + Assert.That(td2.GetAttribute("id"), Is.EqualTo("td2"), "td2 id"); + Assert.That(td2.GetAttribute("colspan"), Is.EqualTo("2"), "td2 colspan should be 2"); + } - // This is a test-case re-creating issue 900. - [Test] - public void ShouldReturnValueOfOnClickAttribute() - { - driver.Url = javascriptPage; + // This is a test-case re-creating issue 900. + [Test] + public void ShouldReturnValueOfOnClickAttribute() + { + driver.Url = javascriptPage; - IWebElement mouseclickDiv = driver.FindElement(By.Id("mouseclick")); + IWebElement mouseclickDiv = driver.FindElement(By.Id("mouseclick")); - string onClickValue = mouseclickDiv.GetAttribute("onclick"); - string expectedOnClickValue = "displayMessage('mouse click');"; - List acceptableOnClickValues = new List(); - acceptableOnClickValues.Add("javascript:" + expectedOnClickValue); - acceptableOnClickValues.Add("function anonymous()\n{\n" + expectedOnClickValue + "\n}"); - acceptableOnClickValues.Add("function onclick()\n{\n" + expectedOnClickValue + "\n}"); - Assert.That(acceptableOnClickValues, Contains.Item(onClickValue)); + string onClickValue = mouseclickDiv.GetAttribute("onclick"); + string expectedOnClickValue = "displayMessage('mouse click');"; + List acceptableOnClickValues = new List(); + acceptableOnClickValues.Add("javascript:" + expectedOnClickValue); + acceptableOnClickValues.Add("function anonymous()\n{\n" + expectedOnClickValue + "\n}"); + acceptableOnClickValues.Add("function onclick()\n{\n" + expectedOnClickValue + "\n}"); + Assert.That(acceptableOnClickValues, Contains.Item(onClickValue)); - IWebElement mousedownDiv = driver.FindElement(By.Id("mousedown")); - Assert.That(mousedownDiv.GetAttribute("onclick"), Is.Null); - } + IWebElement mousedownDiv = driver.FindElement(By.Id("mousedown")); + Assert.That(mousedownDiv.GetAttribute("onclick"), Is.Null); + } - [Test] - public void GetAttributeDoesNotReturnAnObjectForSvgProperties() + [Test] + public void GetAttributeDoesNotReturnAnObjectForSvgProperties() + { + if (TestUtilities.IsOldIE(driver)) { - if (TestUtilities.IsOldIE(driver)) - { - Assert.Ignore("IE8 and earlier do not support SVG"); - } - - driver.Url = svgPage; - IWebElement svgElement = driver.FindElement(By.Id("rotate")); - Assert.That(svgElement.GetAttribute("transform"), Is.EqualTo("rotate(30)")); + Assert.Ignore("IE8 and earlier do not support SVG"); } - [Test] - public void CanRetrieveTheCurrentValueOfATextFormField_textInput() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("working")); - Assert.That(element.GetAttribute("value"), Is.Empty); - element.SendKeys("hello world"); - Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); - } + driver.Url = svgPage; + IWebElement svgElement = driver.FindElement(By.Id("rotate")); + Assert.That(svgElement.GetAttribute("transform"), Is.EqualTo("rotate(30)")); + } - [Test] - public void CanRetrieveTheCurrentValueOfATextFormField_emailInput() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("email")); - Assert.That(element.GetAttribute("value"), Is.Empty); - element.SendKeys("hello world"); - Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); - } + [Test] + public void CanRetrieveTheCurrentValueOfATextFormField_textInput() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("working")); + Assert.That(element.GetAttribute("value"), Is.Empty); + element.SendKeys("hello world"); + Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); + } - [Test] - public void CanRetrieveTheCurrentValueOfATextFormField_textArea() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("emptyTextArea")); - Assert.That(element.GetAttribute("value"), Is.Empty); - element.SendKeys("hello world"); - Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); - } + [Test] + public void CanRetrieveTheCurrentValueOfATextFormField_emailInput() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("email")); + Assert.That(element.GetAttribute("value"), Is.Empty); + element.SendKeys("hello world"); + Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); + } - [Test] - public void ShouldReturnNullForNonPresentBooleanAttributes() - { - driver.Url = booleanAttributes; - IWebElement element1 = driver.FindElement(By.Id("working")); - Assert.That(element1.GetAttribute("required"), Is.Null); - IWebElement element2 = driver.FindElement(By.Id("wallace")); - Assert.That(element2.GetAttribute("nowrap"), Is.Null); - } + [Test] + public void CanRetrieveTheCurrentValueOfATextFormField_textArea() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("emptyTextArea")); + Assert.That(element.GetAttribute("value"), Is.Empty); + element.SendKeys("hello world"); + Assert.That(element.GetAttribute("value"), Is.EqualTo("hello world")); + } - [Test] - public void ShouldReturnTrueForPresentBooleanAttributes() - { - driver.Url = booleanAttributes; - IWebElement element1 = driver.FindElement(By.Id("emailRequired")); - Assert.That(element1.GetAttribute("required"), Is.EqualTo("true")); - IWebElement element2 = driver.FindElement(By.Id("emptyTextAreaRequired")); - Assert.That(element2.GetAttribute("required"), Is.EqualTo("true")); - IWebElement element3 = driver.FindElement(By.Id("inputRequired")); - Assert.That(element3.GetAttribute("required"), Is.EqualTo("true")); - IWebElement element4 = driver.FindElement(By.Id("textAreaRequired")); - Assert.That(element4.GetAttribute("required"), Is.EqualTo("true")); - IWebElement element5 = driver.FindElement(By.Id("unwrappable")); - Assert.That(element5.GetAttribute("nowrap"), Is.EqualTo("true")); - } + [Test] + public void ShouldReturnNullForNonPresentBooleanAttributes() + { + driver.Url = booleanAttributes; + IWebElement element1 = driver.FindElement(By.Id("working")); + Assert.That(element1.GetAttribute("required"), Is.Null); + IWebElement element2 = driver.FindElement(By.Id("wallace")); + Assert.That(element2.GetAttribute("nowrap"), Is.Null); + } - [Test] - public void MultipleAttributeShouldBeNullWhenNotSet() - { - driver.Url = selectPage; - IWebElement element = driver.FindElement(By.Id("selectWithoutMultiple")); - Assert.That(element.GetAttribute("multiple"), Is.Null); - } + [Test] + public void ShouldReturnTrueForPresentBooleanAttributes() + { + driver.Url = booleanAttributes; + IWebElement element1 = driver.FindElement(By.Id("emailRequired")); + Assert.That(element1.GetAttribute("required"), Is.EqualTo("true")); + IWebElement element2 = driver.FindElement(By.Id("emptyTextAreaRequired")); + Assert.That(element2.GetAttribute("required"), Is.EqualTo("true")); + IWebElement element3 = driver.FindElement(By.Id("inputRequired")); + Assert.That(element3.GetAttribute("required"), Is.EqualTo("true")); + IWebElement element4 = driver.FindElement(By.Id("textAreaRequired")); + Assert.That(element4.GetAttribute("required"), Is.EqualTo("true")); + IWebElement element5 = driver.FindElement(By.Id("unwrappable")); + Assert.That(element5.GetAttribute("nowrap"), Is.EqualTo("true")); + } - [Test] - public void MultipleAttributeShouldBeTrueWhenSet() - { - driver.Url = selectPage; - IWebElement element = driver.FindElement(By.Id("selectWithMultipleEqualsMultiple")); - Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); - } + [Test] + public void MultipleAttributeShouldBeNullWhenNotSet() + { + driver.Url = selectPage; + IWebElement element = driver.FindElement(By.Id("selectWithoutMultiple")); + Assert.That(element.GetAttribute("multiple"), Is.Null); + } - [Test] - public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsBlank() - { - driver.Url = selectPage; - IWebElement element = driver.FindElement(By.Id("selectWithEmptyStringMultiple")); - Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); - } + [Test] + public void MultipleAttributeShouldBeTrueWhenSet() + { + driver.Url = selectPage; + IWebElement element = driver.FindElement(By.Id("selectWithMultipleEqualsMultiple")); + Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); + } - [Test] - public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithoutAValue() - { - driver.Url = selectPage; - IWebElement element = driver.FindElement(By.Id("selectWithMultipleWithoutValue")); - Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); - } + [Test] + public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsBlank() + { + driver.Url = selectPage; + IWebElement element = driver.FindElement(By.Id("selectWithEmptyStringMultiple")); + Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); + } - [Test] - public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsSomethingElse() - { - driver.Url = selectPage; - IWebElement element = driver.FindElement(By.Id("selectWithRandomMultipleValue")); - Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); - } + [Test] + public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithoutAValue() + { + driver.Url = selectPage; + IWebElement element = driver.FindElement(By.Id("selectWithMultipleWithoutValue")); + Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); + } - [Test] - public void GetAttributeOfUserDefinedProperty() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("userDefinedProperty.html"); - IWebElement element = driver.FindElement(By.Id("d")); - Assert.That(element.GetAttribute("dynamicProperty"), Is.EqualTo("sampleValue")); - } + [Test] + public void MultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsSomethingElse() + { + driver.Url = selectPage; + IWebElement element = driver.FindElement(By.Id("selectWithRandomMultipleValue")); + Assert.That(element.GetAttribute("multiple"), Is.EqualTo("true")); + } - [Test] - public void ShouldReturnValueOfClassAttributeOfAnElementAfterSwitchingIFrame() - { - driver.Url = iframePage; - driver.SwitchTo().Frame("iframe1"); + [Test] + public void GetAttributeOfUserDefinedProperty() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("userDefinedProperty.html"); + IWebElement element = driver.FindElement(By.Id("d")); + Assert.That(element.GetAttribute("dynamicProperty"), Is.EqualTo("sampleValue")); + } - IWebElement wallace = driver.FindElement(By.XPath("//div[@id='wallace']")); - String className = wallace.GetAttribute("class"); - Assert.That(className, Is.EqualTo("gromit")); - } + [Test] + public void ShouldReturnValueOfClassAttributeOfAnElementAfterSwitchingIFrame() + { + driver.Url = iframePage; + driver.SwitchTo().Frame("iframe1"); + + IWebElement wallace = driver.FindElement(By.XPath("//div[@id='wallace']")); + String className = wallace.GetAttribute("class"); + Assert.That(className, Is.EqualTo("gromit")); } } diff --git a/dotnet/test/common/ElementElementFindingTest.cs b/dotnet/test/common/ElementElementFindingTest.cs index 26d75b6279374..9f439f1cd3383 100644 --- a/dotnet/test/common/ElementElementFindingTest.cs +++ b/dotnet/test/common/ElementElementFindingTest.cs @@ -20,156 +20,155 @@ using NUnit.Framework; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +// TODO(andre.nogueira): Find better name. This class is to distinguish +// finding elements in the driver (whole page), and inside other elements +[TestFixture] +public class ElementElementFindingTest : DriverTestFixture { - // TODO(andre.nogueira): Find better name. This class is to distinguish - // finding elements in the driver (whole page), and inside other elements - [TestFixture] - public class ElementElementFindingTest : DriverTestFixture + #region FindElemement Tests + + [Test] + public void ShouldFindElementById() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Id("test_id_div")); + IWebElement child = parent.FindElement(By.Id("test_id")); + Assert.That(child.Text, Is.EqualTo("inside")); + } + + [Test] + public void ShouldFindElementByLinkText() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + IWebElement child = parent.FindElement(By.PartialLinkText("hello world")); + Assert.That(child.Text, Is.EqualTo("hello world")); + } + + [Test] + public void ShouldFindElementByName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + IWebElement child = parent.FindElement(By.Name("link1")); + Assert.That(child.Text, Is.EqualTo("hello world")); + } + + [Test] + public void ShouldFindElementByXPath() { - #region FindElemement Tests - - [Test] - public void ShouldFindElementById() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Id("test_id_div")); - IWebElement child = parent.FindElement(By.Id("test_id")); - Assert.That(child.Text, Is.EqualTo("inside")); - } - - [Test] - public void ShouldFindElementByLinkText() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - IWebElement child = parent.FindElement(By.PartialLinkText("hello world")); - Assert.That(child.Text, Is.EqualTo("hello world")); - } - - [Test] - public void ShouldFindElementByName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - IWebElement child = parent.FindElement(By.Name("link1")); - Assert.That(child.Text, Is.EqualTo("hello world")); - } - - [Test] - public void ShouldFindElementByXPath() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Id("test_id_div")); - IWebElement child = parent.FindElement(By.XPath("p")); - Assert.That(child.Text, Is.EqualTo("inside")); - } - - [Test] - public void ShouldFindElementByClassName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - IWebElement child = parent.FindElement(By.ClassName("oneother")); - Assert.That(child.Text, Is.EqualTo("But not me")); - } - - [Test] - public void ShouldFindElementByPartialLinkText() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - IWebElement child = parent.FindElement(By.PartialLinkText(" world")); - Assert.That(child.Text, Is.EqualTo("hello world")); - } - - [Test] - public void ShouldFindElementByTagName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Id("test_id_div")); - IWebElement child = parent.FindElement(By.TagName("p")); - Assert.That(child.Text, Is.EqualTo("inside")); - } - #endregion - - #region FindElemements Tests - - [Test] - public void ShouldFindElementsById() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("form2")); - ReadOnlyCollection children = parent.FindElements(By.Id("2")); - Assert.That(children, Has.Count.EqualTo(2)); - } - - [Test] - public void ShouldFindElementsByLinkText() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - ReadOnlyCollection children = parent.FindElements(By.PartialLinkText("hello world")); - Assert.That(children, Has.Count.EqualTo(2)); - Assert.That(children[0].Text, Is.EqualTo("hello world")); - Assert.That(children[1].Text, Is.EqualTo("hello world")); - } - - [Test] - public void ShouldFindElementsByName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("form2")); - ReadOnlyCollection children = parent.FindElements(By.Name("selectomatic")); - Assert.That(children, Has.Count.EqualTo(2)); - } - - [Test] - public void ShouldFindElementsByXPath() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - ReadOnlyCollection children = parent.FindElements(By.XPath("span")); - Assert.That(children, Has.Count.EqualTo(3)); - Assert.That(children[0].Text, Is.EqualTo("Find me")); - Assert.That(children[1].Text, Is.EqualTo("Also me")); - Assert.That(children[2].Text, Is.EqualTo("But not me")); - } - - [Test] - public void ShouldFindElementsByClassName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - ReadOnlyCollection children = parent.FindElements(By.ClassName("one")); - Assert.That(children, Has.Count.EqualTo(2)); - Assert.That(children[0].Text, Is.EqualTo("Find me")); - Assert.That(children[1].Text, Is.EqualTo("Also me")); - } - - [Test] - public void ShouldFindElementsByPartialLinkText() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("div1")); - ReadOnlyCollection children = parent.FindElements(By.PartialLinkText("hello ")); - Assert.That(children, Has.Count.EqualTo(2)); - Assert.That(children[0].Text, Is.EqualTo("hello world")); - Assert.That(children[1].Text, Is.EqualTo("hello world")); - } - - [Test] - public void ShouldFindElementsByTagName() - { - driver.Url = nestedPage; - IWebElement parent = driver.FindElement(By.Name("classes")); - ReadOnlyCollection children = parent.FindElements(By.TagName("span")); - Assert.That(children, Has.Count.EqualTo(3)); - Assert.That(children[0].Text, Is.EqualTo("Find me")); - Assert.That(children[1].Text, Is.EqualTo("Also me")); - Assert.That(children[2].Text, Is.EqualTo("But not me")); - } - - #endregion + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Id("test_id_div")); + IWebElement child = parent.FindElement(By.XPath("p")); + Assert.That(child.Text, Is.EqualTo("inside")); } + + [Test] + public void ShouldFindElementByClassName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + IWebElement child = parent.FindElement(By.ClassName("oneother")); + Assert.That(child.Text, Is.EqualTo("But not me")); + } + + [Test] + public void ShouldFindElementByPartialLinkText() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + IWebElement child = parent.FindElement(By.PartialLinkText(" world")); + Assert.That(child.Text, Is.EqualTo("hello world")); + } + + [Test] + public void ShouldFindElementByTagName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Id("test_id_div")); + IWebElement child = parent.FindElement(By.TagName("p")); + Assert.That(child.Text, Is.EqualTo("inside")); + } + #endregion + + #region FindElemements Tests + + [Test] + public void ShouldFindElementsById() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("form2")); + ReadOnlyCollection children = parent.FindElements(By.Id("2")); + Assert.That(children, Has.Count.EqualTo(2)); + } + + [Test] + public void ShouldFindElementsByLinkText() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + ReadOnlyCollection children = parent.FindElements(By.PartialLinkText("hello world")); + Assert.That(children, Has.Count.EqualTo(2)); + Assert.That(children[0].Text, Is.EqualTo("hello world")); + Assert.That(children[1].Text, Is.EqualTo("hello world")); + } + + [Test] + public void ShouldFindElementsByName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("form2")); + ReadOnlyCollection children = parent.FindElements(By.Name("selectomatic")); + Assert.That(children, Has.Count.EqualTo(2)); + } + + [Test] + public void ShouldFindElementsByXPath() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + ReadOnlyCollection children = parent.FindElements(By.XPath("span")); + Assert.That(children, Has.Count.EqualTo(3)); + Assert.That(children[0].Text, Is.EqualTo("Find me")); + Assert.That(children[1].Text, Is.EqualTo("Also me")); + Assert.That(children[2].Text, Is.EqualTo("But not me")); + } + + [Test] + public void ShouldFindElementsByClassName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + ReadOnlyCollection children = parent.FindElements(By.ClassName("one")); + Assert.That(children, Has.Count.EqualTo(2)); + Assert.That(children[0].Text, Is.EqualTo("Find me")); + Assert.That(children[1].Text, Is.EqualTo("Also me")); + } + + [Test] + public void ShouldFindElementsByPartialLinkText() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("div1")); + ReadOnlyCollection children = parent.FindElements(By.PartialLinkText("hello ")); + Assert.That(children, Has.Count.EqualTo(2)); + Assert.That(children[0].Text, Is.EqualTo("hello world")); + Assert.That(children[1].Text, Is.EqualTo("hello world")); + } + + [Test] + public void ShouldFindElementsByTagName() + { + driver.Url = nestedPage; + IWebElement parent = driver.FindElement(By.Name("classes")); + ReadOnlyCollection children = parent.FindElements(By.TagName("span")); + Assert.That(children, Has.Count.EqualTo(3)); + Assert.That(children[0].Text, Is.EqualTo("Find me")); + Assert.That(children[1].Text, Is.EqualTo("Also me")); + Assert.That(children[2].Text, Is.EqualTo("But not me")); + } + + #endregion } diff --git a/dotnet/test/common/ElementEqualityTest.cs b/dotnet/test/common/ElementEqualityTest.cs index 0ecf01983c9b8..f6d8d25e51f56 100644 --- a/dotnet/test/common/ElementEqualityTest.cs +++ b/dotnet/test/common/ElementEqualityTest.cs @@ -21,62 +21,61 @@ using OpenQA.Selenium.Environment; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ElementEqualityTest : DriverTestFixture { - [TestFixture] - public class ElementEqualityTest : DriverTestFixture + [Test] + public void SameElementLookedUpDifferentWaysShouldBeEqual() { - [Test] - public void SameElementLookedUpDifferentWaysShouldBeEqual() - { - driver.Url = (simpleTestPage); + driver.Url = (simpleTestPage); - IWebElement body = driver.FindElement(By.TagName("body")); - IWebElement xbody = driver.FindElement(By.XPath("//body")); + IWebElement body = driver.FindElement(By.TagName("body")); + IWebElement xbody = driver.FindElement(By.XPath("//body")); - Assert.That(xbody, Is.EqualTo(body)); - } + Assert.That(xbody, Is.EqualTo(body)); + } - [Test] - public void DifferentElementsShouldNotBeEqual() - { - driver.Url = (simpleTestPage); + [Test] + public void DifferentElementsShouldNotBeEqual() + { + driver.Url = (simpleTestPage); - ReadOnlyCollection ps = driver.FindElements(By.TagName("p")); + ReadOnlyCollection ps = driver.FindElements(By.TagName("p")); - Assert.That(ps[1], Is.Not.EqualTo(ps[0])); - } + Assert.That(ps[1], Is.Not.EqualTo(ps[0])); + } - [Test] - public void SameElementLookedUpDifferentWaysUsingFindElementShouldHaveSameHashCode() - { - driver.Url = (simpleTestPage); - IWebElement body = driver.FindElement(By.TagName("body")); - IWebElement xbody = driver.FindElement(By.XPath("//body")); + [Test] + public void SameElementLookedUpDifferentWaysUsingFindElementShouldHaveSameHashCode() + { + driver.Url = (simpleTestPage); + IWebElement body = driver.FindElement(By.TagName("body")); + IWebElement xbody = driver.FindElement(By.XPath("//body")); - Assert.That(xbody.GetHashCode(), Is.EqualTo(body.GetHashCode())); - } + Assert.That(xbody.GetHashCode(), Is.EqualTo(body.GetHashCode())); + } - public void SameElementLookedUpDifferentWaysUsingFindElementsShouldHaveSameHashCode() - { - driver.Url = (simpleTestPage); - ReadOnlyCollection body = driver.FindElements(By.TagName("body")); - ReadOnlyCollection xbody = driver.FindElements(By.XPath("//body")); + public void SameElementLookedUpDifferentWaysUsingFindElementsShouldHaveSameHashCode() + { + driver.Url = (simpleTestPage); + ReadOnlyCollection body = driver.FindElements(By.TagName("body")); + ReadOnlyCollection xbody = driver.FindElements(By.XPath("//body")); - Assert.That(xbody[0].GetHashCode(), Is.EqualTo(body[0].GetHashCode())); - } + Assert.That(xbody[0].GetHashCode(), Is.EqualTo(body[0].GetHashCode())); + } - [Test] - public void AnElementFoundInViaJsShouldHaveSameId() - { - driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("missedJsReference.html"); + [Test] + public void AnElementFoundInViaJsShouldHaveSameId() + { + driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("missedJsReference.html"); - driver.SwitchTo().Frame("inner"); - IWebElement first = driver.FindElement(By.Id("oneline")); + driver.SwitchTo().Frame("inner"); + IWebElement first = driver.FindElement(By.Id("oneline")); - IWebElement element = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('oneline');"); + IWebElement element = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementById('oneline');"); - Assert.That(element, Is.EqualTo(first)); - } + Assert.That(element, Is.EqualTo(first)); } } diff --git a/dotnet/test/common/ElementFindingTest.cs b/dotnet/test/common/ElementFindingTest.cs index e6a02b9e458f4..4ff8f7bab9b89 100644 --- a/dotnet/test/common/ElementFindingTest.cs +++ b/dotnet/test/common/ElementFindingTest.cs @@ -22,1002 +22,1001 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ElementFindingTest : DriverTestFixture { - [TestFixture] - public class ElementFindingTest : DriverTestFixture + // By.id positive + + [Test] + public void ShouldBeAbleToFindASingleElementById() { - // By.id positive + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.Id("linkId")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("linkId")); + } - [Test] - public void ShouldBeAbleToFindASingleElementById() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.Id("linkId")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("linkId")); - } + [Test] + public void ShouldBeAbleToFindASingleElementByNumericId() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("2")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByNumericId() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("2")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("2")); - } + [Test] + public void ShouldBeAbleToFindASingleElementByIdWithNonAlphanumericCharacters() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Id("white space")); + Assert.That(element.Text, Is.EqualTo("space")); + IWebElement element2 = driver.FindElement(By.Id("css#.chars")); + Assert.That(element2.Text, Is.EqualTo("css escapes")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByIdWithNonAlphanumericCharacters() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Id("white space")); - Assert.That(element.Text, Is.EqualTo("space")); - IWebElement element2 = driver.FindElement(By.Id("css#.chars")); - Assert.That(element2.Text, Is.EqualTo("css escapes")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsById() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("2")); + Assert.That(elements, Has.Exactly(8).Items); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsById() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("2")); - Assert.That(elements, Has.Exactly(8).Items); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByNumericId() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("2")); + Assert.That(elements, Has.Exactly(8).Items); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByNumericId() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("2")); - Assert.That(elements, Has.Exactly(8).Items); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByIdWithNonAlphanumericCharacters() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("white space")); + Assert.That(elements, Has.Exactly(2).Items); + ReadOnlyCollection elements2 = driver.FindElements(By.Id("css#.chars")); + Assert.That(elements2, Has.Exactly(2).Items); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByIdWithNonAlphanumericCharacters() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("white space")); - Assert.That(elements, Has.Exactly(2).Items); - ReadOnlyCollection elements2 = driver.FindElements(By.Id("css#.chars")); - Assert.That(elements2, Has.Exactly(2).Items); - } + // By.id negative - // By.id negative + [Test] + public void ShouldNotBeAbleToLocateByIdASingleElementThatDoesNotExist() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Id("nonExistentButton")), Throws.InstanceOf()); + } - [Test] - public void ShouldNotBeAbleToLocateByIdASingleElementThatDoesNotExist() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Id("nonExistentButton")), Throws.InstanceOf()); - } + [Test] + public void ShouldNotBeAbleToLocateByIdMultipleElementsThatDoNotExist() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("nonExistentButton")); + Assert.That(elements, Is.Empty); + } - [Test] - public void ShouldNotBeAbleToLocateByIdMultipleElementsThatDoNotExist() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("nonExistentButton")); - Assert.That(elements, Is.Empty); - } + [Test] + public void FindingASingleElementByEmptyIdShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Id("")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByEmptyIdShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Id("")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByEmptyIdShouldReturnEmptyList() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByEmptyIdShouldReturnEmptyList() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("")); - Assert.That(elements, Is.Empty); - } + [Test] + public void FindingASingleElementByIdWithSpaceShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Id("nonexistent button")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByIdWithSpaceShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Id("nonexistent button")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByIdWithSpaceShouldReturnEmptyList() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Id("nonexistent button")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByIdWithSpaceShouldReturnEmptyList() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Id("nonexistent button")); - Assert.That(elements, Is.Empty); - } + // By.Name positive - // By.Name positive + [Test] + public void ShouldBeAbleToFindASingleElementByName() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Name("checky")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("furrfu")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByName() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Name("checky")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("furrfu")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByName() + { + driver.Url = nestedPage; + ReadOnlyCollection elements = driver.FindElements(By.Name("checky")); + Assert.That(elements, Has.Count.GreaterThan(1)); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByName() - { - driver.Url = nestedPage; - ReadOnlyCollection elements = driver.FindElements(By.Name("checky")); - Assert.That(elements, Has.Count.GreaterThan(1)); - } + [Test] + public void ShouldBeAbleToFindAnElementThatDoesNotSupportTheNameProperty() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.Name("div1")); + Assert.That(element.GetAttribute("name"), Is.EqualTo("div1")); + } - [Test] - public void ShouldBeAbleToFindAnElementThatDoesNotSupportTheNameProperty() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.Name("div1")); - Assert.That(element.GetAttribute("name"), Is.EqualTo("div1")); - } + // By.Name negative - // By.Name negative + [Test] + public void ShouldNotBeAbleToLocateByNameASingleElementThatDoesNotExist() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Name("nonExistentButton")), Throws.InstanceOf()); + } - [Test] - public void ShouldNotBeAbleToLocateByNameASingleElementThatDoesNotExist() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Name("nonExistentButton")), Throws.InstanceOf()); - } + [Test] + public void ShouldNotBeAbleToLocateByNameMultipleElementsThatDoNotExist() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Name("nonExistentButton")); + Assert.That(elements, Is.Empty); + } - [Test] - public void ShouldNotBeAbleToLocateByNameMultipleElementsThatDoNotExist() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Name("nonExistentButton")); - Assert.That(elements, Is.Empty); - } + [Test] + public void FindingASingleElementByEmptyNameShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Name("")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByEmptyNameShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Name("")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByEmptyNameShouldReturnEmptyList() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Name("")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByEmptyNameShouldReturnEmptyList() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Name("")); - Assert.That(elements, Is.Empty); - } + [Test] + public void FindingASingleElementByNameWithSpaceShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Name("nonexistent button")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByNameWithSpaceShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Name("nonexistent button")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByNameWithSpaceShouldReturnEmptyList() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.Name("nonexistent button")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByNameWithSpaceShouldReturnEmptyList() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.Name("nonexistent button")); - Assert.That(elements, Is.Empty); - } + // By.tagName positive - // By.tagName positive + [Test] + public void ShouldBeAbleToFindASingleElementByTagName() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.TagName("input")); + Assert.That(element.TagName.ToLower(), Is.EqualTo("input")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByTagName() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.TagName("input")); - Assert.That(element.TagName.ToLower(), Is.EqualTo("input")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByTagName() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.TagName("input")); + Assert.That(elements, Has.Count.GreaterThan(1)); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByTagName() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.TagName("input")); - Assert.That(elements, Has.Count.GreaterThan(1)); - } + // By.tagName negative - // By.tagName negative + [Test] + public void ShouldNotBeAbleToLocateByTagNameASingleElementThatDoesNotExist() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.TagName("nonExistentButton")), Throws.InstanceOf()); + } - [Test] - public void ShouldNotBeAbleToLocateByTagNameASingleElementThatDoesNotExist() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.TagName("nonExistentButton")), Throws.InstanceOf()); - } + [Test] + public void ShouldNotBeAbleToLocateByTagNameMultipleElementsThatDoNotExist() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.TagName("nonExistentButton")); + Assert.That(elements, Is.Empty); + } - [Test] - public void ShouldNotBeAbleToLocateByTagNameMultipleElementsThatDoNotExist() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.TagName("nonExistentButton")); - Assert.That(elements, Is.Empty); - } + [Test] + public void FindingASingleElementByEmptyTagNameShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.TagName("")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByEmptyTagNameShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.TagName("")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByTagNameWithSpaceShouldThrow() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.TagName("nonexistent button")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByTagNameWithSpaceShouldThrow() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.TagName("nonexistent button")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByTagNameWithSpaceShouldReturnEmptyList() + { + driver.Url = formsPage; + ReadOnlyCollection elements = driver.FindElements(By.TagName("nonexistent button")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByTagNameWithSpaceShouldReturnEmptyList() - { - driver.Url = formsPage; - ReadOnlyCollection elements = driver.FindElements(By.TagName("nonexistent button")); - Assert.That(elements, Is.Empty); - } + // By.ClassName positive - // By.ClassName positive + [Test] + public void ShouldBeAbleToFindASingleElementByClass() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("extraDiv")); + Assert.That(element.Text, Does.StartWith("Another div starts here.")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByClass() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.ClassName("extraDiv")); - Assert.That(element.Text, Does.StartWith("Another div starts here.")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByClassName() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.ClassName("nameC")); + Assert.That(elements, Has.Count.GreaterThan(1)); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByClassName() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.ClassName("nameC")); - Assert.That(elements, Has.Count.GreaterThan(1)); - } + [Test] + public void ShouldFindElementByClassWhenItIsTheFirstNameAmongMany() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("nameA")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - [Test] - public void ShouldFindElementByClassWhenItIsTheFirstNameAmongMany() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.ClassName("nameA")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementByClassWhenItIsTheLastNameAmongMany() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("nameC")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - [Test] - public void ShouldFindElementByClassWhenItIsTheLastNameAmongMany() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.ClassName("nameC")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementByClassWhenItIsInTheMiddleAmongMany() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("nameBnoise")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - [Test] - public void ShouldFindElementByClassWhenItIsInTheMiddleAmongMany() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.ClassName("nameBnoise")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementByClassWhenItsNameIsSurroundedByWhitespace() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("spaceAround")); + Assert.That(element.Text, Is.EqualTo("Spaced out")); + } - [Test] - public void ShouldFindElementByClassWhenItsNameIsSurroundedByWhitespace() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.ClassName("spaceAround")); - Assert.That(element.Text, Is.EqualTo("Spaced out")); - } + [Test] + public void ShouldFindElementsByClassWhenItsNameIsSurroundedByWhitespace() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.ClassName("spaceAround")); + Assert.That(elements, Has.Exactly(1).Items); + Assert.That(elements[0].Text, Is.EqualTo("Spaced out")); + } - [Test] - public void ShouldFindElementsByClassWhenItsNameIsSurroundedByWhitespace() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.ClassName("spaceAround")); - Assert.That(elements, Has.Exactly(1).Items); - Assert.That(elements[0].Text, Is.EqualTo("Spaced out")); - } + // By.ClassName negative - // By.ClassName negative + [Test] + public void ShouldNotFindElementByClassWhenTheNameQueriedIsShorterThanCandidateName() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.ClassName("nameB")), Throws.InstanceOf()); + } - [Test] - public void ShouldNotFindElementByClassWhenTheNameQueriedIsShorterThanCandidateName() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.ClassName("nameB")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByEmptyClassNameShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.ClassName("")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByEmptyClassNameShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.ClassName("")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByEmptyClassNameShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElements(By.ClassName("")), Throws.InstanceOf()); + } - [Test] - public void FindingMultipleElementsByEmptyClassNameShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElements(By.ClassName("")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByCompoundClassNameShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.ClassName("a b")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByCompoundClassNameShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.ClassName("a b")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByCompoundClassNameShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElements(By.ClassName("a b")), Throws.InstanceOf()); + } - [Test] - public void FindingMultipleElementsByCompoundClassNameShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElements(By.ClassName("a b")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByAWeirdLookingClassName() + { + driver.Url = xhtmlTestPage; + String weird = "cls-!@#$%^&*"; + IWebElement element = driver.FindElement(By.ClassName(weird)); + Assert.That(element.GetAttribute("class"), Is.EqualTo(weird)); + } - [Test] - public void FindingASingleElementByAWeirdLookingClassName() - { - driver.Url = xhtmlTestPage; - String weird = "cls-!@#$%^&*"; - IWebElement element = driver.FindElement(By.ClassName(weird)); - Assert.That(element.GetAttribute("class"), Is.EqualTo(weird)); - } + [Test] + public void FindingMultipleElementsByAWeirdLookingClassName() + { + driver.Url = xhtmlTestPage; + String weird = "cls-!@#$%^&*"; + ReadOnlyCollection elements = driver.FindElements(By.ClassName(weird)); + Assert.That(elements, Has.Count.EqualTo(1)); + Assert.That(elements[0].GetAttribute("class"), Is.EqualTo(weird)); + } - [Test] - public void FindingMultipleElementsByAWeirdLookingClassName() - { - driver.Url = xhtmlTestPage; - String weird = "cls-!@#$%^&*"; - ReadOnlyCollection elements = driver.FindElements(By.ClassName(weird)); - Assert.That(elements, Has.Count.EqualTo(1)); - Assert.That(elements[0].GetAttribute("class"), Is.EqualTo(weird)); - } + // By.XPath positive - // By.XPath positive + [Test] + public void ShouldBeAbleToFindASingleElementByXPath() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.XPath("//h1")); + Assert.That(element.Text, Is.EqualTo("XHTML Might Be The Future")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByXPath() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.XPath("//h1")); - Assert.That(element.Text, Is.EqualTo("XHTML Might Be The Future")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByXPath() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.XPath("//div")); + Assert.That(elements, Has.Count.EqualTo(13)); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByXPath() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.XPath("//div")); - Assert.That(elements, Has.Count.EqualTo(13)); - } + [Test] + public void ShouldBeAbleToFindManyElementsRepeatedlyByXPath() + { + driver.Url = xhtmlTestPage; + String xpathString = "//node()[contains(@id,'id')]"; + Assert.That(driver.FindElements(By.XPath(xpathString)), Has.Exactly(3).Items); - [Test] - public void ShouldBeAbleToFindManyElementsRepeatedlyByXPath() - { - driver.Url = xhtmlTestPage; - String xpathString = "//node()[contains(@id,'id')]"; - Assert.That(driver.FindElements(By.XPath(xpathString)), Has.Exactly(3).Items); + xpathString = "//node()[contains(@id,'nope')]"; + Assert.That(driver.FindElements(By.XPath(xpathString)), Is.Empty); + } - xpathString = "//node()[contains(@id,'nope')]"; - Assert.That(driver.FindElements(By.XPath(xpathString)), Is.Empty); - } + [Test] + public void ShouldBeAbleToIdentifyElementsByClass() + { + driver.Url = xhtmlTestPage; + IWebElement header = driver.FindElement(By.XPath("//h1[@class='header']")); + Assert.That(header.Text, Is.EqualTo("XHTML Might Be The Future")); + } - [Test] - public void ShouldBeAbleToIdentifyElementsByClass() - { - driver.Url = xhtmlTestPage; - IWebElement header = driver.FindElement(By.XPath("//h1[@class='header']")); - Assert.That(header.Text, Is.EqualTo("XHTML Might Be The Future")); - } + [Test] + public void ShouldBeAbleToFindAnElementByXPathWithMultipleAttributes() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement( + By.XPath("//form[@name='optional']/input[@type='submit' and @value='Click!']")); + Assert.That(element.TagName.ToLower(), Is.EqualTo("input")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("Click!")); + } - [Test] - public void ShouldBeAbleToFindAnElementByXPathWithMultipleAttributes() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement( - By.XPath("//form[@name='optional']/input[@type='submit' and @value='Click!']")); - Assert.That(element.TagName.ToLower(), Is.EqualTo("input")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("Click!")); - } + [Test] + public void FindingALinkByXpathShouldLocateAnElementWithTheGivenText() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.XPath("//a[text()='click me']")); + Assert.That(element.Text, Is.EqualTo("click me")); + } - [Test] - public void FindingALinkByXpathShouldLocateAnElementWithTheGivenText() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.XPath("//a[text()='click me']")); - Assert.That(element.Text, Is.EqualTo("click me")); - } + [Test] + public void FindingALinkByXpathUsingContainsKeywordShouldWork() + { + driver.Url = nestedPage; + IWebElement element = driver.FindElement(By.XPath("//a[contains(.,'hello world')]")); + Assert.That(element.Text, Does.Contain("hello world")); + } - [Test] - public void FindingALinkByXpathUsingContainsKeywordShouldWork() - { - driver.Url = nestedPage; - IWebElement element = driver.FindElement(By.XPath("//a[contains(.,'hello world')]")); - Assert.That(element.Text, Does.Contain("hello world")); - } + [Test] + [IgnoreBrowser(Browser.IE, "Driver does not support XML namespaces in XPath")] + [IgnoreBrowser(Browser.Firefox, "Driver does not support XML namespaces in XPath")] + [IgnoreBrowser(Browser.Safari, "Not yet implemented")] + public void ShouldBeAbleToFindElementByXPathWithNamespace() + { + driver.Url = svgPage; + IWebElement element = driver.FindElement(By.XPath("//svg:svg//svg:text")); + Assert.That(element.Text, Is.EqualTo("Test Chart")); + } - [Test] - [IgnoreBrowser(Browser.IE, "Driver does not support XML namespaces in XPath")] - [IgnoreBrowser(Browser.Firefox, "Driver does not support XML namespaces in XPath")] - [IgnoreBrowser(Browser.Safari, "Not yet implemented")] - public void ShouldBeAbleToFindElementByXPathWithNamespace() - { - driver.Url = svgPage; - IWebElement element = driver.FindElement(By.XPath("//svg:svg//svg:text")); - Assert.That(element.Text, Is.EqualTo("Test Chart")); - } + // By.XPath negative - // By.XPath negative + [Test] + public void ShouldThrowAnExceptionWhenThereIsNoLinkToClick() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.XPath("//a[@id='Not here']")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowAnExceptionWhenThereIsNoLinkToClick() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.XPath("//a[@id='Not here']")), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInDriverFindElement() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.XPath("this][isnot][valid")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInDriverFindElement() + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInDriverFindElements() + { + if (TestUtilities.IsIE6(driver)) { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.XPath("this][isnot][valid")), Throws.InstanceOf()); + // Ignoring xpath error test in IE6 + return; } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInDriverFindElements() - { - if (TestUtilities.IsIE6(driver)) - { - // Ignoring xpath error test in IE6 - return; - } - - driver.Url = formsPage; - Assert.That(() => driver.FindElements(By.XPath("this][isnot][valid")), Throws.InstanceOf()); - } + driver.Url = formsPage; + Assert.That(() => driver.FindElements(By.XPath("this][isnot][valid")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInElementFindElement() - { - driver.Url = formsPage; - IWebElement body = driver.FindElement(By.TagName("body")); - Assert.That(() => body.FindElement(By.XPath("this][isnot][valid")), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInElementFindElement() + { + driver.Url = formsPage; + IWebElement body = driver.FindElement(By.TagName("body")); + Assert.That(() => body.FindElement(By.XPath("this][isnot][valid")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInElementFindElements() - { - driver.Url = formsPage; - IWebElement body = driver.FindElement(By.TagName("body")); - Assert.That(() => body.FindElements(By.XPath("this][isnot][valid")), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathIsSyntacticallyInvalidInElementFindElements() + { + driver.Url = formsPage; + IWebElement body = driver.FindElement(By.TagName("body")); + Assert.That(() => body.FindElements(By.XPath("this][isnot][valid")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInDriverFindElement() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.XPath("count(//input)")), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInDriverFindElement() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.XPath("count(//input)")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInDriverFindElements() + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInDriverFindElements() + { + if (TestUtilities.IsIE6(driver)) { - if (TestUtilities.IsIE6(driver)) - { - // Ignoring xpath error test in IE6 - return; - } - - driver.Url = formsPage; - Assert.That(() => driver.FindElements(By.XPath("count(//input)")), Throws.InstanceOf()); + // Ignoring xpath error test in IE6 + return; } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInElementFindElement() - { - driver.Url = formsPage; + driver.Url = formsPage; + Assert.That(() => driver.FindElements(By.XPath("count(//input)")), Throws.InstanceOf()); + } - IWebElement body = driver.FindElement(By.TagName("body")); - Assert.That(() => body.FindElement(By.XPath("count(//input)")), Throws.InstanceOf()); - } + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInElementFindElement() + { + driver.Url = formsPage; + + IWebElement body = driver.FindElement(By.TagName("body")); + Assert.That(() => body.FindElement(By.XPath("count(//input)")), Throws.InstanceOf()); + } - [Test] - public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInElementFindElements() + [Test] + public void ShouldThrowInvalidSelectorExceptionWhenXPathReturnsWrongTypeInElementFindElements() + { + if (TestUtilities.IsIE6(driver)) { - if (TestUtilities.IsIE6(driver)) - { - // Ignoring xpath error test in IE6 - return; - } - - driver.Url = formsPage; - IWebElement body = driver.FindElement(By.TagName("body")); - Assert.That(() => body.FindElements(By.XPath("count(//input)")), Throws.InstanceOf()); + // Ignoring xpath error test in IE6 + return; } - // By.CssSelector positive + driver.Url = formsPage; + IWebElement body = driver.FindElement(By.TagName("body")); + Assert.That(() => body.FindElements(By.XPath("count(//input)")), Throws.InstanceOf()); + } - [Test] - public void ShouldBeAbleToFindASingleElementByCssSelector() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.CssSelector("div.content")); - Assert.That(element.TagName.ToLower(), Is.EqualTo("div")); - Assert.That(element.GetAttribute("class"), Is.EqualTo("content")); - } + // By.CssSelector positive - [Test] - public void ShouldBeAbleToFindMultipleElementsByCssSelector() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.CssSelector("p")); - Assert.That(elements, Has.Count.GreaterThan(1)); - } + [Test] + public void ShouldBeAbleToFindASingleElementByCssSelector() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.CssSelector("div.content")); + Assert.That(element.TagName.ToLower(), Is.EqualTo("div")); + Assert.That(element.GetAttribute("class"), Is.EqualTo("content")); + } - [Test] - public void ShouldBeAbleToFindASingleElementByCompoundCssSelector() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.CssSelector("div.extraDiv, div.content")); - Assert.That(element.TagName.ToLower(), Is.EqualTo("div")); - Assert.That(element.GetAttribute("class"), Is.EqualTo("content")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByCssSelector() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.CssSelector("p")); + Assert.That(elements, Has.Count.GreaterThan(1)); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByCompoundCssSelector() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.CssSelector("div.extraDiv, div.content")); - Assert.That(elements, Has.Count.GreaterThan(1)); - Assert.That(elements[0].GetAttribute("class"), Is.EqualTo("content")); - Assert.That(elements[1].GetAttribute("class"), Is.EqualTo("extraDiv")); - } + [Test] + public void ShouldBeAbleToFindASingleElementByCompoundCssSelector() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.CssSelector("div.extraDiv, div.content")); + Assert.That(element.TagName.ToLower(), Is.EqualTo("div")); + Assert.That(element.GetAttribute("class"), Is.EqualTo("content")); + } - [Test] - public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingCssSelector() - { - driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected.html")); - IWebElement element = driver.FindElement(By.CssSelector("option[selected='selected']")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByCompoundCssSelector() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.CssSelector("div.extraDiv, div.content")); + Assert.That(elements, Has.Count.GreaterThan(1)); + Assert.That(elements[0].GetAttribute("class"), Is.EqualTo("content")); + Assert.That(elements[1].GetAttribute("class"), Is.EqualTo("extraDiv")); + } - [Test] - public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingShortCssSelector() - { - driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected.html")); - IWebElement element = driver.FindElement(By.CssSelector("option[selected]")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); - } + [Test] + public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingCssSelector() + { + driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected.html")); + IWebElement element = driver.FindElement(By.CssSelector("option[selected='selected']")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); + } - [Test] - public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingShortCssSelectorOnHtml4Page() - { - driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected_html4.html")); - IWebElement element = driver.FindElement(By.CssSelector("option[selected]")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); - } + [Test] + public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingShortCssSelector() + { + driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected.html")); + IWebElement element = driver.FindElement(By.CssSelector("option[selected]")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); + } - // By.CssSelector negative + [Test] + public void ShouldBeAbleToFindAnElementByBooleanAttributeUsingShortCssSelectorOnHtml4Page() + { + driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("locators_tests/boolean_attribute_selected_html4.html")); + IWebElement element = driver.FindElement(By.CssSelector("option[selected]")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("two")); + } - [Test] - public void ShouldNotFindElementByCssSelectorWhenThereIsNoSuchElement() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.CssSelector(".there-is-no-such-class")), Throws.InstanceOf()); - } + // By.CssSelector negative - [Test] - public void ShouldNotFindElementsByCssSelectorWhenThereIsNoSuchElement() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.CssSelector(".there-is-no-such-class")); - Assert.That(elements, Is.Empty); - } + [Test] + public void ShouldNotFindElementByCssSelectorWhenThereIsNoSuchElement() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.CssSelector(".there-is-no-such-class")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByEmptyCssSelectorShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.CssSelector("")), Throws.InstanceOf()); - } + [Test] + public void ShouldNotFindElementsByCssSelectorWhenThereIsNoSuchElement() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.CssSelector(".there-is-no-such-class")); + Assert.That(elements, Is.Empty); + } - [Test] - public void FindingMultipleElementsByEmptyCssSelectorShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElements(By.CssSelector("")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByEmptyCssSelectorShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.CssSelector("")), Throws.InstanceOf()); + } - [Test] - public void FindingASingleElementByInvalidCssSelectorShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.CssSelector("//a/b/c[@id='1']")), Throws.InstanceOf()); - } + [Test] + public void FindingMultipleElementsByEmptyCssSelectorShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElements(By.CssSelector("")), Throws.InstanceOf()); + } - [Test] - public void FindingMultipleElementsByInvalidCssSelectorShouldThrow() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElements(By.CssSelector("//a/b/c[@id='1']")), Throws.InstanceOf()); - } + [Test] + public void FindingASingleElementByInvalidCssSelectorShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.CssSelector("//a/b/c[@id='1']")), Throws.InstanceOf()); + } - // By.linkText positive + [Test] + public void FindingMultipleElementsByInvalidCssSelectorShouldThrow() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElements(By.CssSelector("//a/b/c[@id='1']")), Throws.InstanceOf()); + } - [Test] - public void ShouldBeAbleToFindALinkByText() - { - driver.Url = xhtmlTestPage; - IWebElement link = driver.FindElement(By.LinkText("click me")); - Assert.That(link.Text, Is.EqualTo("click me")); - } + // By.linkText positive - [Test] - public void ShouldBeAbleToFindMultipleLinksByText() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.LinkText("click me")); - Assert.That(elements, Has.Count.EqualTo(2), "Expected 2 links, got " + elements.Count); - } + [Test] + public void ShouldBeAbleToFindALinkByText() + { + driver.Url = xhtmlTestPage; + IWebElement link = driver.FindElement(By.LinkText("click me")); + Assert.That(link.Text, Is.EqualTo("click me")); + } - [Test] - public void ShouldFindElementByLinkTextContainingEqualsSign() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.LinkText("Link=equalssign")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); - } + [Test] + public void ShouldBeAbleToFindMultipleLinksByText() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.LinkText("click me")); + Assert.That(elements, Has.Count.EqualTo(2), "Expected 2 links, got " + elements.Count); + } - [Test] - public void ShouldFindMultipleElementsByLinkTextContainingEqualsSign() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.LinkText("Link=equalssign")); - Assert.That(elements, Has.Count.EqualTo(1)); - Assert.That(elements[0].GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); - } + [Test] + public void ShouldFindElementByLinkTextContainingEqualsSign() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.LinkText("Link=equalssign")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); + } - [Test] - public void FindsByLinkTextOnXhtmlPage() + [Test] + public void ShouldFindMultipleElementsByLinkTextContainingEqualsSign() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.LinkText("Link=equalssign")); + Assert.That(elements, Has.Count.EqualTo(1)); + Assert.That(elements[0].GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); + } + + [Test] + public void FindsByLinkTextOnXhtmlPage() + { + if (TestUtilities.IsOldIE(driver)) { - if (TestUtilities.IsOldIE(driver)) - { - // Old IE doesn't render XHTML pages, don't try loading XHTML pages in it - return; - } - - driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("actualXhtmlPage.xhtml")); - string linkText = "Foo"; - IWebElement element = driver.FindElement(By.LinkText(linkText)); - Assert.That(element.Text, Is.EqualTo(linkText)); + // Old IE doesn't render XHTML pages, don't try loading XHTML pages in it + return; } - [Test] - [IgnoreBrowser(Browser.Remote)] - public void LinkWithFormattingTags() - { - driver.Url = (simpleTestPage); - IWebElement elem = driver.FindElement(By.Id("links")); + driver.Url = (EnvironmentManager.Instance.UrlBuilder.WhereIs("actualXhtmlPage.xhtml")); + string linkText = "Foo"; + IWebElement element = driver.FindElement(By.LinkText(linkText)); + Assert.That(element.Text, Is.EqualTo(linkText)); + } - IWebElement res = elem.FindElement(By.PartialLinkText("link with formatting tags")); - Assert.That(res.Text, Is.EqualTo("link with formatting tags")); - } + [Test] + [IgnoreBrowser(Browser.Remote)] + public void LinkWithFormattingTags() + { + driver.Url = (simpleTestPage); + IWebElement elem = driver.FindElement(By.Id("links")); - [Test] - public void DriverCanGetLinkByLinkTestIgnoringTrailingWhitespace() - { - driver.Url = simpleTestPage; - IWebElement link = driver.FindElement(By.LinkText("link with trailing space")); - Assert.That(link.GetAttribute("id"), Is.EqualTo("linkWithTrailingSpace")); - Assert.That(link.Text, Is.EqualTo("link with trailing space")); - } + IWebElement res = elem.FindElement(By.PartialLinkText("link with formatting tags")); + Assert.That(res.Text, Is.EqualTo("link with formatting tags")); + } - // By.linkText negative + [Test] + public void DriverCanGetLinkByLinkTestIgnoringTrailingWhitespace() + { + driver.Url = simpleTestPage; + IWebElement link = driver.FindElement(By.LinkText("link with trailing space")); + Assert.That(link.GetAttribute("id"), Is.EqualTo("linkWithTrailingSpace")); + Assert.That(link.Text, Is.EqualTo("link with trailing space")); + } - [Test] - public void ShouldNotBeAbleToLocateByLinkTextASingleElementThatDoesNotExist() - { - driver.Url = xhtmlTestPage; - Assert.That(() => driver.FindElement(By.LinkText("Not here either")), Throws.InstanceOf()); - } + // By.linkText negative - [Test] - public void ShouldNotBeAbleToLocateByLinkTextMultipleElementsThatDoNotExist() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.LinkText("Not here either")); - Assert.That(elements, Is.Empty); - } + [Test] + public void ShouldNotBeAbleToLocateByLinkTextASingleElementThatDoesNotExist() + { + driver.Url = xhtmlTestPage; + Assert.That(() => driver.FindElement(By.LinkText("Not here either")), Throws.InstanceOf()); + } - // By.partialLinkText positive + [Test] + public void ShouldNotBeAbleToLocateByLinkTextMultipleElementsThatDoNotExist() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.LinkText("Not here either")); + Assert.That(elements, Is.Empty); + } - [Test] - public void ShouldBeAbleToFindMultipleElementsByPartialLinkText() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("ick me")); - Assert.That(elements, Has.Exactly(2).Items); - } + // By.partialLinkText positive - [Test] - public void ShouldBeAbleToFindASingleElementByPartialLinkText() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.PartialLinkText("anon")); - Assert.That(element.Text, Does.Contain("anon")); - } + [Test] + public void ShouldBeAbleToFindMultipleElementsByPartialLinkText() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("ick me")); + Assert.That(elements, Has.Exactly(2).Items); + } - [Test] - public void ShouldFindElementByPartialLinkTextContainingEqualsSign() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.PartialLinkText("Link=")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); - } + [Test] + public void ShouldBeAbleToFindASingleElementByPartialLinkText() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.PartialLinkText("anon")); + Assert.That(element.Text, Does.Contain("anon")); + } - [Test] - public void ShouldFindMultipleElementsByPartialLinkTextContainingEqualsSign() - { - driver.Url = xhtmlTestPage; - ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("Link=")); - Assert.That(elements, Has.Count.EqualTo(1)); - Assert.That(elements[0].GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); - } + [Test] + public void ShouldFindElementByPartialLinkTextContainingEqualsSign() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.PartialLinkText("Link=")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); + } - // Misc tests + [Test] + public void ShouldFindMultipleElementsByPartialLinkTextContainingEqualsSign() + { + driver.Url = xhtmlTestPage; + ReadOnlyCollection elements = driver.FindElements(By.PartialLinkText("Link=")); + Assert.That(elements, Has.Count.EqualTo(1)); + Assert.That(elements[0].GetAttribute("id"), Is.EqualTo("linkWithEqualsSign")); + } - [Test] - public void DriverShouldBeAbleToFindElementsAfterLoadingMoreThanOnePageAtATime() - { - driver.Url = formsPage; - driver.Url = xhtmlTestPage; - IWebElement link = driver.FindElement(By.LinkText("click me")); - Assert.That(link.Text, Is.EqualTo("click me")); - } + // Misc tests - // You don't want to ask why this is here - [Test] - public void WhenFindingByNameShouldNotReturnById() - { - driver.Url = formsPage; + [Test] + public void DriverShouldBeAbleToFindElementsAfterLoadingMoreThanOnePageAtATime() + { + driver.Url = formsPage; + driver.Url = xhtmlTestPage; + IWebElement link = driver.FindElement(By.LinkText("click me")); + Assert.That(link.Text, Is.EqualTo("click me")); + } - IWebElement element = driver.FindElement(By.Name("id-name1")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("name")); + // You don't want to ask why this is here + [Test] + public void WhenFindingByNameShouldNotReturnById() + { + driver.Url = formsPage; - element = driver.FindElement(By.Id("id-name1")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("id")); + IWebElement element = driver.FindElement(By.Name("id-name1")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("name")); - element = driver.FindElement(By.Name("id-name2")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("name")); + element = driver.FindElement(By.Id("id-name1")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("id")); - element = driver.FindElement(By.Id("id-name2")); - Assert.That(element.GetAttribute("value"), Is.EqualTo("id")); - } + element = driver.FindElement(By.Name("id-name2")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("name")); - [Test] - public void ShouldBeAbleToFindAHiddenElementsByName() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Name("hidden")); - Assert.That(element.GetAttribute("name"), Is.EqualTo("hidden")); - } + element = driver.FindElement(By.Id("id-name2")); + Assert.That(element.GetAttribute("value"), Is.EqualTo("id")); + } - [Test] - public void ShouldNotBeAbleToFindAnElementOnABlankPage() - { - driver.Url = "about:blank"; - Assert.That(() => driver.FindElement(By.TagName("a")), Throws.InstanceOf()); - } + [Test] + public void ShouldBeAbleToFindAHiddenElementsByName() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Name("hidden")); + Assert.That(element.GetAttribute("name"), Is.EqualTo("hidden")); + } - [Test] - [NeedsFreshDriver(IsCreatedBeforeTest = true)] - public void ShouldNotBeAbleToLocateASingleElementOnABlankPage() - { - // Note we're on the default start page for the browser at this point. - Assert.That(() => driver.FindElement(By.Id("nonExistantButton")), Throws.InstanceOf()); - } + [Test] + public void ShouldNotBeAbleToFindAnElementOnABlankPage() + { + driver.Url = "about:blank"; + Assert.That(() => driver.FindElement(By.TagName("a")), Throws.InstanceOf()); + } - [Test] - [IgnoreBrowser(Browser.Firefox, "Already updated to https://github.com/w3c/webdriver/issues/1594")] - public void AnElementFoundInADifferentFrameIsStale() - { - driver.Url = missedJsReferencePage; - driver.SwitchTo().Frame("inner"); - IWebElement element = driver.FindElement(By.Id("oneline")); - driver.SwitchTo().DefaultContent(); - Assert.That(() => { string foo = element.Text; }, Throws.InstanceOf()); - } + [Test] + [NeedsFreshDriver(IsCreatedBeforeTest = true)] + public void ShouldNotBeAbleToLocateASingleElementOnABlankPage() + { + // Note we're on the default start page for the browser at this point. + Assert.That(() => driver.FindElement(By.Id("nonExistantButton")), Throws.InstanceOf()); + } - ///////////////////////////////////////////////// - // Tests unique to the .NET bindings - ///////////////////////////////////////////////// - [Test] - public void ShouldReturnTitleOfPageIfSet() - { - driver.Url = xhtmlTestPage; - Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); + [Test] + [IgnoreBrowser(Browser.Firefox, "Already updated to https://github.com/w3c/webdriver/issues/1594")] + public void AnElementFoundInADifferentFrameIsStale() + { + driver.Url = missedJsReferencePage; + driver.SwitchTo().Frame("inner"); + IWebElement element = driver.FindElement(By.Id("oneline")); + driver.SwitchTo().DefaultContent(); + Assert.That(() => { string foo = element.Text; }, Throws.InstanceOf()); + } - driver.Url = simpleTestPage; - Assert.That(driver.Title, Is.EqualTo("Hello WebDriver")); - } + ///////////////////////////////////////////////// + // Tests unique to the .NET bindings + ///////////////////////////////////////////////// + [Test] + public void ShouldReturnTitleOfPageIfSet() + { + driver.Url = xhtmlTestPage; + Assert.That(driver.Title, Is.EqualTo("XHTML Test Page")); - [Test] - public void ShouldBeAbleToClickOnLinkIdentifiedByText() - { - driver.Url = xhtmlTestPage; - driver.FindElement(By.LinkText("click me")).Click(); - WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + driver.Url = simpleTestPage; + Assert.That(driver.Title, Is.EqualTo("Hello WebDriver")); + } - [Test] - public void ShouldBeAbleToClickOnLinkIdentifiedById() - { - driver.Url = xhtmlTestPage; - driver.FindElement(By.Id("linkId")).Click(); - WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldBeAbleToClickOnLinkIdentifiedByText() + { + driver.Url = xhtmlTestPage; + driver.FindElement(By.LinkText("click me")).Click(); + WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldFindAnElementBasedOnId() - { - driver.Url = formsPage; + [Test] + public void ShouldBeAbleToClickOnLinkIdentifiedById() + { + driver.Url = xhtmlTestPage; + driver.FindElement(By.Id("linkId")).Click(); + WaitFor(() => { return driver.Title == "We Arrive Here"; }, "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - IWebElement element = driver.FindElement(By.Id("checky")); + [Test] + public void ShouldFindAnElementBasedOnId() + { + driver.Url = formsPage; - Assert.That(element.Selected, Is.False); - } + IWebElement element = driver.FindElement(By.Id("checky")); - [Test] - public void ShouldBeAbleToFindChildrenOfANode() - { - driver.Url = selectableItemsPage; - ReadOnlyCollection elements = driver.FindElements(By.XPath("/html/head")); - IWebElement head = elements[0]; - ReadOnlyCollection importedScripts = head.FindElements(By.TagName("script")); - Assert.That(importedScripts, Has.Exactly(3).Items); - } + Assert.That(element.Selected, Is.False); + } - [Test] - public void ReturnAnEmptyListWhenThereAreNoChildrenOfANode() - { - driver.Url = xhtmlTestPage; - IWebElement table = driver.FindElement(By.Id("table")); - ReadOnlyCollection rows = table.FindElements(By.TagName("tr")); + [Test] + public void ShouldBeAbleToFindChildrenOfANode() + { + driver.Url = selectableItemsPage; + ReadOnlyCollection elements = driver.FindElements(By.XPath("/html/head")); + IWebElement head = elements[0]; + ReadOnlyCollection importedScripts = head.FindElements(By.TagName("script")); + Assert.That(importedScripts, Has.Exactly(3).Items); + } - Assert.That(rows, Is.Empty); - } + [Test] + public void ReturnAnEmptyListWhenThereAreNoChildrenOfANode() + { + driver.Url = xhtmlTestPage; + IWebElement table = driver.FindElement(By.Id("table")); + ReadOnlyCollection rows = table.FindElements(By.TagName("tr")); - [Test] - public void ShouldFindElementsByName() - { - driver.Url = formsPage; + Assert.That(rows, Is.Empty); + } - IWebElement element = driver.FindElement(By.Name("checky")); + [Test] + public void ShouldFindElementsByName() + { + driver.Url = formsPage; - Assert.That(element.GetAttribute("value"), Is.EqualTo("furrfu")); - } + IWebElement element = driver.FindElement(By.Name("checky")); - [Test] - public void ShouldFindElementsByClassWhenItIsTheFirstNameAmongMany() - { - driver.Url = xhtmlTestPage; + Assert.That(element.GetAttribute("value"), Is.EqualTo("furrfu")); + } - IWebElement element = driver.FindElement(By.ClassName("nameA")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementsByClassWhenItIsTheFirstNameAmongMany() + { + driver.Url = xhtmlTestPage; - [Test] - public void ShouldFindElementsByClassWhenItIsTheLastNameAmongMany() - { - driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("nameA")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - IWebElement element = driver.FindElement(By.ClassName("nameC")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementsByClassWhenItIsTheLastNameAmongMany() + { + driver.Url = xhtmlTestPage; - [Test] - public void ShouldFindElementsByClassWhenItIsInTheMiddleAmongMany() - { - driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.ClassName("nameC")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - IWebElement element = driver.FindElement(By.ClassName("nameBnoise")); - Assert.That(element.Text, Is.EqualTo("An H2 title")); - } + [Test] + public void ShouldFindElementsByClassWhenItIsInTheMiddleAmongMany() + { + driver.Url = xhtmlTestPage; - [Test] - public void ShouldFindGrandChildren() - { - driver.Url = formsPage; - IWebElement form = driver.FindElement(By.Id("nested_form")); - form.FindElement(By.Name("x")); - } + IWebElement element = driver.FindElement(By.ClassName("nameBnoise")); + Assert.That(element.Text, Is.EqualTo("An H2 title")); + } - [Test] - public void ShouldNotFindElementOutSideTree() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Name("login")); - Assert.That(() => element.FindElement(By.Name("x")), Throws.InstanceOf()); - } + [Test] + public void ShouldFindGrandChildren() + { + driver.Url = formsPage; + IWebElement form = driver.FindElement(By.Id("nested_form")); + form.FindElement(By.Name("x")); + } - [Test] - public void ShouldReturnElementsThatDoNotSupportTheNameProperty() - { - driver.Url = nestedPage; + [Test] + public void ShouldNotFindElementOutSideTree() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Name("login")); + Assert.That(() => element.FindElement(By.Name("x")), Throws.InstanceOf()); + } - driver.FindElement(By.Name("div1")); - // If this works, we're all good - } + [Test] + public void ShouldReturnElementsThatDoNotSupportTheNameProperty() + { + driver.Url = nestedPage; - [Test] - public void ShouldBeAbleToClickOnLinksWithNoHrefAttribute() - { - driver.Url = javascriptPage; + driver.FindElement(By.Name("div1")); + // If this works, we're all good + } - IWebElement element = driver.FindElement(By.LinkText("No href")); - element.Click(); + [Test] + public void ShouldBeAbleToClickOnLinksWithNoHrefAttribute() + { + driver.Url = javascriptPage; - // if any exception is thrown, we won't get this far. Sanity check - Assert.That(driver.Title, Is.EqualTo("Changed")); - } + IWebElement element = driver.FindElement(By.LinkText("No href")); + element.Click(); - [Test] - public void FindingByTagNameShouldNotIncludeParentElementIfSameTagType() - { - driver.Url = xhtmlTestPage; - IWebElement parent = driver.FindElement(By.Id("my_span")); + // if any exception is thrown, we won't get this far. Sanity check + Assert.That(driver.Title, Is.EqualTo("Changed")); + } - Assert.That(parent.FindElements(By.TagName("div")), Has.Count.EqualTo(2)); - Assert.That(parent.FindElements(By.TagName("span")), Has.Count.EqualTo(2)); - } + [Test] + public void FindingByTagNameShouldNotIncludeParentElementIfSameTagType() + { + driver.Url = xhtmlTestPage; + IWebElement parent = driver.FindElement(By.Id("my_span")); - [Test] - public void FindingByCssShouldNotIncludeParentElementIfSameTagType() - { - driver.Url = xhtmlTestPage; - IWebElement parent = driver.FindElement(By.CssSelector("div#parent")); - IWebElement child = parent.FindElement(By.CssSelector("div")); + Assert.That(parent.FindElements(By.TagName("div")), Has.Count.EqualTo(2)); + Assert.That(parent.FindElements(By.TagName("span")), Has.Count.EqualTo(2)); + } - Assert.That(child.GetAttribute("id"), Is.EqualTo("child")); - } + [Test] + public void FindingByCssShouldNotIncludeParentElementIfSameTagType() + { + driver.Url = xhtmlTestPage; + IWebElement parent = driver.FindElement(By.CssSelector("div#parent")); + IWebElement child = parent.FindElement(By.CssSelector("div")); - [Test] - public void FindingByXPathShouldNotIncludeParentElementIfSameTagType() - { - driver.Url = xhtmlTestPage; - IWebElement parent = driver.FindElement(By.Id("my_span")); + Assert.That(child.GetAttribute("id"), Is.EqualTo("child")); + } - Assert.That(parent.FindElements(By.TagName("div")), Has.Count.EqualTo(2)); - Assert.That(parent.FindElements(By.TagName("span")), Has.Count.EqualTo(2)); - } + [Test] + public void FindingByXPathShouldNotIncludeParentElementIfSameTagType() + { + driver.Url = xhtmlTestPage; + IWebElement parent = driver.FindElement(By.Id("my_span")); - [Test] - public void ShouldBeAbleToInjectXPathEngineIfNeeded() - { - driver.Url = alertsPage; - driver.FindElement(By.XPath("//body")); - driver.FindElement(By.XPath("//h1")); - driver.FindElement(By.XPath("//div")); - driver.FindElement(By.XPath("//p")); - driver.FindElement(By.XPath("//a")); - } + Assert.That(parent.FindElements(By.TagName("div")), Has.Count.EqualTo(2)); + Assert.That(parent.FindElements(By.TagName("span")), Has.Count.EqualTo(2)); + } - [Test] - public void ShouldFindElementByLinkTextContainingDoubleQuote() - { - driver.Url = simpleTestPage; - IWebElement element = driver.FindElement(By.LinkText("link with \" (double quote)")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("quote")); - } + [Test] + public void ShouldBeAbleToInjectXPathEngineIfNeeded() + { + driver.Url = alertsPage; + driver.FindElement(By.XPath("//body")); + driver.FindElement(By.XPath("//h1")); + driver.FindElement(By.XPath("//div")); + driver.FindElement(By.XPath("//p")); + driver.FindElement(By.XPath("//a")); + } - [Test] - public void ShouldFindElementByLinkTextContainingBackslash() - { - driver.Url = simpleTestPage; - IWebElement element = driver.FindElement(By.LinkText("link with \\ (backslash)")); - Assert.That(element.GetAttribute("id"), Is.EqualTo("backslash")); - } + [Test] + public void ShouldFindElementByLinkTextContainingDoubleQuote() + { + driver.Url = simpleTestPage; + IWebElement element = driver.FindElement(By.LinkText("link with \" (double quote)")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("quote")); + } + + [Test] + public void ShouldFindElementByLinkTextContainingBackslash() + { + driver.Url = simpleTestPage; + IWebElement element = driver.FindElement(By.LinkText("link with \\ (backslash)")); + Assert.That(element.GetAttribute("id"), Is.EqualTo("backslash")); } } diff --git a/dotnet/test/common/ElementPropertyTest.cs b/dotnet/test/common/ElementPropertyTest.cs index 2f3b98c80bfe1..6884497ca6367 100644 --- a/dotnet/test/common/ElementPropertyTest.cs +++ b/dotnet/test/common/ElementPropertyTest.cs @@ -19,31 +19,30 @@ using NUnit.Framework; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ElementPropertyTest : DriverTestFixture { - [TestFixture] - public class ElementPropertyTest : DriverTestFixture + [Test] + [IgnoreBrowser(Browser.Remote)] + public void ShouldReturnNullWhenGettingTheValueOfAPropertyThatIsNotListed() { - [Test] - [IgnoreBrowser(Browser.Remote)] - public void ShouldReturnNullWhenGettingTheValueOfAPropertyThatIsNotListed() - { - driver.Url = simpleTestPage; - IWebElement head = driver.FindElement(By.XPath("/html")); - string attribute = head.GetDomProperty("cheese"); - Assert.That(attribute, Is.Null); - } + driver.Url = simpleTestPage; + IWebElement head = driver.FindElement(By.XPath("/html")); + string attribute = head.GetDomProperty("cheese"); + Assert.That(attribute, Is.Null); + } - [Test] - [IgnoreBrowser(Browser.Remote)] - public void CanRetrieveTheCurrentValueOfAProperty() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("working")); + [Test] + [IgnoreBrowser(Browser.Remote)] + public void CanRetrieveTheCurrentValueOfAProperty() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("working")); - Assert.That(element.GetDomProperty("value"), Is.Empty); - element.SendKeys("hello world"); - Assert.That(element.GetDomProperty("value"), Is.EqualTo("hello world")); - } + Assert.That(element.GetDomProperty("value"), Is.Empty); + element.SendKeys("hello world"); + Assert.That(element.GetDomProperty("value"), Is.EqualTo("hello world")); } } diff --git a/dotnet/test/common/ElementSelectingTest.cs b/dotnet/test/common/ElementSelectingTest.cs index 6d9bb1a7125ac..5a663902deee9 100644 --- a/dotnet/test/common/ElementSelectingTest.cs +++ b/dotnet/test/common/ElementSelectingTest.cs @@ -19,343 +19,342 @@ using NUnit.Framework; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ElementSelectingTest : DriverTestFixture { - [TestFixture] - public class ElementSelectingTest : DriverTestFixture + private const string assertCannotPerformActionFormat_action_element = "Expected exception - should not be able to {0} element {1}"; + + [Test] + public void ShouldBeAbleToSelectAnEnabledUnselectedCheckbox() { - private const string assertCannotPerformActionFormat_action_element = "Expected exception - should not be able to {0} element {1}"; + driver.Url = formsPage; + AssertCanSelect(this.EnabledUnselectedCheckbox); + } - [Test] - public void ShouldBeAbleToSelectAnEnabledUnselectedCheckbox() - { - driver.Url = formsPage; - AssertCanSelect(this.EnabledUnselectedCheckbox); - } + [Test] + public void ShouldBeAbleToSelectAnEnabledUnselectedRadioButton() + { + driver.Url = formsPage; + AssertCanSelect(this.EnabledUnselectedRadioButton); + } - [Test] - public void ShouldBeAbleToSelectAnEnabledUnselectedRadioButton() - { - driver.Url = formsPage; - AssertCanSelect(this.EnabledUnselectedRadioButton); - } + [Test] + public void ShouldNotBeAbleToSelectADisabledCheckbox() + { + driver.Url = formsPage; + AssertCannotSelect(this.DisabledUnselectedCheckbox); + } - [Test] - public void ShouldNotBeAbleToSelectADisabledCheckbox() - { - driver.Url = formsPage; - AssertCannotSelect(this.DisabledUnselectedCheckbox); - } + [Test] + public void ShouldNotBeAbleToSelectADisabledCheckboxDisabledWithRandomString() + { + driver.Url = formsPage; + AssertCannotSelect(this.RandomlyDisabledSelectedCheckbox); + } - [Test] - public void ShouldNotBeAbleToSelectADisabledCheckboxDisabledWithRandomString() - { - driver.Url = formsPage; - AssertCannotSelect(this.RandomlyDisabledSelectedCheckbox); - } + [Test] + public void ShouldNotBeAbleToSelectADisabledRadioButton() + { + driver.Url = formsPage; + AssertCannotSelect(this.DisabledUnselectedRadioButton); + } - [Test] - public void ShouldNotBeAbleToSelectADisabledRadioButton() - { - driver.Url = formsPage; - AssertCannotSelect(this.DisabledUnselectedRadioButton); - } + [Test] + public void ShouldNotBeAbleToSelectADisabledRadioButtonDisabledWithRandomString() + { + driver.Url = formsPage; + AssertCannotSelect(this.RandomlyDisabledUnselectedRadioButton); + } - [Test] - public void ShouldNotBeAbleToSelectADisabledRadioButtonDisabledWithRandomString() - { - driver.Url = formsPage; - AssertCannotSelect(this.RandomlyDisabledUnselectedRadioButton); - } + [Test] + public void SelectingRadioButtonShouldUnselectItsSibling() + { + driver.Url = formsPage; - [Test] - public void SelectingRadioButtonShouldUnselectItsSibling() - { - driver.Url = formsPage; + IWebElement originallySelected = this.EnabledSelectedRadioButton; + AssertSelected(originallySelected); - IWebElement originallySelected = this.EnabledSelectedRadioButton; - AssertSelected(originallySelected); + IWebElement toSelect = this.EnabledUnselectedRadioButton; + AssertNotSelected(toSelect); - IWebElement toSelect = this.EnabledUnselectedRadioButton; - AssertNotSelected(toSelect); + toSelect.Click(); + AssertNotSelected(originallySelected); + AssertSelected(toSelect); + } - toSelect.Click(); - AssertNotSelected(originallySelected); - AssertSelected(toSelect); - } + [Test] + public void ShouldBeAbleToToggleAnEnabledUnselectedCheckbox() + { + driver.Url = formsPage; - [Test] - public void ShouldBeAbleToToggleAnEnabledUnselectedCheckbox() - { - driver.Url = formsPage; + IWebElement checkbox = this.EnabledUnselectedCheckbox; + AssertNotSelected(checkbox); - IWebElement checkbox = this.EnabledUnselectedCheckbox; - AssertNotSelected(checkbox); + checkbox.Click(); + AssertSelected(checkbox); - checkbox.Click(); - AssertSelected(checkbox); + checkbox.Click(); + AssertNotSelected(checkbox); + } - checkbox.Click(); - AssertNotSelected(checkbox); - } + [Test] + public void ShouldBeAbleToToggleAnEnabledSelectedCheckbox() + { + driver.Url = formsPage; - [Test] - public void ShouldBeAbleToToggleAnEnabledSelectedCheckbox() - { - driver.Url = formsPage; + IWebElement checkbox = this.EnabledSelectedCheckbox; + AssertSelected(checkbox); - IWebElement checkbox = this.EnabledSelectedCheckbox; - AssertSelected(checkbox); + checkbox.Click(); + AssertNotSelected(checkbox); - checkbox.Click(); - AssertNotSelected(checkbox); + checkbox.Click(); + AssertSelected(checkbox); + } - checkbox.Click(); - AssertSelected(checkbox); - } + [Test] + public void ClickingOnASelectedRadioButtonShouldLeaveItSelected() + { + driver.Url = formsPage; - [Test] - public void ClickingOnASelectedRadioButtonShouldLeaveItSelected() - { - driver.Url = formsPage; + IWebElement button = this.EnabledSelectedRadioButton; + Assert.That(button.Selected, "Radio button should be selected"); - IWebElement button = this.EnabledSelectedRadioButton; - Assert.That(button.Selected, "Radio button should be selected"); + button.Click(); - button.Click(); + Assert.That(button.Selected, "Radio button should be selected"); + } - Assert.That(button.Selected, "Radio button should be selected"); - } + [Test] + public void ShouldBeAbleToToggleEnabledMultiSelectOption() + { + driver.Url = formsPage; + AssertCanToggle(this.SelectedMultipleSelectOption); + } - [Test] - public void ShouldBeAbleToToggleEnabledMultiSelectOption() - { - driver.Url = formsPage; - AssertCanToggle(this.SelectedMultipleSelectOption); - } + [Test] + public void ShouldBeAbleToToggleSelectableCheckboxByClickingOnIt() + { + driver.Url = formsPage; - [Test] - public void ShouldBeAbleToToggleSelectableCheckboxByClickingOnIt() - { - driver.Url = formsPage; + IWebElement checkbox = this.EnabledUnselectedCheckbox; + AssertNotSelected(checkbox); - IWebElement checkbox = this.EnabledUnselectedCheckbox; - AssertNotSelected(checkbox); + checkbox.Click(); + AssertSelected(checkbox); - checkbox.Click(); - AssertSelected(checkbox); + checkbox.Click(); + AssertNotSelected(checkbox); + } - checkbox.Click(); - AssertNotSelected(checkbox); - } + [Test] + public void ShouldBeAbleToSelectSelectableRadioButtonByClickingOnIt() + { + driver.Url = formsPage; - [Test] - public void ShouldBeAbleToSelectSelectableRadioButtonByClickingOnIt() - { - driver.Url = formsPage; + IWebElement radioButton = this.EnabledUnselectedRadioButton; + AssertNotSelected(radioButton); - IWebElement radioButton = this.EnabledUnselectedRadioButton; - AssertNotSelected(radioButton); + radioButton.Click(); + AssertSelected(radioButton); - radioButton.Click(); - AssertSelected(radioButton); + radioButton.Click(); + AssertSelected(radioButton); + } - radioButton.Click(); - AssertSelected(radioButton); - } + [Test] + public void ClickingDisabledSelectedCheckboxShouldBeNoop() + { + driver.Url = formsPage; + AssertClickingPreservesCurrentlySelectedStatus(this.RandomlyDisabledSelectedCheckbox); + } - [Test] - public void ClickingDisabledSelectedCheckboxShouldBeNoop() - { - driver.Url = formsPage; - AssertClickingPreservesCurrentlySelectedStatus(this.RandomlyDisabledSelectedCheckbox); - } + [Test] + public void ClickingDisabledUnselectedCheckboxShouldBeNoop() + { + driver.Url = formsPage; + AssertClickingPreservesCurrentlySelectedStatus(this.DisabledUnselectedCheckbox); + } - [Test] - public void ClickingDisabledUnselectedCheckboxShouldBeNoop() - { - driver.Url = formsPage; - AssertClickingPreservesCurrentlySelectedStatus(this.DisabledUnselectedCheckbox); - } + [Test] + public void ClickingDisabledSelectedRadioButtonShouldBeNoop() + { + driver.Url = formsPage; + AssertClickingPreservesCurrentlySelectedStatus(this.DisabledSelectedRadioButton); + } - [Test] - public void ClickingDisabledSelectedRadioButtonShouldBeNoop() - { - driver.Url = formsPage; - AssertClickingPreservesCurrentlySelectedStatus(this.DisabledSelectedRadioButton); - } + [Test] + public void ClickingDisabledUnselectedRadioButtonShouldBeNoop() + { + driver.Url = formsPage; + AssertClickingPreservesCurrentlySelectedStatus(this.DisabledUnselectedRadioButton); + } - [Test] - public void ClickingDisabledUnselectedRadioButtonShouldBeNoop() - { - driver.Url = formsPage; - AssertClickingPreservesCurrentlySelectedStatus(this.DisabledUnselectedRadioButton); - } + private static void AssertNotSelected(IWebElement element) + { + AssertSelected(element, false); + } - private static void AssertNotSelected(IWebElement element) - { - AssertSelected(element, false); - } + private static void AssertSelected(IWebElement element) + { + AssertSelected(element, true); + } - private static void AssertSelected(IWebElement element) - { - AssertSelected(element, true); - } + private static void AssertSelected(IWebElement element, bool isSelected) + { + Assert.That(element.Selected, Is.EqualTo(isSelected), string.Format("Expected element {0} to be {1} but was {2}", Describe(element), SelectedToString(isSelected), SelectedToString(!isSelected))); + } - private static void AssertSelected(IWebElement element, bool isSelected) - { - Assert.That(element.Selected, Is.EqualTo(isSelected), string.Format("Expected element {0} to be {1} but was {2}", Describe(element), SelectedToString(isSelected), SelectedToString(!isSelected))); - } + private static void AssertCannotSelect(IWebElement element) + { + bool previous = element.Selected; + element.Click(); + Assert.That(element.Selected, Is.EqualTo(previous)); + } - private static void AssertCannotSelect(IWebElement element) - { - bool previous = element.Selected; - element.Click(); - Assert.That(element.Selected, Is.EqualTo(previous)); - } + private static void AssertCanSelect(IWebElement element) + { + AssertNotSelected(element); - private static void AssertCanSelect(IWebElement element) - { - AssertNotSelected(element); + element.Click(); + AssertSelected(element); + } + private static void AssertClickingPreservesCurrentlySelectedStatus(IWebElement element) + { + bool currentSelectedStatus = element.Selected; + try + { element.Click(); - AssertSelected(element); } - - private static void AssertClickingPreservesCurrentlySelectedStatus(IWebElement element) + catch (InvalidElementStateException) { - bool currentSelectedStatus = element.Selected; - try - { - element.Click(); - } - catch (InvalidElementStateException) - { - // This is expected, as we are clicking disabled elements. - } - - AssertSelected(element, currentSelectedStatus); + // This is expected, as we are clicking disabled elements. } - private static string SelectedToString(bool isSelected) - { - return isSelected ? "[selected]" : "[not selected]"; - } + AssertSelected(element, currentSelectedStatus); + } - private static string Describe(IWebElement element) - { - return element.GetAttribute("id"); - } + private static string SelectedToString(bool isSelected) + { + return isSelected ? "[selected]" : "[not selected]"; + } - private static void AssertCanToggle(IWebElement element) - { - bool originalState = element.Selected; + private static string Describe(IWebElement element) + { + return element.GetAttribute("id"); + } - AssertSelected(element, originalState); + private static void AssertCanToggle(IWebElement element) + { + bool originalState = element.Selected; - AssertTogglingSwapsSelectedStateFrom(element, originalState); - AssertTogglingSwapsSelectedStateFrom(element, !originalState); - } + AssertSelected(element, originalState); - private static void AssertTogglingSwapsSelectedStateFrom(IWebElement element, bool originalState) - { - element.Click(); - bool isNowSelected = element.Selected; - Assert.That(originalState, Is.Not.EqualTo(isNowSelected), string.Format("Expected element {0} to have been toggled to {1} but was {2}", Describe(element), SelectedToString(!originalState), SelectedToString(originalState))); - AssertSelected(element, !originalState); - } + AssertTogglingSwapsSelectedStateFrom(element, originalState); + AssertTogglingSwapsSelectedStateFrom(element, !originalState); + } - //TODO: Test disabled multi-selects - //TODO: Test selecting options + private static void AssertTogglingSwapsSelectedStateFrom(IWebElement element, bool originalState) + { + element.Click(); + bool isNowSelected = element.Selected; + Assert.That(originalState, Is.Not.EqualTo(isNowSelected), string.Format("Expected element {0} to have been toggled to {1} but was {2}", Describe(element), SelectedToString(!originalState), SelectedToString(originalState))); + AssertSelected(element, !originalState); + } + //TODO: Test disabled multi-selects + //TODO: Test selecting options - private IWebElement EnabledUnselectedCheckbox + + private IWebElement EnabledUnselectedCheckbox + { + get { - get - { - return driver.FindElement(By.Id("checky")); - } + return driver.FindElement(By.Id("checky")); } + } - private IWebElement EnabledSelectedCheckbox + private IWebElement EnabledSelectedCheckbox + { + get { - get - { - return driver.FindElement(By.Id("checkedchecky")); - } + return driver.FindElement(By.Id("checkedchecky")); } + } - private IWebElement DisabledUnselectedCheckbox + private IWebElement DisabledUnselectedCheckbox + { + get { - get - { - return driver.FindElement(By.Id("disabledchecky")); - } + return driver.FindElement(By.Id("disabledchecky")); } + } - private IWebElement RandomlyDisabledSelectedCheckbox + private IWebElement RandomlyDisabledSelectedCheckbox + { + get { - get - { - return driver.FindElement(By.Id("randomly_disabled_checky")); - } + return driver.FindElement(By.Id("randomly_disabled_checky")); } + } - private IWebElement EnabledUnselectedRadioButton + private IWebElement EnabledUnselectedRadioButton + { + get { - get - { - return driver.FindElement(By.Id("peas")); - } + return driver.FindElement(By.Id("peas")); } + } - private IWebElement EnabledSelectedRadioButton + private IWebElement EnabledSelectedRadioButton + { + get { - get - { - return driver.FindElement(By.Id("cheese_and_peas")); - } + return driver.FindElement(By.Id("cheese_and_peas")); } + } - private IWebElement DisabledSelectedRadioButton + private IWebElement DisabledSelectedRadioButton + { + get { - get - { - return driver.FindElement(By.Id("lone_disabled_selected_radio")); - } + return driver.FindElement(By.Id("lone_disabled_selected_radio")); } + } - private IWebElement DisabledUnselectedRadioButton + private IWebElement DisabledUnselectedRadioButton + { + get { - get - { - return driver.FindElement(By.Id("nothing")); - } + return driver.FindElement(By.Id("nothing")); } + } - private IWebElement RandomlyDisabledUnselectedRadioButton + private IWebElement RandomlyDisabledUnselectedRadioButton + { + get { - get - { - return driver.FindElement(By.Id("randomly_disabled_nothing")); - } + return driver.FindElement(By.Id("randomly_disabled_nothing")); } + } - private IWebElement SelectedMultipleSelectOption + private IWebElement SelectedMultipleSelectOption + { + get { - get - { - IWebElement select = driver.FindElement(By.Name("multi")); - return select.FindElements(By.TagName("option"))[0]; - } + IWebElement select = driver.FindElement(By.Name("multi")); + return select.FindElements(By.TagName("option"))[0]; } + } - private IWebElement NonSelectableElement + private IWebElement NonSelectableElement + { + get { - get - { - return driver.FindElement(By.TagName("div")); - } + return driver.FindElement(By.TagName("div")); } } } diff --git a/dotnet/test/common/Environment/DriverConfig.cs b/dotnet/test/common/Environment/DriverConfig.cs index 16f0f4475fd77..31905d2a9b97c 100644 --- a/dotnet/test/common/Environment/DriverConfig.cs +++ b/dotnet/test/common/Environment/DriverConfig.cs @@ -20,25 +20,24 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +[JsonObject] +public class DriverConfig { - [JsonObject] - public class DriverConfig - { - [JsonProperty] - public string DriverTypeName { get; set; } + [JsonProperty] + public string DriverTypeName { get; set; } - [JsonProperty] - [JsonConverter(typeof(StringEnumConverter))] - public Browser BrowserValue { get; set; } + [JsonProperty] + [JsonConverter(typeof(StringEnumConverter))] + public Browser BrowserValue { get; set; } - [JsonProperty] - public string RemoteCapabilities { get; set; } + [JsonProperty] + public string RemoteCapabilities { get; set; } - [JsonProperty] - public bool AutoStartRemoteServer { get; set; } + [JsonProperty] + public bool AutoStartRemoteServer { get; set; } - [JsonProperty] - public bool Logging { get; set; } - } + [JsonProperty] + public bool Logging { get; set; } } diff --git a/dotnet/test/common/Environment/DriverFactory.cs b/dotnet/test/common/Environment/DriverFactory.cs index 2175fc2c4e010..f58eea2430673 100644 --- a/dotnet/test/common/Environment/DriverFactory.cs +++ b/dotnet/test/common/Environment/DriverFactory.cs @@ -28,219 +28,218 @@ using System.IO; using System.Reflection; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class DriverFactory { - public class DriverFactory + string driverPath; + string browserBinaryLocation; + private Dictionary serviceTypes = new Dictionary(); + private Dictionary optionsTypes = new Dictionary(); + + public DriverFactory(string driverPath, string browserBinaryLocation) { - string driverPath; - string browserBinaryLocation; - private Dictionary serviceTypes = new Dictionary(); - private Dictionary optionsTypes = new Dictionary(); + this.driverPath = driverPath; + this.browserBinaryLocation = browserBinaryLocation; - public DriverFactory(string driverPath, string browserBinaryLocation) - { - this.driverPath = driverPath; - this.browserBinaryLocation = browserBinaryLocation; + this.PopulateServiceTypes(); + this.PopulateOptionsTypes(); + } - this.PopulateServiceTypes(); - this.PopulateOptionsTypes(); - } + private void PopulateOptionsTypes() + { + this.optionsTypes[Browser.Chrome] = typeof(ChromeOptions); + this.optionsTypes[Browser.Edge] = typeof(EdgeOptions); + this.optionsTypes[Browser.Firefox] = typeof(FirefoxOptions); + this.optionsTypes[Browser.IE] = typeof(InternetExplorerOptions); + this.optionsTypes[Browser.Safari] = typeof(SafariOptions); + } - private void PopulateOptionsTypes() - { - this.optionsTypes[Browser.Chrome] = typeof(ChromeOptions); - this.optionsTypes[Browser.Edge] = typeof(EdgeOptions); - this.optionsTypes[Browser.Firefox] = typeof(FirefoxOptions); - this.optionsTypes[Browser.IE] = typeof(InternetExplorerOptions); - this.optionsTypes[Browser.Safari] = typeof(SafariOptions); - } + private void PopulateServiceTypes() + { + this.serviceTypes[Browser.Chrome] = typeof(ChromeDriverService); + this.serviceTypes[Browser.Edge] = typeof(EdgeDriverService); + this.serviceTypes[Browser.Firefox] = typeof(FirefoxDriverService); + this.serviceTypes[Browser.IE] = typeof(InternetExplorerDriverService); + this.serviceTypes[Browser.Safari] = typeof(SafariDriverService); + } - private void PopulateServiceTypes() - { - this.serviceTypes[Browser.Chrome] = typeof(ChromeDriverService); - this.serviceTypes[Browser.Edge] = typeof(EdgeDriverService); - this.serviceTypes[Browser.Firefox] = typeof(FirefoxDriverService); - this.serviceTypes[Browser.IE] = typeof(InternetExplorerDriverService); - this.serviceTypes[Browser.Safari] = typeof(SafariDriverService); - } + public event EventHandler DriverStarting; - public event EventHandler DriverStarting; + public IWebDriver CreateDriver(Type driverType, bool logging = false) + { + return CreateDriverWithOptions(driverType, null, logging); + } - public IWebDriver CreateDriver(Type driverType, bool logging = false) + public IWebDriver CreateDriverWithOptions(Type driverType, DriverOptions driverOptions, bool logging = false) + { + Browser browser = Browser.All; + DriverService service = null; + DriverOptions options = null; + bool enableLogging = logging; + + List constructorArgTypeList = new List(); + IWebDriver driver = null; + if (typeof(ChromeDriver).IsAssignableFrom(driverType)) { - return CreateDriverWithOptions(driverType, null, logging); - } + browser = Browser.Chrome; + options = GetDriverOptions(driverType, driverOptions); - public IWebDriver CreateDriverWithOptions(Type driverType, DriverOptions driverOptions, bool logging = false) - { - Browser browser = Browser.All; - DriverService service = null; - DriverOptions options = null; - bool enableLogging = logging; - - List constructorArgTypeList = new List(); - IWebDriver driver = null; - if (typeof(ChromeDriver).IsAssignableFrom(driverType)) + var chromeOptions = (ChromeOptions)options; + chromeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage"); + + service = CreateService(); + if (!string.IsNullOrEmpty(this.browserBinaryLocation)) { - browser = Browser.Chrome; - options = GetDriverOptions(driverType, driverOptions); - - var chromeOptions = (ChromeOptions)options; - chromeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage"); - - service = CreateService(); - if (!string.IsNullOrEmpty(this.browserBinaryLocation)) - { - ((ChromeOptions)options).BinaryLocation = this.browserBinaryLocation; - } - if (enableLogging) - { - ((ChromiumDriverService)service).EnableVerboseLogging = true; - } + ((ChromeOptions)options).BinaryLocation = this.browserBinaryLocation; } - else if (typeof(EdgeDriver).IsAssignableFrom(driverType)) + if (enableLogging) { - browser = Browser.Edge; - options = GetDriverOptions(driverType, driverOptions); - - var edgeOptions = (EdgeOptions)options; - edgeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage"); - - service = CreateService(); - if (!string.IsNullOrEmpty(this.browserBinaryLocation)) - { - ((EdgeOptions)options).BinaryLocation = this.browserBinaryLocation; - } - if (enableLogging) - { - ((ChromiumDriverService)service).EnableVerboseLogging = true; - } + ((ChromiumDriverService)service).EnableVerboseLogging = true; } - else if (typeof(InternetExplorerDriver).IsAssignableFrom(driverType)) + } + else if (typeof(EdgeDriver).IsAssignableFrom(driverType)) + { + browser = Browser.Edge; + options = GetDriverOptions(driverType, driverOptions); + + var edgeOptions = (EdgeOptions)options; + edgeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage"); + + service = CreateService(); + if (!string.IsNullOrEmpty(this.browserBinaryLocation)) { - browser = Browser.IE; - options = GetDriverOptions(driverType, driverOptions); - service = CreateService(); - if (enableLogging) - { - ((InternetExplorerDriverService)service).LoggingLevel = InternetExplorerDriverLogLevel.Trace; - } + ((EdgeOptions)options).BinaryLocation = this.browserBinaryLocation; } - else if (typeof(FirefoxDriver).IsAssignableFrom(driverType)) + if (enableLogging) { - browser = Browser.Firefox; - options = GetDriverOptions(driverType, driverOptions); - service = CreateService(); - if (!string.IsNullOrEmpty(this.browserBinaryLocation)) - { - ((FirefoxOptions)options).BinaryLocation = this.browserBinaryLocation; - } - if (enableLogging) - { - ((FirefoxDriverService)service).LogLevel = FirefoxDriverLogLevel.Trace; - } + ((ChromiumDriverService)service).EnableVerboseLogging = true; } - else if (typeof(SafariDriver).IsAssignableFrom(driverType)) + } + else if (typeof(InternetExplorerDriver).IsAssignableFrom(driverType)) + { + browser = Browser.IE; + options = GetDriverOptions(driverType, driverOptions); + service = CreateService(); + if (enableLogging) { - browser = Browser.Safari; - options = GetDriverOptions(driverType, driverOptions); - service = CreateService(); + ((InternetExplorerDriverService)service).LoggingLevel = InternetExplorerDriverLogLevel.Trace; } - - if (!String.IsNullOrEmpty(this.driverPath) && service != null) + } + else if (typeof(FirefoxDriver).IsAssignableFrom(driverType)) + { + browser = Browser.Firefox; + options = GetDriverOptions(driverType, driverOptions); + service = CreateService(); + if (!string.IsNullOrEmpty(this.browserBinaryLocation)) { - service.DriverServicePath = Path.GetDirectoryName(this.driverPath); - service.DriverServiceExecutableName = Path.GetFileName(this.driverPath); + ((FirefoxOptions)options).BinaryLocation = this.browserBinaryLocation; } - - this.OnDriverLaunching(service, options); - - if (browser != Browser.All) + if (enableLogging) { - constructorArgTypeList.Add(this.serviceTypes[browser]); - constructorArgTypeList.Add(this.optionsTypes[browser]); - ConstructorInfo ctorInfo = driverType.GetConstructor(constructorArgTypeList.ToArray()); - if (ctorInfo != null) - { - return (IWebDriver)ctorInfo.Invoke(new object[] { service, options }); - } + ((FirefoxDriverService)service).LogLevel = FirefoxDriverLogLevel.Trace; } + } + else if (typeof(SafariDriver).IsAssignableFrom(driverType)) + { + browser = Browser.Safari; + options = GetDriverOptions(driverType, driverOptions); + service = CreateService(); + } - driver = (IWebDriver)Activator.CreateInstance(driverType); - return driver; + if (!String.IsNullOrEmpty(this.driverPath) && service != null) + { + service.DriverServicePath = Path.GetDirectoryName(this.driverPath); + service.DriverServiceExecutableName = Path.GetFileName(this.driverPath); } - protected void OnDriverLaunching(DriverService service, DriverOptions options) + this.OnDriverLaunching(service, options); + + if (browser != Browser.All) { - if (this.DriverStarting != null) + constructorArgTypeList.Add(this.serviceTypes[browser]); + constructorArgTypeList.Add(this.optionsTypes[browser]); + ConstructorInfo ctorInfo = driverType.GetConstructor(constructorArgTypeList.ToArray()); + if (ctorInfo != null) { - DriverStartingEventArgs args = new DriverStartingEventArgs(service, options); - this.DriverStarting(this, args); + return (IWebDriver)ctorInfo.Invoke(new object[] { service, options }); } } - private T GetDriverOptions(Type driverType, DriverOptions overriddenOptions) where T : DriverOptions, new() + driver = (IWebDriver)Activator.CreateInstance(driverType); + return driver; + } + + protected void OnDriverLaunching(DriverService service, DriverOptions options) + { + if (this.DriverStarting != null) { - T options = new T(); - Type optionsType = typeof(T); + DriverStartingEventArgs args = new DriverStartingEventArgs(service, options); + this.DriverStarting(this, args); + } + } - PropertyInfo defaultOptionsProperty = driverType.GetProperty("DefaultOptions", BindingFlags.Public | BindingFlags.Static); - if (defaultOptionsProperty != null && defaultOptionsProperty.PropertyType == optionsType) - { - options = (T)defaultOptionsProperty.GetValue(null, null); - } + private T GetDriverOptions(Type driverType, DriverOptions overriddenOptions) where T : DriverOptions, new() + { + T options = new T(); + Type optionsType = typeof(T); - if (overriddenOptions != null) - { - options.PageLoadStrategy = overriddenOptions.PageLoadStrategy; - options.UnhandledPromptBehavior = overriddenOptions.UnhandledPromptBehavior; - options.Proxy = overriddenOptions.Proxy; + PropertyInfo defaultOptionsProperty = driverType.GetProperty("DefaultOptions", BindingFlags.Public | BindingFlags.Static); + if (defaultOptionsProperty != null && defaultOptionsProperty.PropertyType == optionsType) + { + options = (T)defaultOptionsProperty.GetValue(null, null); + } - options.ScriptTimeout = overriddenOptions.ScriptTimeout; - options.PageLoadTimeout = overriddenOptions.PageLoadTimeout; - options.ImplicitWaitTimeout = overriddenOptions.ImplicitWaitTimeout; + if (overriddenOptions != null) + { + options.PageLoadStrategy = overriddenOptions.PageLoadStrategy; + options.UnhandledPromptBehavior = overriddenOptions.UnhandledPromptBehavior; + options.Proxy = overriddenOptions.Proxy; - options.UseWebSocketUrl = overriddenOptions.UseWebSocketUrl; - } + options.ScriptTimeout = overriddenOptions.ScriptTimeout; + options.PageLoadTimeout = overriddenOptions.PageLoadTimeout; + options.ImplicitWaitTimeout = overriddenOptions.ImplicitWaitTimeout; - return options; + options.UseWebSocketUrl = overriddenOptions.UseWebSocketUrl; } + return options; + } - private T MergeOptions(object baseOptions, DriverOptions overriddenOptions) where T : DriverOptions, new() - { - // If the driver type has a static DefaultOptions property, - // get the value of that property, which should be a valid - // options of the generic type (T). Otherwise, create a new - // instance of the browser-specific options class. - T mergedOptions = new T(); - if (baseOptions != null && baseOptions is T) - { - mergedOptions = (T)baseOptions; - } - - if (overriddenOptions != null) - { - mergedOptions.PageLoadStrategy = overriddenOptions.PageLoadStrategy; - mergedOptions.UnhandledPromptBehavior = overriddenOptions.UnhandledPromptBehavior; - mergedOptions.Proxy = overriddenOptions.Proxy; - } - return mergedOptions; + private T MergeOptions(object baseOptions, DriverOptions overriddenOptions) where T : DriverOptions, new() + { + // If the driver type has a static DefaultOptions property, + // get the value of that property, which should be a valid + // options of the generic type (T). Otherwise, create a new + // instance of the browser-specific options class. + T mergedOptions = new T(); + if (baseOptions != null && baseOptions is T) + { + mergedOptions = (T)baseOptions; } - private T CreateService() where T : DriverService + if (overriddenOptions != null) { - T service = default(T); - Type serviceType = typeof(T); + mergedOptions.PageLoadStrategy = overriddenOptions.PageLoadStrategy; + mergedOptions.UnhandledPromptBehavior = overriddenOptions.UnhandledPromptBehavior; + mergedOptions.Proxy = overriddenOptions.Proxy; + } - MethodInfo createDefaultServiceMethod = serviceType.GetMethod("CreateDefaultService", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); - if (createDefaultServiceMethod != null && createDefaultServiceMethod.ReturnType == serviceType) - { - service = (T)createDefaultServiceMethod.Invoke(null, new object[] { }); - } + return mergedOptions; + } - return service; + private T CreateService() where T : DriverService + { + T service = default(T); + Type serviceType = typeof(T); + + MethodInfo createDefaultServiceMethod = serviceType.GetMethod("CreateDefaultService", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null); + if (createDefaultServiceMethod != null && createDefaultServiceMethod.ReturnType == serviceType) + { + service = (T)createDefaultServiceMethod.Invoke(null, new object[] { }); } + + return service; } } diff --git a/dotnet/test/common/Environment/DriverStartingEventArgs.cs b/dotnet/test/common/Environment/DriverStartingEventArgs.cs index 2b37ba3c2cbc2..82183b0ad7d7b 100644 --- a/dotnet/test/common/Environment/DriverStartingEventArgs.cs +++ b/dotnet/test/common/Environment/DriverStartingEventArgs.cs @@ -17,21 +17,20 @@ // under the License. // -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class DriverStartingEventArgs { - public class DriverStartingEventArgs - { - DriverService service; - DriverOptions options; + DriverService service; + DriverOptions options; - public DriverStartingEventArgs(DriverService service, DriverOptions options) - { - this.Service = service; - this.Options = options; - } + public DriverStartingEventArgs(DriverService service, DriverOptions options) + { + this.Service = service; + this.Options = options; + } - public DriverService Service { get => service; set => service = value; } + public DriverService Service { get => service; set => service = value; } - public DriverOptions Options { get => options; set => options = value; } - } + public DriverOptions Options { get => options; set => options = value; } } diff --git a/dotnet/test/common/Environment/EnvironmentManager.cs b/dotnet/test/common/Environment/EnvironmentManager.cs index 9c0b53b90c56c..380871e2f79b6 100644 --- a/dotnet/test/common/Environment/EnvironmentManager.cs +++ b/dotnet/test/common/Environment/EnvironmentManager.cs @@ -26,289 +26,288 @@ using System.Linq; using System.Runtime.InteropServices; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class EnvironmentManager { - public class EnvironmentManager + private static EnvironmentManager instance; + private Type driverType; + private Browser browser; + private IWebDriver driver; + private UrlBuilder urlBuilder; + private TestWebServer webServer; + private DriverFactory driverFactory; + private RemoteSeleniumServer remoteServer; + private string remoteCapabilities; + + private EnvironmentManager() { - private static EnvironmentManager instance; - private Type driverType; - private Browser browser; - private IWebDriver driver; - private UrlBuilder urlBuilder; - private TestWebServer webServer; - private DriverFactory driverFactory; - private RemoteSeleniumServer remoteServer; - private string remoteCapabilities; - - private EnvironmentManager() + string dataFilePath; + Runfiles runfiles = null; + try { - string dataFilePath; - Runfiles runfiles = null; - try - { - runfiles = Runfiles.Create(); - dataFilePath = runfiles.Rlocation("_main/dotnet/test/common/appconfig.json"); - } - catch (FileNotFoundException) - { - dataFilePath = "appconfig.json"; - } - string currentDirectory = this.CurrentDirectory; + runfiles = Runfiles.Create(); + dataFilePath = runfiles.Rlocation("_main/dotnet/test/common/appconfig.json"); + } + catch (FileNotFoundException) + { + dataFilePath = "appconfig.json"; + } + string currentDirectory = this.CurrentDirectory; - string content = File.ReadAllText(dataFilePath); - TestEnvironment env = JsonConvert.DeserializeObject(content); + string content = File.ReadAllText(dataFilePath); + TestEnvironment env = JsonConvert.DeserializeObject(content); - string activeDriverConfig = System.Environment.GetEnvironmentVariable("ACTIVE_DRIVER_CONFIG") ?? TestContext.Parameters.Get("ActiveDriverConfig", env.ActiveDriverConfig); - string driverServiceLocation = System.Environment.GetEnvironmentVariable("DRIVER_SERVICE_LOCATION") ?? TestContext.Parameters.Get("DriverServiceLocation", env.DriverServiceLocation); + string activeDriverConfig = System.Environment.GetEnvironmentVariable("ACTIVE_DRIVER_CONFIG") ?? TestContext.Parameters.Get("ActiveDriverConfig", env.ActiveDriverConfig); + string driverServiceLocation = System.Environment.GetEnvironmentVariable("DRIVER_SERVICE_LOCATION") ?? TestContext.Parameters.Get("DriverServiceLocation", env.DriverServiceLocation); - string browserLocation = System.Environment.GetEnvironmentVariable("BROWSER_LOCATION") ?? TestContext.Parameters.Get("BrowserLocation", string.Empty); + string browserLocation = System.Environment.GetEnvironmentVariable("BROWSER_LOCATION") ?? TestContext.Parameters.Get("BrowserLocation", string.Empty); - string activeWebsiteConfig = TestContext.Parameters.Get("ActiveWebsiteConfig", env.ActiveWebsiteConfig); - DriverConfig driverConfig = env.DriverConfigs[activeDriverConfig]; - WebsiteConfig websiteConfig = env.WebSiteConfigs[activeWebsiteConfig]; + string activeWebsiteConfig = TestContext.Parameters.Get("ActiveWebsiteConfig", env.ActiveWebsiteConfig); + DriverConfig driverConfig = env.DriverConfigs[activeDriverConfig]; + WebsiteConfig websiteConfig = env.WebSiteConfigs[activeWebsiteConfig]; - int port = PortUtilities.FindFreePort(); - websiteConfig.Port = port.ToString(); + int port = PortUtilities.FindFreePort(); + websiteConfig.Port = port.ToString(); - TestWebServerConfig webServerConfig = env.TestWebServerConfig; - webServerConfig.CaptureConsoleOutput = TestContext.Parameters.Get("CaptureWebServerOutput", env.TestWebServerConfig.CaptureConsoleOutput); - webServerConfig.HideCommandPromptWindow = TestContext.Parameters.Get("HideWebServerCommandPrompt", env.TestWebServerConfig.HideCommandPromptWindow); - webServerConfig.JavaHomeDirectory = TestContext.Parameters.Get("WebServerJavaHome", env.TestWebServerConfig.JavaHomeDirectory); - webServerConfig.Port = websiteConfig.Port; + TestWebServerConfig webServerConfig = env.TestWebServerConfig; + webServerConfig.CaptureConsoleOutput = TestContext.Parameters.Get("CaptureWebServerOutput", env.TestWebServerConfig.CaptureConsoleOutput); + webServerConfig.HideCommandPromptWindow = TestContext.Parameters.Get("HideWebServerCommandPrompt", env.TestWebServerConfig.HideCommandPromptWindow); + webServerConfig.JavaHomeDirectory = TestContext.Parameters.Get("WebServerJavaHome", env.TestWebServerConfig.JavaHomeDirectory); + webServerConfig.Port = websiteConfig.Port; - this.driverFactory = new DriverFactory(driverServiceLocation, browserLocation); - this.driverFactory.DriverStarting += OnDriverStarting; + this.driverFactory = new DriverFactory(driverServiceLocation, browserLocation); + this.driverFactory.DriverStarting += OnDriverStarting; - // Search for the driver type in the all assemblies, - // bazel uses unpredictable assembly names to execute tests - driverType = AppDomain.CurrentDomain.GetAssemblies() - .Reverse() - .Select(assembly => assembly.GetType(driverConfig.DriverTypeName)) - .FirstOrDefault(t => t != null); + // Search for the driver type in the all assemblies, + // bazel uses unpredictable assembly names to execute tests + driverType = AppDomain.CurrentDomain.GetAssemblies() + .Reverse() + .Select(assembly => assembly.GetType(driverConfig.DriverTypeName)) + .FirstOrDefault(t => t != null); - if (driverType == null) - { - throw new ArgumentOutOfRangeException($"Unable to find driver type {driverConfig.DriverTypeName}"); - } + if (driverType == null) + { + throw new ArgumentOutOfRangeException($"Unable to find driver type {driverConfig.DriverTypeName}"); + } - browser = driverConfig.BrowserValue; - remoteCapabilities = driverConfig.RemoteCapabilities; + browser = driverConfig.BrowserValue; + remoteCapabilities = driverConfig.RemoteCapabilities; - urlBuilder = new UrlBuilder(websiteConfig); + urlBuilder = new UrlBuilder(websiteConfig); - // When run using the `bazel test` command, the following environment - // variable will be set. If not set, we're running from a build system - // outside Bazel, and need to locate the directory containing the jar. - string projectRoot = System.Environment.GetEnvironmentVariable("TEST_SRCDIR"); - if (string.IsNullOrEmpty(projectRoot)) + // When run using the `bazel test` command, the following environment + // variable will be set. If not set, we're running from a build system + // outside Bazel, and need to locate the directory containing the jar. + string projectRoot = System.Environment.GetEnvironmentVariable("TEST_SRCDIR"); + if (string.IsNullOrEmpty(projectRoot)) + { + // Walk up the directory tree until we find ourselves in a directory + // where the path to the Java web server can be determined. + bool continueTraversal = true; + DirectoryInfo info = new DirectoryInfo(currentDirectory); + while (continueTraversal) { - // Walk up the directory tree until we find ourselves in a directory - // where the path to the Java web server can be determined. - bool continueTraversal = true; - DirectoryInfo info = new DirectoryInfo(currentDirectory); - while (continueTraversal) + if (info == info.Root) { - if (info == info.Root) + break; + } + + foreach (var childDir in info.EnumerateDirectories()) + { + // Case 1: The current directory of this assembly is in the + // same direct sub-tree as the Java targets (usually meaning + // executing tests from the same build system as that which + // builds the Java targets). + // If we find a child directory named "java", then the web + // server should be able to be found under there. + if (string.Compare(childDir.Name, "java", StringComparison.OrdinalIgnoreCase) == 0) { + continueTraversal = false; break; } - foreach (var childDir in info.EnumerateDirectories()) + // Case 2: The current directory of this assembly is a different + // sub-tree as the Java targets (usually meaning executing tests + // from a different build system as that which builds the Java + // targets). + // If we travel to a place in the tree where there is a child + // directory named "bazel-bin", the web server should be found + // in the "java" subdirectory of that directory. + if (string.Compare(childDir.Name, "bazel-bin", StringComparison.OrdinalIgnoreCase) == 0) { - // Case 1: The current directory of this assembly is in the - // same direct sub-tree as the Java targets (usually meaning - // executing tests from the same build system as that which - // builds the Java targets). - // If we find a child directory named "java", then the web - // server should be able to be found under there. - if (string.Compare(childDir.Name, "java", StringComparison.OrdinalIgnoreCase) == 0) + string javaOutDirectory = Path.Combine(childDir.FullName, "java"); + if (Directory.Exists(javaOutDirectory)) { + info = childDir; continueTraversal = false; break; } - - // Case 2: The current directory of this assembly is a different - // sub-tree as the Java targets (usually meaning executing tests - // from a different build system as that which builds the Java - // targets). - // If we travel to a place in the tree where there is a child - // directory named "bazel-bin", the web server should be found - // in the "java" subdirectory of that directory. - if (string.Compare(childDir.Name, "bazel-bin", StringComparison.OrdinalIgnoreCase) == 0) - { - string javaOutDirectory = Path.Combine(childDir.FullName, "java"); - if (Directory.Exists(javaOutDirectory)) - { - info = childDir; - continueTraversal = false; - break; - } - } - } - - if (continueTraversal) - { - info = info.Parent; } } - projectRoot = info.FullName; - } - else - { - projectRoot += "/_main"; - } - - // Find selenium-manager binary. - try - { - string managerFilePath = ""; - runfiles ??= Runfiles.Create(); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/windows/selenium-manager.exe"); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (continueTraversal) { - managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/linux/selenium-manager"); + info = info.Parent; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/macos/selenium-manager"); - } - - System.Environment.SetEnvironmentVariable("SE_MANAGER_PATH", managerFilePath); - } - catch (FileNotFoundException) - { - // Use the default one. - } - - webServer = new TestWebServer(projectRoot, webServerConfig); - bool autoStartRemoteServer = false; - if (browser == Browser.Remote) - { - autoStartRemoteServer = driverConfig.AutoStartRemoteServer; } - remoteServer = new RemoteSeleniumServer(projectRoot, autoStartRemoteServer); + projectRoot = info.FullName; + } + else + { + projectRoot += "/_main"; } - ~EnvironmentManager() + // Find selenium-manager binary. + try { - if (remoteServer != null) + string managerFilePath = ""; + runfiles ??= Runfiles.Create(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - remoteServer.StopAsync().Wait(); + managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/windows/selenium-manager.exe"); } - if (webServer != null) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - webServer.StopAsync().Wait(); + managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/linux/selenium-manager"); } - CloseCurrentDriver(); - } - - public event EventHandler DriverStarting; - - public static EnvironmentManager Instance - { - get + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - if (instance == null) - { - instance = new EnvironmentManager(); - } - - return instance; + managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/macos/selenium-manager"); } - } - public Browser Browser - { - get { return browser; } + System.Environment.SetEnvironmentVariable("SE_MANAGER_PATH", managerFilePath); } - - public string CurrentDirectory + catch (FileNotFoundException) { - get - { - string assemblyLocation = Path.GetDirectoryName(typeof(EnvironmentManager).Assembly.Location); - string testDirectory = TestContext.CurrentContext.TestDirectory; - if (assemblyLocation != testDirectory) - { - return assemblyLocation; - } - return testDirectory; - } + // Use the default one. } - public TestWebServer WebServer + webServer = new TestWebServer(projectRoot, webServerConfig); + bool autoStartRemoteServer = false; + if (browser == Browser.Remote) { - get { return webServer; } + autoStartRemoteServer = driverConfig.AutoStartRemoteServer; } - public RemoteSeleniumServer RemoteServer + remoteServer = new RemoteSeleniumServer(projectRoot, autoStartRemoteServer); + } + + ~EnvironmentManager() + { + if (remoteServer != null) { - get { return remoteServer; } + remoteServer.StopAsync().Wait(); } - - public string RemoteCapabilities + if (webServer != null) { - get { return remoteCapabilities; } + webServer.StopAsync().Wait(); } + CloseCurrentDriver(); + } + + public event EventHandler DriverStarting; - public UrlBuilder UrlBuilder + public static EnvironmentManager Instance + { + get { - get + if (instance == null) { - return urlBuilder; + instance = new EnvironmentManager(); } + + return instance; } + } + + public Browser Browser + { + get { return browser; } + } - public IWebDriver GetCurrentDriver() + public string CurrentDirectory + { + get { - if (driver != null) + string assemblyLocation = Path.GetDirectoryName(typeof(EnvironmentManager).Assembly.Location); + string testDirectory = TestContext.CurrentContext.TestDirectory; + if (assemblyLocation != testDirectory) { - return driver; - } - else - { - return CreateFreshDriver(); + return assemblyLocation; } + return testDirectory; } + } - public IWebDriver CreateDriverInstance() - { - return driverFactory.CreateDriver(driverType); - } + public TestWebServer WebServer + { + get { return webServer; } + } + + public RemoteSeleniumServer RemoteServer + { + get { return remoteServer; } + } - public IWebDriver CreateDriverInstance(DriverOptions options) + public string RemoteCapabilities + { + get { return remoteCapabilities; } + } + + public UrlBuilder UrlBuilder + { + get { - return driverFactory.CreateDriverWithOptions(driverType, options); + return urlBuilder; } + } - public IWebDriver CreateFreshDriver() + public IWebDriver GetCurrentDriver() + { + if (driver != null) { - CloseCurrentDriver(); - driver = CreateDriverInstance(); return driver; } + else + { + return CreateFreshDriver(); + } + } + + public IWebDriver CreateDriverInstance() + { + return driverFactory.CreateDriver(driverType); + } - public void CloseCurrentDriver() + public IWebDriver CreateDriverInstance(DriverOptions options) + { + return driverFactory.CreateDriverWithOptions(driverType, options); + } + + public IWebDriver CreateFreshDriver() + { + CloseCurrentDriver(); + driver = CreateDriverInstance(); + return driver; + } + + public void CloseCurrentDriver() + { + if (driver != null) { - if (driver != null) - { - driver.Quit(); - } - driver = null; + driver.Quit(); } + driver = null; + } - protected void OnDriverStarting(object sender, DriverStartingEventArgs e) + protected void OnDriverStarting(object sender, DriverStartingEventArgs e) + { + if (this.DriverStarting != null) { - if (this.DriverStarting != null) - { - this.DriverStarting(sender, e); - } + this.DriverStarting(sender, e); } } } diff --git a/dotnet/test/common/Environment/InlinePage.cs b/dotnet/test/common/Environment/InlinePage.cs index 660f959a46e01..bed0ad7d54fd0 100644 --- a/dotnet/test/common/Environment/InlinePage.cs +++ b/dotnet/test/common/Environment/InlinePage.cs @@ -20,93 +20,92 @@ using System.Collections.Generic; using System.Text; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class InlinePage { - public class InlinePage + private string title = string.Empty; + private List scripts = new List(); + private List styles = new List(); + private List bodyParts = new List(); + private string onLoad; + private string onBeforeUnload; + + public InlinePage WithTitle(string title) { - private string title = string.Empty; - private List scripts = new List(); - private List styles = new List(); - private List bodyParts = new List(); - private string onLoad; - private string onBeforeUnload; + this.title = title; + return this; + } - public InlinePage WithTitle(string title) - { - this.title = title; - return this; - } + public InlinePage WithScripts(params string[] scripts) + { + this.scripts.AddRange(scripts); + return this; + } - public InlinePage WithScripts(params string[] scripts) - { - this.scripts.AddRange(scripts); - return this; - } + public InlinePage WithStyles(params string[] styles) + { + this.styles.AddRange(styles); + return this; + } + + public InlinePage WithBody(params string[] bodyParts) + { + this.bodyParts.AddRange(bodyParts); + return this; + } - public InlinePage WithStyles(params string[] styles) + public InlinePage WithOnLoad(string onLoad) + { + this.onLoad = onLoad; + return this; + } + + public InlinePage WithOnBeforeUnload(string onBeforeUnload) + { + this.onBeforeUnload = onBeforeUnload; + return this; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(""); + builder.Append(""); + builder.AppendFormat("{0}", this.title); + builder.Append(""); + builder.Append(""); + builder.Append(""); + builder.Append(""); + foreach (string bodyPart in this.bodyParts) { - StringBuilder builder = new StringBuilder(""); - builder.Append(""); - builder.AppendFormat("{0}", this.title); - builder.Append(""); - builder.Append(""); - builder.Append(""); - builder.Append(""); - foreach (string bodyPart in this.bodyParts) - { - builder.Append(bodyPart).Append("\n"); - } - - builder.Append(""); - builder.Append(""); - return builder.ToString(); + builder.Append(bodyPart).Append("\n"); } + + builder.Append(""); + builder.Append(""); + return builder.ToString(); } } diff --git a/dotnet/test/common/Environment/RemoteSeleniumServer.cs b/dotnet/test/common/Environment/RemoteSeleniumServer.cs index 8c12943c928f3..bc9fed6f5b8fe 100644 --- a/dotnet/test/common/Environment/RemoteSeleniumServer.cs +++ b/dotnet/test/common/Environment/RemoteSeleniumServer.cs @@ -24,93 +24,92 @@ using System.Net.Http; using System.Threading.Tasks; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class RemoteSeleniumServer { - public class RemoteSeleniumServer - { - private Process webserverProcess; - private string serverJarName = @"java/src/org/openqa/selenium/grid/selenium_server_deploy.jar"; - private string projectRootPath; - private bool autoStart; + private Process webserverProcess; + private string serverJarName = @"java/src/org/openqa/selenium/grid/selenium_server_deploy.jar"; + private string projectRootPath; + private bool autoStart; - public RemoteSeleniumServer(string projectRoot, bool autoStartServer) - { - projectRootPath = projectRoot; - autoStart = autoStartServer; - } + public RemoteSeleniumServer(string projectRoot, bool autoStartServer) + { + projectRootPath = projectRoot; + autoStart = autoStartServer; + } - public async Task StartAsync() + public async Task StartAsync() + { + if (autoStart && (webserverProcess == null || webserverProcess.HasExited)) { - if (autoStart && (webserverProcess == null || webserverProcess.HasExited)) + serverJarName = serverJarName.Replace('/', Path.DirectorySeparatorChar); + if (!File.Exists(Path.Combine(projectRootPath, serverJarName))) { - serverJarName = serverJarName.Replace('/', Path.DirectorySeparatorChar); - if (!File.Exists(Path.Combine(projectRootPath, serverJarName))) - { - throw new FileNotFoundException( - string.Format( - "Selenium server jar at {0} didn't exist - please build it using something like {1}", - serverJarName, - "go //java/src/org/openqa/grid/selenium:selenium")); - } + throw new FileNotFoundException( + string.Format( + "Selenium server jar at {0} didn't exist - please build it using something like {1}", + serverJarName, + "go //java/src/org/openqa/grid/selenium:selenium")); + } - webserverProcess = new Process(); - webserverProcess.StartInfo.FileName = "java.exe"; - webserverProcess.StartInfo.Arguments = " -jar " + serverJarName + " standalone --port 6000 --selenium-manager true --enable-managed-downloads true"; - webserverProcess.StartInfo.WorkingDirectory = projectRootPath; - webserverProcess.Start(); - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(30)); - bool isRunning = false; + webserverProcess = new Process(); + webserverProcess.StartInfo.FileName = "java.exe"; + webserverProcess.StartInfo.Arguments = " -jar " + serverJarName + " standalone --port 6000 --selenium-manager true --enable-managed-downloads true"; + webserverProcess.StartInfo.WorkingDirectory = projectRootPath; + webserverProcess.Start(); + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(30)); + bool isRunning = false; - // Poll until the webserver is correctly serving pages. - using var httpClient = new HttpClient(); + // Poll until the webserver is correctly serving pages. + using var httpClient = new HttpClient(); - while (!isRunning && DateTime.Now < timeout) + while (!isRunning && DateTime.Now < timeout) + { + try { - try - { - using var response = await httpClient.GetAsync("http://localhost:6000/wd/hub/status"); + using var response = await httpClient.GetAsync("http://localhost:6000/wd/hub/status"); - if (response.StatusCode == HttpStatusCode.OK) - { - isRunning = true; - } - } - catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) + if (response.StatusCode == HttpStatusCode.OK) { + isRunning = true; } } - - if (!isRunning) + catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) { - throw new TimeoutException("Could not start the remote selenium server in 30 seconds"); } } + + if (!isRunning) + { + throw new TimeoutException("Could not start the remote selenium server in 30 seconds"); + } } + } - public async Task StopAsync() + public async Task StopAsync() + { + if (autoStart && webserverProcess != null && !webserverProcess.HasExited) { - if (autoStart && webserverProcess != null && !webserverProcess.HasExited) + using (var httpClient = new HttpClient()) { - using (var httpClient = new HttpClient()) + try { - try - { - using var response = await httpClient.GetAsync("http://localhost:6000/selenium-server/driver?cmd=shutDownSeleniumServer"); - } - catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) - { - } + using var response = await httpClient.GetAsync("http://localhost:6000/selenium-server/driver?cmd=shutDownSeleniumServer"); } - - webserverProcess.WaitForExit(10000); - if (!webserverProcess.HasExited) + catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) { - webserverProcess.Kill(); } + } - webserverProcess.Dispose(); - webserverProcess = null; + webserverProcess.WaitForExit(10000); + if (!webserverProcess.HasExited) + { + webserverProcess.Kill(); } + + webserverProcess.Dispose(); + webserverProcess = null; } } } diff --git a/dotnet/test/common/Environment/TestEnvironment.cs b/dotnet/test/common/Environment/TestEnvironment.cs index 40bffe0c739b3..93f4551d43fd8 100644 --- a/dotnet/test/common/Environment/TestEnvironment.cs +++ b/dotnet/test/common/Environment/TestEnvironment.cs @@ -20,33 +20,32 @@ using Newtonsoft.Json; using System.Collections.Generic; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +[JsonObject] +class TestEnvironment { - [JsonObject] - class TestEnvironment - { - [JsonProperty] - public bool CaptureWebServerOutput { get; set; } + [JsonProperty] + public bool CaptureWebServerOutput { get; set; } - [JsonProperty] - public string DriverServiceLocation { get; set; } + [JsonProperty] + public string DriverServiceLocation { get; set; } - [JsonProperty] - public bool HideWebServerCommandPrompt { get; set; } + [JsonProperty] + public bool HideWebServerCommandPrompt { get; set; } - [JsonProperty] - public string ActiveDriverConfig { get; set; } + [JsonProperty] + public string ActiveDriverConfig { get; set; } - [JsonProperty] - public string ActiveWebsiteConfig { get; set; } + [JsonProperty] + public string ActiveWebsiteConfig { get; set; } - [JsonProperty] - public Dictionary WebSiteConfigs { get; set; } + [JsonProperty] + public Dictionary WebSiteConfigs { get; set; } - [JsonProperty] - public Dictionary DriverConfigs { get; set; } + [JsonProperty] + public Dictionary DriverConfigs { get; set; } - [JsonProperty] - public TestWebServerConfig TestWebServerConfig { get; set; } - } + [JsonProperty] + public TestWebServerConfig TestWebServerConfig { get; set; } } diff --git a/dotnet/test/common/Environment/TestWebServer.cs b/dotnet/test/common/Environment/TestWebServer.cs index 3ab1dbddb8a85..c4c95919141ca 100644 --- a/dotnet/test/common/Environment/TestWebServer.cs +++ b/dotnet/test/common/Environment/TestWebServer.cs @@ -25,144 +25,143 @@ using System.Net.Http; using System.Threading.Tasks; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class TestWebServer { - public class TestWebServer - { - private Process webserverProcess; + private Process webserverProcess; - private string standaloneAppserverPath; - private string projectRootPath; - private bool captureWebServerOutput; - private bool hideCommandPrompt; - private string port; + private string standaloneAppserverPath; + private string projectRootPath; + private bool captureWebServerOutput; + private bool hideCommandPrompt; + private string port; - public TestWebServer(string projectRoot, TestWebServerConfig config) - { - this.projectRootPath = projectRoot; - this.captureWebServerOutput = config.CaptureConsoleOutput; - this.hideCommandPrompt = config.HideCommandPromptWindow; - this.port = config.Port; - } + public TestWebServer(string projectRoot, TestWebServerConfig config) + { + this.projectRootPath = projectRoot; + this.captureWebServerOutput = config.CaptureConsoleOutput; + this.hideCommandPrompt = config.HideCommandPromptWindow; + this.port = config.Port; + } - public async Task StartAsync() + public async Task StartAsync() + { + if (webserverProcess == null || webserverProcess.HasExited) { - if (webserverProcess == null || webserverProcess.HasExited) + try { - try - { - var runfiles = Runfiles.Create(); - standaloneAppserverPath = runfiles.Rlocation(@"_main/java/test/org/openqa/selenium/environment/appserver"); - } - catch (FileNotFoundException) - { - // means we are NOT running under bazel runtime - // most likely in IDE - } + var runfiles = Runfiles.Create(); + standaloneAppserverPath = runfiles.Rlocation(@"_main/java/test/org/openqa/selenium/environment/appserver"); + } + catch (FileNotFoundException) + { + // means we are NOT running under bazel runtime + // most likely in IDE + } - var processFileName = standaloneAppserverPath ?? "bazel"; + var processFileName = standaloneAppserverPath ?? "bazel"; - string processArguments = $"{port}"; + string processArguments = $"{port}"; - if (standaloneAppserverPath is null) - { - processArguments = $"run //java/test/org/openqa/selenium/environment:appserver {processArguments}"; + if (standaloneAppserverPath is null) + { + processArguments = $"run //java/test/org/openqa/selenium/environment:appserver {processArguments}"; - // Override project root path to be exact selenium repo path, not 'bazel-bin' - projectRootPath = Path.Combine(AppContext.BaseDirectory, "../../../../../.."); - } + // Override project root path to be exact selenium repo path, not 'bazel-bin' + projectRootPath = Path.Combine(AppContext.BaseDirectory, "../../../../../.."); + } - webserverProcess = new Process(); + webserverProcess = new Process(); - webserverProcess.StartInfo.FileName = processFileName; - webserverProcess.StartInfo.Arguments = processArguments; - webserverProcess.StartInfo.WorkingDirectory = projectRootPath; - webserverProcess.StartInfo.UseShellExecute = !(hideCommandPrompt || captureWebServerOutput); - webserverProcess.StartInfo.CreateNoWindow = hideCommandPrompt; + webserverProcess.StartInfo.FileName = processFileName; + webserverProcess.StartInfo.Arguments = processArguments; + webserverProcess.StartInfo.WorkingDirectory = projectRootPath; + webserverProcess.StartInfo.UseShellExecute = !(hideCommandPrompt || captureWebServerOutput); + webserverProcess.StartInfo.CreateNoWindow = hideCommandPrompt; - captureWebServerOutput = true; + captureWebServerOutput = true; - if (captureWebServerOutput) - { - webserverProcess.StartInfo.RedirectStandardOutput = true; - webserverProcess.StartInfo.RedirectStandardError = true; - } + if (captureWebServerOutput) + { + webserverProcess.StartInfo.RedirectStandardOutput = true; + webserverProcess.StartInfo.RedirectStandardError = true; + } - webserverProcess.Start(); + webserverProcess.Start(); - TimeSpan timeout = TimeSpan.FromSeconds(30); - DateTime endTime = DateTime.Now.Add(TimeSpan.FromSeconds(30)); - bool isRunning = false; + TimeSpan timeout = TimeSpan.FromSeconds(30); + DateTime endTime = DateTime.Now.Add(TimeSpan.FromSeconds(30)); + bool isRunning = false; - // Poll until the webserver is correctly serving pages. - using var httpClient = new HttpClient(); + // Poll until the webserver is correctly serving pages. + using var httpClient = new HttpClient(); - while (!isRunning && DateTime.Now < endTime) + while (!isRunning && DateTime.Now < endTime) + { + try { - try - { - using var response = await httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("simpleTest.html")); + using var response = await httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("simpleTest.html")); - if (response.StatusCode == HttpStatusCode.OK) - { - isRunning = true; - } - } - catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) + if (response.StatusCode == HttpStatusCode.OK) { + isRunning = true; } } + catch (Exception ex) when (ex is HttpRequestException || ex is TimeoutException) + { + } + } - if (!isRunning) + if (!isRunning) + { + string output = "'CaptureWebServerOutput' parameter is false. Web server output not captured"; + string error = "'CaptureWebServerOutput' parameter is false. Web server output not being captured."; + if (captureWebServerOutput) { - string output = "'CaptureWebServerOutput' parameter is false. Web server output not captured"; - string error = "'CaptureWebServerOutput' parameter is false. Web server output not being captured."; - if (captureWebServerOutput) - { - error = webserverProcess.StandardError.ReadToEnd(); - output = webserverProcess.StandardOutput.ReadToEnd(); - } + error = webserverProcess.StandardError.ReadToEnd(); + output = webserverProcess.StandardOutput.ReadToEnd(); + } - string errorMessage = string.Format("Could not start the test web server in {0} seconds.\nWorking directory: {1}\nProcess Args: {2}\nstdout: {3}\nstderr: {4}", timeout.TotalSeconds, projectRootPath, processArguments, output, error); + string errorMessage = string.Format("Could not start the test web server in {0} seconds.\nWorking directory: {1}\nProcess Args: {2}\nstdout: {3}\nstderr: {4}", timeout.TotalSeconds, projectRootPath, processArguments, output, error); - throw new TimeoutException(errorMessage); - } + throw new TimeoutException(errorMessage); } } + } - public async Task StopAsync() + public async Task StopAsync() + { + if (webserverProcess != null) { - if (webserverProcess != null) + using (var httpClient = new HttpClient()) { - using (var httpClient = new HttpClient()) + try { - try - { - using (await httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("quitquitquit"))) - { - - } - } - catch (HttpRequestException) + using (await httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("quitquitquit"))) { } } - - try + catch (HttpRequestException) { - webserverProcess.WaitForExit(10000); - if (!webserverProcess.HasExited) - { - webserverProcess.Kill(entireProcessTree: true); - } + } - finally + } + + try + { + webserverProcess.WaitForExit(10000); + if (!webserverProcess.HasExited) { - webserverProcess.Dispose(); - webserverProcess = null; + webserverProcess.Kill(entireProcessTree: true); } } + finally + { + webserverProcess.Dispose(); + webserverProcess = null; + } } } } diff --git a/dotnet/test/common/Environment/TestWebServerConfig.cs b/dotnet/test/common/Environment/TestWebServerConfig.cs index a70ea76412d28..50c97f1c83f5e 100644 --- a/dotnet/test/common/Environment/TestWebServerConfig.cs +++ b/dotnet/test/common/Environment/TestWebServerConfig.cs @@ -19,20 +19,19 @@ using Newtonsoft.Json; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +[JsonObject] +public class TestWebServerConfig { - [JsonObject] - public class TestWebServerConfig - { - [JsonProperty] - public bool CaptureConsoleOutput { get; set; } + [JsonProperty] + public bool CaptureConsoleOutput { get; set; } - [JsonProperty] - public bool HideCommandPromptWindow { get; set; } + [JsonProperty] + public bool HideCommandPromptWindow { get; set; } - [JsonProperty] - public string JavaHomeDirectory { get; set; } + [JsonProperty] + public string JavaHomeDirectory { get; set; } - public string Port { get; set; } - } + public string Port { get; set; } } diff --git a/dotnet/test/common/Environment/UrlBuilder.cs b/dotnet/test/common/Environment/UrlBuilder.cs index 2bbe6d1c0351c..d9bb5cfc0d96e 100644 --- a/dotnet/test/common/Environment/UrlBuilder.cs +++ b/dotnet/test/common/Environment/UrlBuilder.cs @@ -25,134 +25,133 @@ using System.Net.Sockets; using System.Text; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +public class UrlBuilder { - public class UrlBuilder + string protocol; + string hostName; + string port; + string securePort; + string path; + string alternateHostName; + + public string AlternateHostName { - string protocol; - string hostName; - string port; - string securePort; - string path; - string alternateHostName; - - public string AlternateHostName - { - get { return alternateHostName; } - } + get { return alternateHostName; } + } - public string HostName - { - get { return hostName; } - } + public string HostName + { + get { return hostName; } + } - public string Path - { - get { return path; } - } + public string Path + { + get { return path; } + } - public UrlBuilder(WebsiteConfig config) + public UrlBuilder(WebsiteConfig config) + { + protocol = config.Protocol; + hostName = config.HostName; + port = config.Port; + securePort = config.SecurePort; + path = config.Folder; + //Use the first IPv4 address that we find + IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); + foreach (IPAddress ip in Dns.GetHostEntry(hostName).AddressList) { - protocol = config.Protocol; - hostName = config.HostName; - port = config.Port; - securePort = config.SecurePort; - path = config.Folder; - //Use the first IPv4 address that we find - IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); - foreach (IPAddress ip in Dns.GetHostEntry(hostName).AddressList) + if (ip.AddressFamily == AddressFamily.InterNetwork) { - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - ipAddress = ip; - break; - } + ipAddress = ip; + break; } - alternateHostName = ipAddress.ToString(); } + alternateHostName = ipAddress.ToString(); + } - public string LocalWhereIs(string page) - { - string location = string.Empty; - location = "http://localhost/" + port + "/" + path + "/" + page; + public string LocalWhereIs(string page) + { + string location = string.Empty; + location = "http://localhost/" + port + "/" + path + "/" + page; - return location; - } + return location; + } - public string WhereIs(string page) - { - string location = string.Empty; - location = "http://" + hostName + ":" + port + "/" + path + "/" + page; + public string WhereIs(string page) + { + string location = string.Empty; + location = "http://" + hostName + ":" + port + "/" + path + "/" + page; - return location; - } + return location; + } - public string WhereElseIs(string page) - { - string location = string.Empty; - location = "http://" + alternateHostName + ":" + port + "/" + path + "/" + page; + public string WhereElseIs(string page) + { + string location = string.Empty; + location = "http://" + alternateHostName + ":" + port + "/" + path + "/" + page; - return location; - } + return location; + } - public string WhereIsViaNonLoopbackAddress(string page) + public string WhereIsViaNonLoopbackAddress(string page) + { + string hostNameAsIPAddress = "127.0.0.1"; + IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName()); + foreach (IPAddress address in addresses) { - string hostNameAsIPAddress = "127.0.0.1"; - IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName()); - foreach (IPAddress address in addresses) + if (address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(address)) { - if (address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(address)) - { - hostNameAsIPAddress = address.ToString(); - break; - } + hostNameAsIPAddress = address.ToString(); + break; } - - string location = string.Empty; - location = "http://" + hostNameAsIPAddress + ":" + port + "/" + path + "/" + page; - - return location; } - public string WhereIsSecure(string page) - { - string location = string.Empty; - location = "https://" + hostName + ":" + securePort + "/" + path + "/" + page; + string location = string.Empty; + location = "http://" + hostNameAsIPAddress + ":" + port + "/" + path + "/" + page; - return location; - } - public string CreateInlinePage(InlinePage page) - { - Uri createPageUri = new Uri(new Uri(WhereIs(string.Empty)), "createPage"); + return location; + } - Dictionary payloadDictionary = new Dictionary - { - ["content"] = page.ToString() - }; + public string WhereIsSecure(string page) + { + string location = string.Empty; + location = "https://" + hostName + ":" + securePort + "/" + path + "/" + page; - string commandPayload = JsonConvert.SerializeObject(payloadDictionary); + return location; + } + public string CreateInlinePage(InlinePage page) + { + Uri createPageUri = new Uri(new Uri(WhereIs(string.Empty)), "createPage"); - using var httpClient = new HttpClient(); + Dictionary payloadDictionary = new Dictionary + { + ["content"] = page.ToString() + }; - var postHttpContent = new StringContent(commandPayload, Encoding.UTF8, "application/json"); + string commandPayload = JsonConvert.SerializeObject(payloadDictionary); - using var response = httpClient.PostAsync(createPageUri, postHttpContent).GetAwaiter().GetResult(); + using var httpClient = new HttpClient(); - var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + var postHttpContent = new StringContent(commandPayload, Encoding.UTF8, "application/json"); - // The response string from the Java remote server has trailing null - // characters. This is due to the fix for issue 288. - if (responseString.IndexOf('\0') >= 0) - { - responseString = responseString.Substring(0, responseString.IndexOf('\0')); - } + using var response = httpClient.PostAsync(createPageUri, postHttpContent).GetAwaiter().GetResult(); - if (responseString.Contains("localhost")) - { - responseString = responseString.Replace("localhost", this.hostName); - } + var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - return responseString; + // The response string from the Java remote server has trailing null + // characters. This is due to the fix for issue 288. + if (responseString.IndexOf('\0') >= 0) + { + responseString = responseString.Substring(0, responseString.IndexOf('\0')); } + + if (responseString.Contains("localhost")) + { + responseString = responseString.Replace("localhost", this.hostName); + } + + return responseString; } } diff --git a/dotnet/test/common/Environment/WebsiteConfig.cs b/dotnet/test/common/Environment/WebsiteConfig.cs index a550b796f42d3..fe3b148345067 100644 --- a/dotnet/test/common/Environment/WebsiteConfig.cs +++ b/dotnet/test/common/Environment/WebsiteConfig.cs @@ -19,24 +19,23 @@ using Newtonsoft.Json; -namespace OpenQA.Selenium.Environment +namespace OpenQA.Selenium.Environment; + +[JsonObject] +public class WebsiteConfig { - [JsonObject] - public class WebsiteConfig - { - [JsonProperty] - public string Protocol { get; set; } + [JsonProperty] + public string Protocol { get; set; } - [JsonProperty] - public string HostName { get; set; } + [JsonProperty] + public string HostName { get; set; } - [JsonProperty] - public string Port { get; set; } + [JsonProperty] + public string Port { get; set; } - [JsonProperty] - public string SecurePort { get; set; } + [JsonProperty] + public string SecurePort { get; set; } - [JsonProperty] - public string Folder { get; set; } - } + [JsonProperty] + public string Folder { get; set; } } diff --git a/dotnet/test/common/ErrorsTest.cs b/dotnet/test/common/ErrorsTest.cs index ad5ea3a120833..137534d12fa92 100644 --- a/dotnet/test/common/ErrorsTest.cs +++ b/dotnet/test/common/ErrorsTest.cs @@ -19,24 +19,23 @@ using NUnit.Framework; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ErrorsTest : DriverTestFixture { - [TestFixture] - public class ErrorsTest : DriverTestFixture + /// + /// Regression test for Selenium RC issue 363. + /// http://code.google.com/p/selenium/issues/detail?id=363 + /// This will trivially pass on browsers that do not support the onerror + /// handler (e.g. Internet Explorer). + /// + [Test] + public void ShouldNotGenerateErrorsWhenOpeningANewPage() { - /// - /// Regression test for Selenium RC issue 363. - /// http://code.google.com/p/selenium/issues/detail?id=363 - /// This will trivially pass on browsers that do not support the onerror - /// handler (e.g. Internet Explorer). - /// - [Test] - public void ShouldNotGenerateErrorsWhenOpeningANewPage() - { - driver.Url = errorsPage; - object result = ((IJavaScriptExecutor)driver).ExecuteScript("return window.ERRORS.join('\\n');"); - Assert.That(result, Is.Empty, "Should have no errors"); - } - + driver.Url = errorsPage; + object result = ((IJavaScriptExecutor)driver).ExecuteScript("return window.ERRORS.join('\\n');"); + Assert.That(result, Is.Empty, "Should have no errors"); } + } diff --git a/dotnet/test/common/ExecutingAsyncJavascriptTest.cs b/dotnet/test/common/ExecutingAsyncJavascriptTest.cs index c663d6ddac78f..219fe1819cd67 100644 --- a/dotnet/test/common/ExecutingAsyncJavascriptTest.cs +++ b/dotnet/test/common/ExecutingAsyncJavascriptTest.cs @@ -21,345 +21,344 @@ using System; using System.Collections.ObjectModel; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ExecutingAsyncJavascriptTest : DriverTestFixture { - [TestFixture] - public class ExecutingAsyncJavascriptTest : DriverTestFixture - { - private IJavaScriptExecutor executor; - private TimeSpan originalTimeout = TimeSpan.MinValue; + private IJavaScriptExecutor executor; + private TimeSpan originalTimeout = TimeSpan.MinValue; - [SetUp] - public void SetUpEnvironment() + [SetUp] + public void SetUpEnvironment() + { + if (driver is IJavaScriptExecutor) { - if (driver is IJavaScriptExecutor) - { - executor = (IJavaScriptExecutor)driver; - } - - try - { - originalTimeout = driver.Manage().Timeouts().AsynchronousJavaScript; - } - catch (NotImplementedException) - { - // For driver implementations that do not support getting timeouts, - // just set a default 30-second timeout. - originalTimeout = TimeSpan.FromSeconds(30); - } - - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(1); + executor = (IJavaScriptExecutor)driver; } - [TearDown] - public void TearDownEnvironment() + try { - driver.Manage().Timeouts().AsynchronousJavaScript = originalTimeout; + originalTimeout = driver.Manage().Timeouts().AsynchronousJavaScript; } - - [Test] - public void ShouldNotTimeoutIfCallbackInvokedImmediately() + catch (NotImplementedException) { - driver.Url = ajaxyPage; - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"); - Assert.That(result, Is.InstanceOf()); - Assert.That((long)result, Is.EqualTo(123)); + // For driver implementations that do not support getting timeouts, + // just set a default 30-second timeout. + originalTimeout = TimeSpan.FromSeconds(30); } - [Test] - public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() - { - driver.Url = ajaxyPage; - Assert.That((long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"), Is.EqualTo(123)); - driver.Url = ajaxyPage; - Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]('abc');").ToString(), Is.EqualTo("abc")); - driver.Url = ajaxyPage; - Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](false);"), Is.False); - driver.Url = ajaxyPage; - Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); - } + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(1); + } - [Test] - public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() - { - driver.Url = ajaxyPage; - Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1](null);"), Is.Null); - Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]();"), Is.Null); - } + [TearDown] + public void TearDownEnvironment() + { + driver.Manage().Timeouts().AsynchronousJavaScript = originalTimeout; + } - [Test] - public void ShouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() - { - driver.Url = ajaxyPage; + [Test] + public void ShouldNotTimeoutIfCallbackInvokedImmediately() + { + driver.Url = ajaxyPage; + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"); + Assert.That(result, Is.InstanceOf()); + Assert.That((long)result, Is.EqualTo(123)); + } - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([]);"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf>()); - Assert.That((ReadOnlyCollection)result, Has.Count.EqualTo(0)); - } + [Test] + public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() + { + driver.Url = ajaxyPage; + Assert.That((long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"), Is.EqualTo(123)); + driver.Url = ajaxyPage; + Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]('abc');").ToString(), Is.EqualTo("abc")); + driver.Url = ajaxyPage; + Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](false);"), Is.False); + driver.Url = ajaxyPage; + Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); + } - [Test] - public void ShouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() - { - driver.Url = ajaxyPage; + [Test] + public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() + { + driver.Url = ajaxyPage; + Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1](null);"), Is.Null); + Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]();"), Is.Null); + } - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](new Array());"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf>()); - Assert.That((ReadOnlyCollection)result, Has.Count.EqualTo(0)); - } + [Test] + public void ShouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() + { + driver.Url = ajaxyPage; - [Test] - public void ShouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() - { - driver.Url = ajaxyPage; - - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([null, 123, 'abc', true, false]);"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf>()); - ReadOnlyCollection resultList = result as ReadOnlyCollection; - Assert.That(resultList, Has.Count.EqualTo(5)); - Assert.That(resultList[0], Is.Null); - Assert.That((long)resultList[1], Is.EqualTo(123)); - Assert.That(resultList[2].ToString(), Is.EqualTo("abc")); - Assert.That((bool)resultList[3], Is.True); - Assert.That((bool)resultList[4], Is.False); - } + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([]);"); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That((ReadOnlyCollection)result, Has.Count.EqualTo(0)); + } - [Test] - public void ShouldBeAbleToReturnWebElementsFromAsyncScripts() - { - driver.Url = ajaxyPage; + [Test] + public void ShouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() + { + driver.Url = ajaxyPage; - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](document.body);"); - Assert.That(result, Is.InstanceOf()); - Assert.That(((IWebElement)result).TagName.ToLower(), Is.EqualTo("body")); - } + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](new Array());"); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That((ReadOnlyCollection)result, Has.Count.EqualTo(0)); + } - [Test] - public void ShouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() - { - driver.Url = ajaxyPage; - - object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([document.body, document.body]);"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf>()); - ReadOnlyCollection resultsList = (ReadOnlyCollection)result; - Assert.That(resultsList, Has.Count.EqualTo(2)); - Assert.That(resultsList[0], Is.InstanceOf()); - Assert.That(resultsList[1], Is.InstanceOf()); - Assert.That(((IWebElement)resultsList[0]).TagName.ToLower(), Is.EqualTo("body")); - Assert.That(((IWebElement)resultsList[0]), Is.EqualTo((IWebElement)resultsList[1])); - } + [Test] + public void ShouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() + { + driver.Url = ajaxyPage; + + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([null, 123, 'abc', true, false]);"); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + ReadOnlyCollection resultList = result as ReadOnlyCollection; + Assert.That(resultList, Has.Count.EqualTo(5)); + Assert.That(resultList[0], Is.Null); + Assert.That((long)resultList[1], Is.EqualTo(123)); + Assert.That(resultList[2].ToString(), Is.EqualTo("abc")); + Assert.That((bool)resultList[3], Is.True); + Assert.That((bool)resultList[4], Is.False); + } - [Test] - public void ShouldTimeoutIfScriptDoesNotInvokeCallback() - { - driver.Url = ajaxyPage; - Assert.That(() => executor.ExecuteAsyncScript("return 1 + 2;"), Throws.InstanceOf()); - } + [Test] + public void ShouldBeAbleToReturnWebElementsFromAsyncScripts() + { + driver.Url = ajaxyPage; - [Test] - public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() - { - driver.Url = ajaxyPage; - Assert.That(() => executor.ExecuteAsyncScript("window.setTimeout(function() {}, 0);"), Throws.InstanceOf()); - } + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](document.body);"); + Assert.That(result, Is.InstanceOf()); + Assert.That(((IWebElement)result).TagName.ToLower(), Is.EqualTo("body")); + } - [Test] - public void ShouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() - { - driver.Url = ajaxyPage; - executor.ExecuteAsyncScript( - "var callback = arguments[arguments.length - 1];" + - "window.setTimeout(function() { callback(123); }, 0)"); - } + [Test] + public void ShouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() + { + driver.Url = ajaxyPage; + + object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([document.body, document.body]);"); + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + ReadOnlyCollection resultsList = (ReadOnlyCollection)result; + Assert.That(resultsList, Has.Count.EqualTo(2)); + Assert.That(resultsList[0], Is.InstanceOf()); + Assert.That(resultsList[1], Is.InstanceOf()); + Assert.That(((IWebElement)resultsList[0]).TagName.ToLower(), Is.EqualTo("body")); + Assert.That(((IWebElement)resultsList[0]), Is.EqualTo((IWebElement)resultsList[1])); + } - [Test] - public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() - { - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(500); - driver.Url = ajaxyPage; - Assert.That(() => executor.ExecuteAsyncScript( - "var callback = arguments[arguments.length - 1];" + - "window.setTimeout(callback, 1500);"), Throws.InstanceOf()); - } + [Test] + public void ShouldTimeoutIfScriptDoesNotInvokeCallback() + { + driver.Url = ajaxyPage; + Assert.That(() => executor.ExecuteAsyncScript("return 1 + 2;"), Throws.InstanceOf()); + } - [Test] - public void ShouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() - { - driver.Url = ajaxyPage; - Assert.That(() => executor.ExecuteAsyncScript("window.location = '" + dynamicPage + "';"), Throws.InstanceOf()); - } + [Test] + public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() + { + driver.Url = ajaxyPage; + Assert.That(() => executor.ExecuteAsyncScript("window.setTimeout(function() {}, 0);"), Throws.InstanceOf()); + } - [Test] - public void ShouldCatchErrorsWhenExecutingInitialScript() - { - driver.Url = ajaxyPage; - Assert.That(() => executor.ExecuteAsyncScript("throw Error('you should catch this!');"), Throws.InstanceOf()); - } + [Test] + public void ShouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() + { + driver.Url = ajaxyPage; + executor.ExecuteAsyncScript( + "var callback = arguments[arguments.length - 1];" + + "window.setTimeout(function() { callback(123); }, 0)"); + } - [Test] - public void ShouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() - { - driver.Url = ajaxyPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(1000); - Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); - Assert.That((bool)executor.ExecuteAsyncScript("var cb = arguments[arguments.length - 1]; window.setTimeout(function(){cb(true);}, 9);"), Is.True); - } + [Test] + public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() + { + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(500); + driver.Url = ajaxyPage; + Assert.That(() => executor.ExecuteAsyncScript( + "var callback = arguments[arguments.length - 1];" + + "window.setTimeout(callback, 1500);"), Throws.InstanceOf()); + } - [Test] - [IgnoreBrowser(Browser.Chrome, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Edge, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Firefox, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.IE, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Safari, ".NET language bindings do not properly parse JavaScript stack trace")] - public void ShouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() - { - driver.Url = ajaxyPage; - string js = "function functionB() { throw Error('errormessage'); };" - + "function functionA() { functionB(); };" - + "functionA();"; - - Assert.That( - () => executor.ExecuteAsyncScript(js), - Throws.InstanceOf() - .With.Message.Contains("errormessage") - .And.Property(nameof(WebDriverException.StackTrace)).Contains("functionB")); - } + [Test] + public void ShouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() + { + driver.Url = ajaxyPage; + Assert.That(() => executor.ExecuteAsyncScript("window.location = '" + dynamicPage + "';"), Throws.InstanceOf()); + } - [Test] - public void ShouldBeAbleToExecuteAsynchronousScripts() - { - // Reset the timeout to the 30-second default instead of zero. - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(30); - driver.Url = ajaxyPage; + [Test] + public void ShouldCatchErrorsWhenExecutingInitialScript() + { + driver.Url = ajaxyPage; + Assert.That(() => executor.ExecuteAsyncScript("throw Error('you should catch this!');"), Throws.InstanceOf()); + } - IWebElement typer = driver.FindElement(By.Name("typer")); - typer.SendKeys("bob"); - Assert.That(typer.GetAttribute("value"), Is.EqualTo("bob")); + [Test] + public void ShouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() + { + driver.Url = ajaxyPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(1000); + Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); + Assert.That((bool)executor.ExecuteAsyncScript("var cb = arguments[arguments.length - 1]; window.setTimeout(function(){cb(true);}, 9);"), Is.True); + } - driver.FindElement(By.Id("red")).Click(); - driver.FindElement(By.Name("submit")).Click(); + [Test] + [IgnoreBrowser(Browser.Chrome, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Edge, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Firefox, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.IE, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Safari, ".NET language bindings do not properly parse JavaScript stack trace")] + public void ShouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() + { + driver.Url = ajaxyPage; + string js = "function functionB() { throw Error('errormessage'); };" + + "function functionA() { functionB(); };" + + "functionA();"; + + Assert.That( + () => executor.ExecuteAsyncScript(js), + Throws.InstanceOf() + .With.Message.Contains("errormessage") + .And.Property(nameof(WebDriverException.StackTrace)).Contains("functionB")); + } - Assert.That(GetNumberOfDivElements(), Is.EqualTo(1), "There should only be 1 DIV at this point, which is used for the butter message"); + [Test] + public void ShouldBeAbleToExecuteAsynchronousScripts() + { + // Reset the timeout to the 30-second default instead of zero. + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(30); + driver.Url = ajaxyPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(10); - string text = (string)executor.ExecuteAsyncScript( - "var callback = arguments[arguments.length - 1];" - + "window.registerListener(arguments[arguments.length - 1]);"); - Assert.That(text, Is.EqualTo("bob")); - Assert.That(typer.GetAttribute("value"), Is.Empty); + IWebElement typer = driver.FindElement(By.Name("typer")); + typer.SendKeys("bob"); + Assert.That(typer.GetAttribute("value"), Is.EqualTo("bob")); - Assert.That(GetNumberOfDivElements(), Is.EqualTo(2), "There should be 1 DIV (for the butter message) + 1 DIV (for the new label)"); - } + driver.FindElement(By.Id("red")).Click(); + driver.FindElement(By.Name("submit")).Click(); - [Test] - public void ShouldBeAbleToPassMultipleArgumentsToAsyncScripts() - { - driver.Url = ajaxyPage; - long result = (long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2); - Assert.That(result, Is.EqualTo(3)); - } + Assert.That(GetNumberOfDivElements(), Is.EqualTo(1), "There should only be 1 DIV at this point, which is used for the butter message"); - [Test] - public void ShouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() - { - string script = - "var url = arguments[0];" + - "var callback = arguments[arguments.length - 1];" + - // Adapted from http://www.quirksmode.org/js/xmlhttp.html - "var XMLHttpFactories = [" + - " function () {return new XMLHttpRequest()}," + - " function () {return new ActiveXObject('Msxml2.XMLHTTP')}," + - " function () {return new ActiveXObject('Msxml3.XMLHTTP')}," + - " function () {return new ActiveXObject('Microsoft.XMLHTTP')}" + - "];" + - "var xhr = false;" + - "while (!xhr && XMLHttpFactories.length) {" + - " try {" + - " xhr = XMLHttpFactories.shift().call();" + - " } catch (e) {}" + - "}" + - "if (!xhr) throw Error('unable to create XHR object');" + - "xhr.open('GET', url, true);" + - "xhr.onreadystatechange = function() {" + - " if (xhr.readyState == 4) callback(xhr.responseText);" + - "};" + - "xhr.send();"; - - driver.Url = ajaxyPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(3); - string response = (string)executor.ExecuteAsyncScript(script, sleepingPage + "?time=2"); - Assert.That(response.Trim(), Is.EqualTo("DoneSlept for 2s")); - } + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(10); + string text = (string)executor.ExecuteAsyncScript( + "var callback = arguments[arguments.length - 1];" + + "window.registerListener(arguments[arguments.length - 1]);"); + Assert.That(text, Is.EqualTo("bob")); + Assert.That(typer.GetAttribute("value"), Is.Empty); - [Test] - public void ThrowsIfScriptTriggersAlert() - { - driver.Url = simpleTestPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); - ((IJavaScriptExecutor)driver).ExecuteAsyncScript( - "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); - Assert.That(() => driver.Title, Throws.InstanceOf()); + Assert.That(GetNumberOfDivElements(), Is.EqualTo(2), "There should be 1 DIV (for the butter message) + 1 DIV (for the new label)"); + } - string title = driver.Title; - } + [Test] + public void ShouldBeAbleToPassMultipleArgumentsToAsyncScripts() + { + driver.Url = ajaxyPage; + long result = (long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2); + Assert.That(result, Is.EqualTo(3)); + } - [Test] - public void ThrowsIfAlertHappensDuringScript() - { - driver.Url = slowLoadingAlertPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); - ((IJavaScriptExecutor)driver).ExecuteAsyncScript("setTimeout(arguments[0], 1000);"); - Assert.That(() => driver.Title, Throws.InstanceOf()); + [Test] + public void ShouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() + { + string script = + "var url = arguments[0];" + + "var callback = arguments[arguments.length - 1];" + + // Adapted from http://www.quirksmode.org/js/xmlhttp.html + "var XMLHttpFactories = [" + + " function () {return new XMLHttpRequest()}," + + " function () {return new ActiveXObject('Msxml2.XMLHTTP')}," + + " function () {return new ActiveXObject('Msxml3.XMLHTTP')}," + + " function () {return new ActiveXObject('Microsoft.XMLHTTP')}" + + "];" + + "var xhr = false;" + + "while (!xhr && XMLHttpFactories.length) {" + + " try {" + + " xhr = XMLHttpFactories.shift().call();" + + " } catch (e) {}" + + "}" + + "if (!xhr) throw Error('unable to create XHR object');" + + "xhr.open('GET', url, true);" + + "xhr.onreadystatechange = function() {" + + " if (xhr.readyState == 4) callback(xhr.responseText);" + + "};" + + "xhr.send();"; + + driver.Url = ajaxyPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(3); + string response = (string)executor.ExecuteAsyncScript(script, sleepingPage + "?time=2"); + Assert.That(response.Trim(), Is.EqualTo("DoneSlept for 2s")); + } - // Shouldn't throw - string title = driver.Title; - } + [Test] + public void ThrowsIfScriptTriggersAlert() + { + driver.Url = simpleTestPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); + ((IJavaScriptExecutor)driver).ExecuteAsyncScript( + "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); + Assert.That(() => driver.Title, Throws.InstanceOf()); - [Test] - public void ThrowsIfScriptTriggersAlertWhichTimesOut() - { - driver.Url = simpleTestPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); - ((IJavaScriptExecutor)driver) - .ExecuteAsyncScript("setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); - Assert.That(() => driver.Title, Throws.InstanceOf()); - - // Shouldn't throw - string title = driver.Title; - } + string title = driver.Title; + } - [Test] - public void ThrowsIfAlertHappensDuringScriptWhichTimesOut() - { - driver.Url = slowLoadingAlertPage; - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); - ((IJavaScriptExecutor)driver).ExecuteAsyncScript(""); - Assert.That(() => driver.Title, Throws.InstanceOf()); + [Test] + public void ThrowsIfAlertHappensDuringScript() + { + driver.Url = slowLoadingAlertPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); + ((IJavaScriptExecutor)driver).ExecuteAsyncScript("setTimeout(arguments[0], 1000);"); + Assert.That(() => driver.Title, Throws.InstanceOf()); - // Shouldn't throw - string title = driver.Title; - } + // Shouldn't throw + string title = driver.Title; + } - [Test] - [IgnoreBrowser(Browser.Firefox, "Driver chooses not to return text from unhandled alert")] - public void IncludesAlertTextInUnhandledAlertException() - { - driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); - string alertText = "Look! An alert!"; - ((IJavaScriptExecutor)driver).ExecuteAsyncScript( - "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('" + alertText - + "'); }, 50);"); - Assert.That(() => driver.Title, Throws.InstanceOf().With.Property("AlertText").EqualTo(alertText)); - } + [Test] + public void ThrowsIfScriptTriggersAlertWhichTimesOut() + { + driver.Url = simpleTestPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); + ((IJavaScriptExecutor)driver) + .ExecuteAsyncScript("setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); + Assert.That(() => driver.Title, Throws.InstanceOf()); + + // Shouldn't throw + string title = driver.Title; + } - private long GetNumberOfDivElements() - { - IJavaScriptExecutor jsExecutor = driver as IJavaScriptExecutor; - // Selenium does not support "findElements" yet, so we have to do this through a script. - return (long)jsExecutor.ExecuteScript("return document.getElementsByTagName('div').length;"); - } + [Test] + public void ThrowsIfAlertHappensDuringScriptWhichTimesOut() + { + driver.Url = slowLoadingAlertPage; + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); + ((IJavaScriptExecutor)driver).ExecuteAsyncScript(""); + Assert.That(() => driver.Title, Throws.InstanceOf()); + + // Shouldn't throw + string title = driver.Title; + } + + [Test] + [IgnoreBrowser(Browser.Firefox, "Driver chooses not to return text from unhandled alert")] + public void IncludesAlertTextInUnhandledAlertException() + { + driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); + string alertText = "Look! An alert!"; + ((IJavaScriptExecutor)driver).ExecuteAsyncScript( + "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('" + alertText + + "'); }, 50);"); + Assert.That(() => driver.Title, Throws.InstanceOf().With.Property("AlertText").EqualTo(alertText)); + } + + private long GetNumberOfDivElements() + { + IJavaScriptExecutor jsExecutor = driver as IJavaScriptExecutor; + // Selenium does not support "findElements" yet, so we have to do this through a script. + return (long)jsExecutor.ExecuteScript("return document.getElementsByTagName('div').length;"); } } diff --git a/dotnet/test/common/ExecutingJavascriptTest.cs b/dotnet/test/common/ExecutingJavascriptTest.cs index 32cfbbccb08ef..1baa3cd7dba98 100644 --- a/dotnet/test/common/ExecutingJavascriptTest.cs +++ b/dotnet/test/common/ExecutingJavascriptTest.cs @@ -23,957 +23,956 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class ExecutingJavascriptTest : DriverTestFixture { - [TestFixture] - public class ExecutingJavascriptTest : DriverTestFixture + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAString() { - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAString() - { - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = xhtmlTestPage; + driver.Url = xhtmlTestPage; - object result = ExecuteScript("return document.title;"); + object result = ExecuteScript("return document.title;"); - Assert.That(result, Is.InstanceOf()); - Assert.That(result, Is.EqualTo("XHTML Test Page")); - } + Assert.That(result, Is.InstanceOf()); + Assert.That(result, Is.EqualTo("XHTML Test Page")); + } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnALong() - { - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnALong() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = xhtmlTestPage; + driver.Url = xhtmlTestPage; - object result = ExecuteScript("return document.title.length;"); + object result = ExecuteScript("return document.title.length;"); - Assert.That(result, Is.InstanceOf()); - Assert.That((long)result, Is.EqualTo((long)"XHTML Test Page".Length)); - } + Assert.That(result, Is.InstanceOf()); + Assert.That((long)result, Is.EqualTo((long)"XHTML Test Page".Length)); + } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAWebElement() - { - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAWebElement() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = xhtmlTestPage; + driver.Url = xhtmlTestPage; - object result = ExecuteScript("return document.getElementById('id1');"); + object result = ExecuteScript("return document.getElementById('id1');"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf()); - } + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf()); + } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnABoolean() - { - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnABoolean() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = xhtmlTestPage; + driver.Url = xhtmlTestPage; - object result = ExecuteScript("return true;"); + object result = ExecuteScript("return true;"); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.InstanceOf()); - Assert.That((bool)result, Is.True); - } + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf()); + Assert.That((bool)result, Is.True); + } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAStringArray() + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAStringArray() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - List expectedResult = new List(); - expectedResult.Add("zero"); - expectedResult.Add("one"); - expectedResult.Add("two"); - object result = ExecuteScript("return ['zero', 'one', 'two'];"); - Assert.That(result, Is.InstanceOf>()); - ReadOnlyCollection list = (ReadOnlyCollection)result; - Assert.That(list, Is.EqualTo(expectedResult.AsReadOnly())); + return; } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() + driver.Url = javascriptPage; + List expectedResult = new List(); + expectedResult.Add("zero"); + expectedResult.Add("one"); + expectedResult.Add("two"); + object result = ExecuteScript("return ['zero', 'one', 'two'];"); + Assert.That(result, Is.InstanceOf>()); + ReadOnlyCollection list = (ReadOnlyCollection)result; + Assert.That(list, Is.EqualTo(expectedResult.AsReadOnly())); + } + + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() + { + if (!(driver is IJavaScriptExecutor)) + { + return; + } + + driver.Url = javascriptPage; + List expectedResult = new List(); + expectedResult.Add("zero"); + List subList = new List(); + subList.Add(true); + subList.Add(false); + expectedResult.Add(subList.AsReadOnly()); + object result = ExecuteScript("return ['zero', [true, false]];"); + Assert.That(result, Is.InstanceOf>()); + ReadOnlyCollection list = (ReadOnlyCollection)result; + Assert.That(result, Is.EqualTo(expectedResult.AsReadOnly())); + } + + [Test] + public void ShouldBeAbleToExecuteJavascriptAndReturnABasicObjectLiteral() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - List expectedResult = new List(); - expectedResult.Add("zero"); - List subList = new List(); - subList.Add(true); - subList.Add(false); - expectedResult.Add(subList.AsReadOnly()); - object result = ExecuteScript("return ['zero', [true, false]];"); - Assert.That(result, Is.InstanceOf>()); - ReadOnlyCollection list = (ReadOnlyCollection)result; - Assert.That(result, Is.EqualTo(expectedResult.AsReadOnly())); + return; } - [Test] - public void ShouldBeAbleToExecuteJavascriptAndReturnABasicObjectLiteral() + driver.Url = javascriptPage; + + object result = ExecuteScript("return {abc: '123', tired: false};"); + Assert.That(result, Is.InstanceOf>()); + Dictionary map = (Dictionary)result; + + Dictionary expected = new Dictionary(); + expected.Add("abc", "123"); + expected.Add("tired", false); + + Assert.That(map, Has.Count.EqualTo(expected.Count), "Expected:<" + expected.Count + ">, but was:<" + map.Count + ">"); + foreach (string expectedKey in expected.Keys) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - - object result = ExecuteScript("return {abc: '123', tired: false};"); - Assert.That(result, Is.InstanceOf>()); - Dictionary map = (Dictionary)result; - - Dictionary expected = new Dictionary(); - expected.Add("abc", "123"); - expected.Add("tired", false); - - Assert.That(map, Has.Count.EqualTo(expected.Count), "Expected:<" + expected.Count + ">, but was:<" + map.Count + ">"); - foreach (string expectedKey in expected.Keys) - { - Assert.That(map, Does.ContainKey(expectedKey)); - Assert.That(map[expectedKey], Is.EqualTo(expected[expectedKey])); - } + Assert.That(map, Does.ContainKey(expectedKey)); + Assert.That(map[expectedKey], Is.EqualTo(expected[expectedKey])); } + } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAnObjectLiteral() + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAnObjectLiteral() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - - Dictionary expectedPerson = new Dictionary(); - expectedPerson.Add("first", "John"); - expectedPerson.Add("last", "Doe"); - Dictionary expectedResult = new Dictionary(); - expectedResult.Add("foo", "bar"); - List subList = new List() { "a", "b", "c" }; - expectedResult.Add("baz", subList.AsReadOnly()); - expectedResult.Add("person", expectedPerson); - - object result = ExecuteScript( - "return {foo:'bar', baz: ['a', 'b', 'c'], " + - "person: {first: 'John',last: 'Doe'}};"); - Assert.That(result, Is.InstanceOf>()); - - Dictionary map = (Dictionary)result; - Assert.That(map, Has.Count.EqualTo(3)); - foreach (string expectedKey in expectedResult.Keys) - { - Assert.That(map, Does.ContainKey(expectedKey)); - } - - Assert.That(map["foo"], Is.EqualTo("bar")); - Assert.That((ReadOnlyCollection)map["baz"], Is.EqualTo((ReadOnlyCollection)expectedResult["baz"])); - - Dictionary person = (Dictionary)map["person"]; - Assert.That(person, Has.Count.EqualTo(2)); - Assert.That(person["first"], Is.EqualTo("John")); - Assert.That(person["last"], Is.EqualTo("Doe")); + return; } - [Test] - public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAComplexObject() - { - driver.Url = javascriptPage; + driver.Url = javascriptPage; - object result = ExecuteScript("return window.location;"); + Dictionary expectedPerson = new Dictionary(); + expectedPerson.Add("first", "John"); + expectedPerson.Add("last", "Doe"); + Dictionary expectedResult = new Dictionary(); + expectedResult.Add("foo", "bar"); + List subList = new List() { "a", "b", "c" }; + expectedResult.Add("baz", subList.AsReadOnly()); + expectedResult.Add("person", expectedPerson); - Assert.That(result, Is.InstanceOf>()); - Dictionary map = (Dictionary)result; - Assert.That(map["protocol"], Is.EqualTo("http:")); - Assert.That(map["href"], Is.EqualTo(javascriptPage)); - } + object result = ExecuteScript( + "return {foo:'bar', baz: ['a', 'b', 'c'], " + + "person: {first: 'John',last: 'Doe'}};"); + Assert.That(result, Is.InstanceOf>()); - [Test] - public void PassingAndReturningALongShouldReturnAWholeNumber() + Dictionary map = (Dictionary)result; + Assert.That(map, Has.Count.EqualTo(3)); + foreach (string expectedKey in expectedResult.Keys) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - long expectedResult = 1L; - object result = ExecuteScript("return arguments[0];", expectedResult); - Assert.That(result, Is.InstanceOf().Or.InstanceOf()); - Assert.That(result, Is.EqualTo((long)expectedResult)); + Assert.That(map, Does.ContainKey(expectedKey)); } - [Test] - public void PassingAndReturningADoubleShouldReturnADecimal() - { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - double expectedResult = 1.2; - object result = ExecuteScript("return arguments[0];", expectedResult); - Assert.That(result, Is.InstanceOf().Or.InstanceOf()); - Assert.That(result, Is.EqualTo((double)expectedResult)); - } + Assert.That(map["foo"], Is.EqualTo("bar")); + Assert.That((ReadOnlyCollection)map["baz"], Is.EqualTo((ReadOnlyCollection)expectedResult["baz"])); - [Test] - public void ShouldThrowAnExceptionWhenTheJavascriptIsBad() - { - if (!(driver is IJavaScriptExecutor)) - return; + Dictionary person = (Dictionary)map["person"]; + Assert.That(person, Has.Count.EqualTo(2)); + Assert.That(person["first"], Is.EqualTo("John")); + Assert.That(person["last"], Is.EqualTo("Doe")); + } - driver.Url = xhtmlTestPage; - Assert.That(() => ExecuteScript("return squiggle();"), Throws.InstanceOf()); - } + [Test] + public void ShouldBeAbleToExecuteSimpleJavascriptAndReturnAComplexObject() + { + driver.Url = javascriptPage; - [Test] - [IgnoreBrowser(Browser.Chrome, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Edge, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Firefox, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.IE, ".NET language bindings do not properly parse JavaScript stack trace")] - [IgnoreBrowser(Browser.Safari, ".NET language bindings do not properly parse JavaScript stack trace")] - public void ShouldThrowAnExceptionWithMessageAndStacktraceWhenTheJavascriptIsBad() + object result = ExecuteScript("return window.location;"); + + Assert.That(result, Is.InstanceOf>()); + Dictionary map = (Dictionary)result; + Assert.That(map["protocol"], Is.EqualTo("http:")); + Assert.That(map["href"], Is.EqualTo(javascriptPage)); + } + + [Test] + public void PassingAndReturningALongShouldReturnAWholeNumber() + { + if (!(driver is IJavaScriptExecutor)) { - driver.Url = xhtmlTestPage; - string js = "function functionB() { throw Error('errormessage'); };" - + "function functionA() { functionB(); };" - + "functionA();"; - - Assert.That( - () => ExecuteScript(js), - Throws.InstanceOf() - .With.Message.Contains("errormessage") - .And.Property(nameof(WebDriverException.StackTrace)).Contains("functionB")); + return; } - [Test] - public void ShouldBeAbleToCallFunctionsDefinedOnThePage() + driver.Url = javascriptPage; + long expectedResult = 1L; + object result = ExecuteScript("return arguments[0];", expectedResult); + Assert.That(result, Is.InstanceOf().Or.InstanceOf()); + Assert.That(result, Is.EqualTo((long)expectedResult)); + } + + [Test] + public void PassingAndReturningADoubleShouldReturnADecimal() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - return; + return; + } - driver.Url = javascriptPage; - ExecuteScript("displayMessage('I like cheese');"); - string text = driver.FindElement(By.Id("result")).Text; + driver.Url = javascriptPage; + double expectedResult = 1.2; + object result = ExecuteScript("return arguments[0];", expectedResult); + Assert.That(result, Is.InstanceOf().Or.InstanceOf()); + Assert.That(result, Is.EqualTo((double)expectedResult)); + } - Assert.That(text.Trim(), Is.EqualTo("I like cheese")); - } + [Test] + public void ShouldThrowAnExceptionWhenTheJavascriptIsBad() + { + if (!(driver is IJavaScriptExecutor)) + return; - [Test] - public void ShouldBeAbleToPassAStringAsAnArgument() - { - if (!(driver is IJavaScriptExecutor)) - return; + driver.Url = xhtmlTestPage; + Assert.That(() => ExecuteScript("return squiggle();"), Throws.InstanceOf()); + } - driver.Url = javascriptPage; + [Test] + [IgnoreBrowser(Browser.Chrome, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Edge, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Firefox, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.IE, ".NET language bindings do not properly parse JavaScript stack trace")] + [IgnoreBrowser(Browser.Safari, ".NET language bindings do not properly parse JavaScript stack trace")] + public void ShouldThrowAnExceptionWithMessageAndStacktraceWhenTheJavascriptIsBad() + { + driver.Url = xhtmlTestPage; + string js = "function functionB() { throw Error('errormessage'); };" + + "function functionA() { functionB(); };" + + "functionA();"; + + Assert.That( + () => ExecuteScript(js), + Throws.InstanceOf() + .With.Message.Contains("errormessage") + .And.Property(nameof(WebDriverException.StackTrace)).Contains("functionB")); + } - string text = (string)ExecuteScript("return arguments[0] == 'Hello!' ? 'Hello!' : 'Goodbye!';", "Hello!"); - Assert.That(text, Is.EqualTo("Hello!")); - } + [Test] + public void ShouldBeAbleToCallFunctionsDefinedOnThePage() + { + if (!(driver is IJavaScriptExecutor)) + return; - [Test] - public void ShouldBeAbleToPassABooleanAsAnArgument() - { + driver.Url = javascriptPage; + ExecuteScript("displayMessage('I like cheese');"); + string text = driver.FindElement(By.Id("result")).Text; - string function = "return arguments[0] == true ? true : false;"; + Assert.That(text.Trim(), Is.EqualTo("I like cheese")); + } - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToPassAStringAsAnArgument() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - bool result = (bool)ExecuteScript(function, true); - Assert.That(result, Is.True); + string text = (string)ExecuteScript("return arguments[0] == 'Hello!' ? 'Hello!' : 'Goodbye!';", "Hello!"); + Assert.That(text, Is.EqualTo("Hello!")); + } - result = (bool)ExecuteScript(function, false); - Assert.That(result, Is.False); - } + [Test] + public void ShouldBeAbleToPassABooleanAsAnArgument() + { - [Test] - public void ShouldBeAbleToPassANumberAsAnArgument() - { - string functionTemplate = "return arguments[0] == {0} ? {0} : 0;"; + string function = "return arguments[0] == true ? true : false;"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - string function = string.Format(functionTemplate, 3); - long result = (long)ExecuteScript(function, 3); - Assert.That(result, Is.EqualTo(3)); + bool result = (bool)ExecuteScript(function, true); + Assert.That(result, Is.True); - function = string.Format(functionTemplate, -3); - result = (long)ExecuteScript(function, -3); - Assert.That(result, Is.EqualTo(-3)); + result = (bool)ExecuteScript(function, false); + Assert.That(result, Is.False); + } - function = string.Format(functionTemplate, 2147483647); - result = (long)ExecuteScript(function, 2147483647); - Assert.That(result, Is.EqualTo(2147483647)); + [Test] + public void ShouldBeAbleToPassANumberAsAnArgument() + { + string functionTemplate = "return arguments[0] == {0} ? {0} : 0;"; - function = string.Format(functionTemplate, -2147483647); - result = (long)ExecuteScript(function, -2147483647); - Assert.That(result, Is.EqualTo(-2147483647)); - } + if (!(driver is IJavaScriptExecutor)) + return; - [Test] + driver.Url = javascriptPage; - public void ShouldBeAbleToPassAWebElementAsArgument() - { - if (!(driver is IJavaScriptExecutor)) - return; + string function = string.Format(functionTemplate, 3); + long result = (long)ExecuteScript(function, 3); + Assert.That(result, Is.EqualTo(3)); - driver.Url = javascriptPage; - IWebElement button = driver.FindElement(By.Id("plainButton")); - string value = (string)ExecuteScript("arguments[0]['flibble'] = arguments[0].getAttribute('id'); return arguments[0]['flibble'];", button); + function = string.Format(functionTemplate, -3); + result = (long)ExecuteScript(function, -3); + Assert.That(result, Is.EqualTo(-3)); - Assert.That(value, Is.EqualTo("plainButton")); - } + function = string.Format(functionTemplate, 2147483647); + result = (long)ExecuteScript(function, 2147483647); + Assert.That(result, Is.EqualTo(2147483647)); + + function = string.Format(functionTemplate, -2147483647); + result = (long)ExecuteScript(function, -2147483647); + Assert.That(result, Is.EqualTo(-2147483647)); + } + + [Test] + + public void ShouldBeAbleToPassAWebElementAsArgument() + { + if (!(driver is IJavaScriptExecutor)) + return; + + driver.Url = javascriptPage; + IWebElement button = driver.FindElement(By.Id("plainButton")); + string value = (string)ExecuteScript("arguments[0]['flibble'] = arguments[0].getAttribute('id'); return arguments[0]['flibble'];", button); + + Assert.That(value, Is.EqualTo("plainButton")); + } - [Test] - public void PassingArrayAsOnlyArgumentShouldFlattenArray() + [Test] + public void PassingArrayAsOnlyArgumentShouldFlattenArray() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - object[] array = new object[] { "zero", 1, true, 3.14159 }; - long length = (long)ExecuteScript("return arguments[0].length", array); - Assert.That(length, Is.EqualTo(array.Length)); + return; } - [Test] - public void ShouldBeAbleToPassAnArrayAsAdditionalArgument() + driver.Url = javascriptPage; + object[] array = new object[] { "zero", 1, true, 3.14159 }; + long length = (long)ExecuteScript("return arguments[0].length", array); + Assert.That(length, Is.EqualTo(array.Length)); + } + + [Test] + public void ShouldBeAbleToPassAnArrayAsAdditionalArgument() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - object[] array = new object[] { "zero", 1, true, 3.14159, false }; - long length = (long)ExecuteScript("return arguments[1].length", "string", array); - Assert.That(length, Is.EqualTo(array.Length)); + return; } - [Test] - public void ShouldBeAbleToPassACollectionAsArgument() + driver.Url = javascriptPage; + object[] array = new object[] { "zero", 1, true, 3.14159, false }; + long length = (long)ExecuteScript("return arguments[1].length", "string", array); + Assert.That(length, Is.EqualTo(array.Length)); + } + + [Test] + public void ShouldBeAbleToPassACollectionAsArgument() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } - - driver.Url = javascriptPage; - List collection = new List(); - collection.Add("Cheddar"); - collection.Add("Brie"); - collection.Add(7); - long length = (long)ExecuteScript("return arguments[0].length", collection); - Assert.That(length, Is.EqualTo(collection.Count)); + return; } - [Test] - public void ShouldThrowAnExceptionIfAnArgumentIsNotValid() - { - if (!(driver is IJavaScriptExecutor)) - return; + driver.Url = javascriptPage; + List collection = new List(); + collection.Add("Cheddar"); + collection.Add("Brie"); + collection.Add(7); + long length = (long)ExecuteScript("return arguments[0].length", collection); + Assert.That(length, Is.EqualTo(collection.Count)); + } + + [Test] + public void ShouldThrowAnExceptionIfAnArgumentIsNotValid() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - Assert.That( - () => ExecuteScript("return arguments[0];", driver), - Throws.ArgumentException.With.Message.StartsWith("Argument is of an illegal type: ")); - } + Assert.That( + () => ExecuteScript("return arguments[0];", driver), + Throws.ArgumentException.With.Message.StartsWith("Argument is of an illegal type: ")); + } - [Test] - public void ShouldBeAbleToPassInMoreThanOneArgument() + [Test] + public void ShouldBeAbleToPassInMoreThanOneArgument() + { + if (!(driver is IJavaScriptExecutor)) { - if (!(driver is IJavaScriptExecutor)) - { - return; - } + return; + } - driver.Url = javascriptPage; - string result = (string)ExecuteScript("return arguments[0] + arguments[1];", "one", "two"); + driver.Url = javascriptPage; + string result = (string)ExecuteScript("return arguments[0] + arguments[1];", "one", "two"); - Assert.That(result, Is.EqualTo("onetwo")); - } + Assert.That(result, Is.EqualTo("onetwo")); + } - [Test] - public void ShouldBeAbleToGrabTheBodyOfFrameOnceSwitchedTo() - { - driver.Url = richTextPage; + [Test] + public void ShouldBeAbleToGrabTheBodyOfFrameOnceSwitchedTo() + { + driver.Url = richTextPage; - driver.SwitchTo().Frame("editFrame"); - IWebElement body = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.body"); + driver.SwitchTo().Frame("editFrame"); + IWebElement body = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.body"); - Assert.That(body.Text, Is.Empty); - } + Assert.That(body.Text, Is.Empty); + } - // This is a duplicate test of ShouldBeAbleToExecuteScriptAndReturnElementsList. - // It's here and commented only to make comparison with the Java language bindings - // tests easier. - //[Test] - //public void testShouldBeAbleToReturnAnArrayOfWebElements() - //{ - // driver.Url = formsPage; + // This is a duplicate test of ShouldBeAbleToExecuteScriptAndReturnElementsList. + // It's here and commented only to make comparison with the Java language bindings + // tests easier. + //[Test] + //public void testShouldBeAbleToReturnAnArrayOfWebElements() + //{ + // driver.Url = formsPage; - // ReadOnlyCollection items = (ReadOnlyCollection)((IJavaScriptExecutor)driver) - // .ExecuteScript("return document.getElementsByName('snack');"); + // ReadOnlyCollection items = (ReadOnlyCollection)((IJavaScriptExecutor)driver) + // .ExecuteScript("return document.getElementsByName('snack');"); - // Assert.That(items.Count, Is.Not.EqualTo(0)); - //} + // Assert.That(items.Count, Is.Not.EqualTo(0)); + //} - [Test] - public void JavascriptStringHandlingShouldWorkAsExpected() - { - driver.Url = javascriptPage; + [Test] + public void JavascriptStringHandlingShouldWorkAsExpected() + { + driver.Url = javascriptPage; - string value = (string)ExecuteScript("return '';"); - Assert.That(value, Is.Empty); + string value = (string)ExecuteScript("return '';"); + Assert.That(value, Is.Empty); - value = (string)ExecuteScript("return undefined;"); - Assert.That(value, Is.Null); + value = (string)ExecuteScript("return undefined;"); + Assert.That(value, Is.Null); - value = (string)ExecuteScript("return ' '"); - Assert.That(value, Is.EqualTo(" ")); - } + value = (string)ExecuteScript("return ' '"); + Assert.That(value, Is.EqualTo(" ")); + } - [Test] - public void ShouldBeAbleToExecuteABigChunkOfJavascriptCode() + [Test] + public void ShouldBeAbleToExecuteABigChunkOfJavascriptCode() + { + driver.Url = javascriptPage; + string path = System.IO.Path.Combine(Environment.EnvironmentManager.Instance.CurrentDirectory, ".." + System.IO.Path.DirectorySeparatorChar + ".."); + string[] fileList = System.IO.Directory.GetFiles(path, "jquery-1.2.6.min.js", System.IO.SearchOption.AllDirectories); + if (fileList.Length > 0) { - driver.Url = javascriptPage; - string path = System.IO.Path.Combine(Environment.EnvironmentManager.Instance.CurrentDirectory, ".." + System.IO.Path.DirectorySeparatorChar + ".."); - string[] fileList = System.IO.Directory.GetFiles(path, "jquery-1.2.6.min.js", System.IO.SearchOption.AllDirectories); - if (fileList.Length > 0) - { - string jquery = System.IO.File.ReadAllText(fileList[0]); - Assert.That(jquery, Has.Length.GreaterThan(50000)); - ExecuteScript(jquery, null); - } + string jquery = System.IO.File.ReadAllText(fileList[0]); + Assert.That(jquery, Has.Length.GreaterThan(50000)); + ExecuteScript(jquery, null); } + } - [Test] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task ShouldBeAbleToPinJavascriptCodeAndExecuteRepeatedly() - { - using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); + [Test] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task ShouldBeAbleToPinJavascriptCodeAndExecuteRepeatedly() + { + using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); - driver.Url = xhtmlTestPage; + driver.Url = xhtmlTestPage; - PinnedScript script = await jsEngine.PinScript("return document.title;"); - for (int i = 0; i < 5; i++) - { - object result = ((IJavaScriptExecutor)driver).ExecuteScript(script); + PinnedScript script = await jsEngine.PinScript("return document.title;"); + for (int i = 0; i < 5; i++) + { + object result = ((IJavaScriptExecutor)driver).ExecuteScript(script); - Assert.That(result, Is.InstanceOf()); - Assert.That(result, Is.EqualTo("XHTML Test Page")); - } + Assert.That(result, Is.InstanceOf()); + Assert.That(result, Is.EqualTo("XHTML Test Page")); + } - await jsEngine.UnpinScript(script); + await jsEngine.UnpinScript(script); - Assert.That( - () => ((IJavaScriptExecutor)driver).ExecuteScript(script), - Throws.TypeOf()); - } + Assert.That( + () => ((IJavaScriptExecutor)driver).ExecuteScript(script), + Throws.TypeOf()); + } - [Test] - [NeedsFreshDriver(IsCreatedAfterTest = true)] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task ShouldBeAbleToAddInitializationScriptAndExecuteOnNewDocument() - { - const string ScriptValue = "alert('notice')"; - const string ScriptName = "AlertScript"; + [Test] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task ShouldBeAbleToAddInitializationScriptAndExecuteOnNewDocument() + { + const string ScriptValue = "alert('notice')"; + const string ScriptName = "AlertScript"; - using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); + using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); - var initScript = await jsEngine.AddInitializationScript(ScriptName, ScriptValue); + var initScript = await jsEngine.AddInitializationScript(ScriptName, ScriptValue); - Assert.That(initScript, Is.Not.Null); - Assert.That(initScript.ScriptSource, Is.EqualTo(ScriptValue)); - Assert.That(initScript.ScriptName, Is.EqualTo(ScriptName)); - Assert.That(initScript.ScriptId, Is.Not.Null); + Assert.That(initScript, Is.Not.Null); + Assert.That(initScript.ScriptSource, Is.EqualTo(ScriptValue)); + Assert.That(initScript.ScriptName, Is.EqualTo(ScriptName)); + Assert.That(initScript.ScriptId, Is.Not.Null); - await jsEngine.StartEventMonitoring(); + await jsEngine.StartEventMonitoring(); - driver.Navigate().Refresh(); - driver.SwitchTo().Alert().Accept(); + driver.Navigate().Refresh(); + driver.SwitchTo().Alert().Accept(); - Assert.That(jsEngine.InitializationScripts, Does.Contain(initScript)); - await jsEngine.RemoveInitializationScript(ScriptName); + Assert.That(jsEngine.InitializationScripts, Does.Contain(initScript)); + await jsEngine.RemoveInitializationScript(ScriptName); - driver.Navigate().Refresh(); - Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); + driver.Navigate().Refresh(); + Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); - Assert.That(jsEngine.InitializationScripts, Does.Not.Contain(initScript)); + Assert.That(jsEngine.InitializationScripts, Does.Not.Contain(initScript)); - await jsEngine.AddInitializationScript(ScriptName, ScriptValue); + await jsEngine.AddInitializationScript(ScriptName, ScriptValue); - driver.Navigate().Refresh(); - driver.SwitchTo().Alert().Accept(); - Assert.That(jsEngine.InitializationScripts, Does.Contain(initScript)); + driver.Navigate().Refresh(); + driver.SwitchTo().Alert().Accept(); + Assert.That(jsEngine.InitializationScripts, Does.Contain(initScript)); - await jsEngine.ClearInitializationScripts(); + await jsEngine.ClearInitializationScripts(); - driver.Navigate().Refresh(); - Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); - Assert.That(jsEngine.InitializationScripts, Is.Empty); + driver.Navigate().Refresh(); + Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); + Assert.That(jsEngine.InitializationScripts, Is.Empty); - await jsEngine.AddInitializationScript(ScriptName, ScriptValue); - driver.Navigate().Refresh(); - driver.SwitchTo().Alert().Accept(); + await jsEngine.AddInitializationScript(ScriptName, ScriptValue); + driver.Navigate().Refresh(); + driver.SwitchTo().Alert().Accept(); - await jsEngine.ClearAll(); - driver.Navigate().Refresh(); - Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); - Assert.That(jsEngine.InitializationScripts, Is.Empty); - } + await jsEngine.ClearAll(); + driver.Navigate().Refresh(); + Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); + Assert.That(jsEngine.InitializationScripts, Is.Empty); + } - [Test] - [NeedsFreshDriver(IsCreatedAfterTest = true)] - [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] - [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] - public async Task ShouldBeAbleToAddAndRemoveScriptCallbackBinding() - { - const string ScriptValue = "alert('Hello world')"; - const string ScriptName = "alert"; + [Test] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] + public async Task ShouldBeAbleToAddAndRemoveScriptCallbackBinding() + { + const string ScriptValue = "alert('Hello world')"; + const string ScriptName = "alert"; - using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); + using IJavaScriptEngine jsEngine = new JavaScriptEngine(driver); - var executedBindings = new List(); - jsEngine.JavaScriptCallbackExecuted += AddToList; - await jsEngine.AddInitializationScript(ScriptName, ScriptValue); - await jsEngine.StartEventMonitoring(); + var executedBindings = new List(); + jsEngine.JavaScriptCallbackExecuted += AddToList; + await jsEngine.AddInitializationScript(ScriptName, ScriptValue); + await jsEngine.StartEventMonitoring(); - driver.Navigate().Refresh(); - driver.SwitchTo().Alert().Accept(); + driver.Navigate().Refresh(); + driver.SwitchTo().Alert().Accept(); - await jsEngine.AddScriptCallbackBinding(ScriptName); + await jsEngine.AddScriptCallbackBinding(ScriptName); - driver.Navigate().Refresh(); - Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); + driver.Navigate().Refresh(); + Assert.That(() => driver.SwitchTo().Alert().Accept(), Throws.TypeOf()); - Assert.That(executedBindings, Does.Contain(ScriptName)); - int oldCount = executedBindings.Count; - driver.Navigate().Refresh(); + Assert.That(executedBindings, Does.Contain(ScriptName)); + int oldCount = executedBindings.Count; + driver.Navigate().Refresh(); - Assert.That(executedBindings, Has.Count.GreaterThan(oldCount)); - Assert.That(jsEngine.ScriptCallbackBindings, Does.Contain(ScriptName)); - oldCount = executedBindings.Count; + Assert.That(executedBindings, Has.Count.GreaterThan(oldCount)); + Assert.That(jsEngine.ScriptCallbackBindings, Does.Contain(ScriptName)); + oldCount = executedBindings.Count; - await jsEngine.RemoveScriptCallbackBinding(ScriptName); - Assert.That(jsEngine.ScriptCallbackBindings, Is.Empty); - await jsEngine.AddScriptCallbackBinding(ScriptName); - Assert.That(jsEngine.ScriptCallbackBindings, Does.Contain(ScriptName)); - await jsEngine.ClearScriptCallbackBindings(); - Assert.That(jsEngine.ScriptCallbackBindings, Is.Empty); + await jsEngine.RemoveScriptCallbackBinding(ScriptName); + Assert.That(jsEngine.ScriptCallbackBindings, Is.Empty); + await jsEngine.AddScriptCallbackBinding(ScriptName); + Assert.That(jsEngine.ScriptCallbackBindings, Does.Contain(ScriptName)); + await jsEngine.ClearScriptCallbackBindings(); + Assert.That(jsEngine.ScriptCallbackBindings, Is.Empty); - jsEngine.JavaScriptCallbackExecuted -= AddToList; - driver.Navigate().Refresh(); - Assert.That(executedBindings, Has.Count.EqualTo(oldCount)); + jsEngine.JavaScriptCallbackExecuted -= AddToList; + driver.Navigate().Refresh(); + Assert.That(executedBindings, Has.Count.EqualTo(oldCount)); - void AddToList(object sender, JavaScriptCallbackExecutedEventArgs e) => executedBindings.Add(e.BindingName); - } + void AddToList(object sender, JavaScriptCallbackExecutedEventArgs e) => executedBindings.Add(e.BindingName); + } - [Test] - public void ShouldBeAbleToExecuteScriptAndReturnElementsList() - { - driver.Url = formsPage; - String scriptToExec = "return document.getElementsByName('snack');"; + [Test] + public void ShouldBeAbleToExecuteScriptAndReturnElementsList() + { + driver.Url = formsPage; + String scriptToExec = "return document.getElementsByName('snack');"; - object resultObject = ((IJavaScriptExecutor)driver).ExecuteScript(scriptToExec); + object resultObject = ((IJavaScriptExecutor)driver).ExecuteScript(scriptToExec); - ReadOnlyCollection resultsList = (ReadOnlyCollection)resultObject; + ReadOnlyCollection resultsList = (ReadOnlyCollection)resultObject; - Assert.That(resultsList, Is.Not.Empty); - } + Assert.That(resultsList, Is.Not.Empty); + } - [Test] - public void ShouldBeAbleToCreateAPersistentValue() - { - driver.Url = formsPage; + [Test] + public void ShouldBeAbleToCreateAPersistentValue() + { + driver.Url = formsPage; - ExecuteScript("document.alerts = []"); - ExecuteScript("document.alerts.push('hello world');"); - string text = (string)ExecuteScript("return document.alerts.shift()"); + ExecuteScript("document.alerts = []"); + ExecuteScript("document.alerts.push('hello world');"); + string text = (string)ExecuteScript("return document.alerts.shift()"); - Assert.That(text, Is.EqualTo("hello world")); - } + Assert.That(text, Is.EqualTo("hello world")); + } - [Test] - public void ShouldBeAbleToHandleAnArrayOfElementsAsAnObjectArray() - { - driver.Url = formsPage; + [Test] + public void ShouldBeAbleToHandleAnArrayOfElementsAsAnObjectArray() + { + driver.Url = formsPage; - ReadOnlyCollection forms = driver.FindElements(By.TagName("form")); - object[] args = new object[] { forms }; + ReadOnlyCollection forms = driver.FindElements(By.TagName("form")); + object[] args = new object[] { forms }; - string name = (string)((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0][0].tagName", args); + string name = (string)((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0][0].tagName", args); - Assert.That(name, Is.EqualTo("form").IgnoreCase); - } + Assert.That(name, Is.EqualTo("form").IgnoreCase); + } - [Test] - public void ShouldBeAbleToPassADictionaryAsAParameter() - { - driver.Url = simpleTestPage; + [Test] + public void ShouldBeAbleToPassADictionaryAsAParameter() + { + driver.Url = simpleTestPage; - List nums = new List() { 1, 2 }; - Dictionary args = new Dictionary(); - args["bar"] = "test"; - args["foo"] = nums; + List nums = new List() { 1, 2 }; + Dictionary args = new Dictionary(); + args["bar"] = "test"; + args["foo"] = nums; - object res = ((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0]['foo'][1]", args); + object res = ((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0]['foo'][1]", args); - Assert.That((long)res, Is.EqualTo(2)); - } + Assert.That((long)res, Is.EqualTo(2)); + } - [Test] - public void ShouldThrowAnExceptionWhenArgumentsWithStaleElementPassed() + [Test] + public void ShouldThrowAnExceptionWhenArgumentsWithStaleElementPassed() + { + IJavaScriptExecutor executor = driver as IJavaScriptExecutor; + if (executor == null) { - IJavaScriptExecutor executor = driver as IJavaScriptExecutor; - if (executor == null) - { - return; - } + return; + } - driver.Url = simpleTestPage; + driver.Url = simpleTestPage; - IWebElement el = driver.FindElement(By.Id("oneline")); + IWebElement el = driver.FindElement(By.Id("oneline")); - driver.Url = simpleTestPage; + driver.Url = simpleTestPage; - Dictionary args = new Dictionary(); - args["key"] = new object[] { "a", new object[] { "zero", 1, true, 3.14159, false, el }, "c" }; - Assert.That( - () => executor.ExecuteScript("return undefined;", args), - Throws.TypeOf()); - } + Dictionary args = new Dictionary(); + args["key"] = new object[] { "a", new object[] { "zero", 1, true, 3.14159, false, el }, "c" }; + Assert.That( + () => executor.ExecuteScript("return undefined;", args), + Throws.TypeOf()); + } - [Test] - [IgnoreBrowser(Browser.Chrome, "Browser does not return Date object.")] - [IgnoreBrowser(Browser.Edge, "Browser does not return Date object.")] - public void ShouldBeAbleToReturnADateObject() - { - driver.Url = simpleTestPage; + [Test] + [IgnoreBrowser(Browser.Chrome, "Browser does not return Date object.")] + [IgnoreBrowser(Browser.Edge, "Browser does not return Date object.")] + public void ShouldBeAbleToReturnADateObject() + { + driver.Url = simpleTestPage; - string date = (string)ExecuteScript("return new Date();"); - DateTime.Parse(date); - } + string date = (string)ExecuteScript("return new Date();"); + DateTime.Parse(date); + } - [Test] - [IgnoreBrowser(Browser.Chrome, "Driver returns object that allows getting text.")] - [IgnoreBrowser(Browser.Edge, "Driver returns object that allows getting text.")] - [IgnoreBrowser(Browser.Firefox, "Driver does not return the documentElement object.")] - [IgnoreBrowser(Browser.IE, "Driver does not return the documentElement object.")] - [IgnoreBrowser(Browser.Safari, "Driver does not return the documentElement object.")] - public void ShouldReturnDocumentElementIfDocumentIsReturned() - { - driver.Url = simpleTestPage; + [Test] + [IgnoreBrowser(Browser.Chrome, "Driver returns object that allows getting text.")] + [IgnoreBrowser(Browser.Edge, "Driver returns object that allows getting text.")] + [IgnoreBrowser(Browser.Firefox, "Driver does not return the documentElement object.")] + [IgnoreBrowser(Browser.IE, "Driver does not return the documentElement object.")] + [IgnoreBrowser(Browser.Safari, "Driver does not return the documentElement object.")] + public void ShouldReturnDocumentElementIfDocumentIsReturned() + { + driver.Url = simpleTestPage; - object value = ExecuteScript("return document"); + object value = ExecuteScript("return document"); - Assert.That(value, Is.InstanceOf()); - Assert.That(((IWebElement)value).Text, Does.Contain("A single line of text")); - } + Assert.That(value, Is.InstanceOf()); + Assert.That(((IWebElement)value).Text, Does.Contain("A single line of text")); + } - [Test] - public void ShouldHandleObjectThatThatHaveToJSONMethod() - { - driver.Url = simpleTestPage; + [Test] + public void ShouldHandleObjectThatThatHaveToJSONMethod() + { + driver.Url = simpleTestPage; - object value = ExecuteScript("return window.performance.timing"); + object value = ExecuteScript("return window.performance.timing"); - Assert.That(value, Is.InstanceOf>()); - } + Assert.That(value, Is.InstanceOf>()); + } - [Test] - public void ShouldHandleRecursiveStructures() - { - driver.Url = simpleTestPage; - - Assert.That( - () => ExecuteScript( - """ - var obj1 = {}; - var obj2 = {}; - obj1['obj2'] = obj2; - obj2['obj1'] = obj1; - return obj1 - """ - ), - Throws.TypeOf()); - } + [Test] + public void ShouldHandleRecursiveStructures() + { + driver.Url = simpleTestPage; + + Assert.That( + () => ExecuteScript( + """ + var obj1 = {}; + var obj2 = {}; + obj1['obj2'] = obj2; + obj2['obj1'] = obj1; + return obj1 + """ + ), + Throws.TypeOf()); + } - //------------------------------------------------------------------ - // Tests below here are not included in the Java test suite - //------------------------------------------------------------------ - [Test] - [NeedsFreshDriver(IsCreatedBeforeTest = true, IsCreatedAfterTest = true)] - [Ignore("Reason for ignore: Failure indicates hang condition, which would break the test suite. Really needs a timeout set.")] - public void ShouldThrowExceptionIfExecutingOnNoPage() - { - Assert.That( - () => ((IJavaScriptExecutor)driver).ExecuteScript("return 1;"), - Throws.InstanceOf()); - } + //------------------------------------------------------------------ + // Tests below here are not included in the Java test suite + //------------------------------------------------------------------ + [Test] + [NeedsFreshDriver(IsCreatedBeforeTest = true, IsCreatedAfterTest = true)] + [Ignore("Reason for ignore: Failure indicates hang condition, which would break the test suite. Really needs a timeout set.")] + public void ShouldThrowExceptionIfExecutingOnNoPage() + { + Assert.That( + () => ((IJavaScriptExecutor)driver).ExecuteScript("return 1;"), + Throws.InstanceOf()); + } - [Test] - public void ExecutingLargeJavaScript() - { - string script = """ - // stolen from injectableSelenium.js in WebDriver - var browserbot = { - - triggerEvent: function(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { - canBubble = (typeof(canBubble) == undefined) ? true : canBubble; - - if (element.fireEvent && element.ownerDocument && element.ownerDocument.createEventObject) { - // IE - var evt = this.createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); - element.fireEvent('on' + eventType,evt); - } else { - var evt = document.createEvent('HTMLEvents'); - - try { - evt.shiftKey = shiftKeyDown; - evt.metaKey = metaKeyDown; - evt.altKey = altKeyDown; - evt.ctrlKey = controlKeyDown; - } catch(e) { - // Nothing sane to do - } - - evt.initEvent(eventType, canBubble, true); - return element.dispatchEvent(evt); - } - }, - - getVisibleText: function() { - var selection = getSelection(); - var range = document.createRange(); - range.selectNodeContents(document.documentElement); - selection.addRange(range); - - var string = selection.toString(); - selection.removeAllRanges(); - - return string; - }, - - getOuterHTML: function(element) { - if(element.outerHTML) { - return element.outerHTML; - } else if(typeof(XMLSerializer) != undefined) { - return new XMLSerializer().serializeToString(element); - } else { - throw "can't get outerHTML in this browser"; + [Test] + public void ExecutingLargeJavaScript() + { + string script = """ + // stolen from injectableSelenium.js in WebDriver + var browserbot = { + + triggerEvent: function(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { + canBubble = (typeof(canBubble) == undefined) ? true : canBubble; + + if (element.fireEvent && element.ownerDocument && element.ownerDocument.createEventObject) { + // IE + var evt = this.createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); + element.fireEvent('on' + eventType,evt); + } else { + var evt = document.createEvent('HTMLEvents'); + + try { + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + } catch(e) { + // Nothing sane to do } + + evt.initEvent(eventType, canBubble, true); + return element.dispatchEvent(evt); + } + }, + + getVisibleText: function() { + var selection = getSelection(); + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + selection.addRange(range); + + var string = selection.toString(); + selection.removeAllRanges(); + + return string; + }, + + getOuterHTML: function(element) { + if(element.outerHTML) { + return element.outerHTML; + } else if(typeof(XMLSerializer) != undefined) { + return new XMLSerializer().serializeToString(element); + } else { + throw "can't get outerHTML in this browser"; } - }; + } + }; - return browserbot.getOuterHTML.apply(browserbot, arguments); - """; + return browserbot.getOuterHTML.apply(browserbot, arguments); + """; - driver.Url = javascriptPage; - IWebElement element = driver.FindElement(By.TagName("body")); - object x = ExecuteScript(script, element); - } + driver.Url = javascriptPage; + IWebElement element = driver.FindElement(By.TagName("body")); + object x = ExecuteScript(script, element); + } - [Test] + [Test] - public void ShouldBeAbleToPassMoreThanOneStringAsArguments() - { - if (!(driver is IJavaScriptExecutor)) - return; + public void ShouldBeAbleToPassMoreThanOneStringAsArguments() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; - string text = (string)ExecuteScript("return arguments[0] + arguments[1] + arguments[2] + arguments[3];", "Hello,", " ", "world", "!"); + driver.Url = javascriptPage; + string text = (string)ExecuteScript("return arguments[0] + arguments[1] + arguments[2] + arguments[3];", "Hello,", " ", "world", "!"); - Assert.That(text, Is.EqualTo("Hello, world!")); - } + Assert.That(text, Is.EqualTo("Hello, world!")); + } - [Test] - public void ShouldBeAbleToPassMoreThanOneBooleanAsArguments() - { + [Test] + public void ShouldBeAbleToPassMoreThanOneBooleanAsArguments() + { - string function = "return (arguments[0] ? 'True' : 'False') + (arguments[1] ? 'True' : 'False');"; + string function = "return (arguments[0] ? 'True' : 'False') + (arguments[1] ? 'True' : 'False');"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - string text = (string)ExecuteScript(function, true, true); - Assert.That(text, Is.EqualTo("TrueTrue")); + string text = (string)ExecuteScript(function, true, true); + Assert.That(text, Is.EqualTo("TrueTrue")); - text = (string)ExecuteScript(function, false, true); - Assert.That(text, Is.EqualTo("FalseTrue")); + text = (string)ExecuteScript(function, false, true); + Assert.That(text, Is.EqualTo("FalseTrue")); - text = (string)ExecuteScript(function, true, false); - Assert.That(text, Is.EqualTo("TrueFalse")); + text = (string)ExecuteScript(function, true, false); + Assert.That(text, Is.EqualTo("TrueFalse")); - text = (string)ExecuteScript(function, false, false); - Assert.That(text, Is.EqualTo("FalseFalse")); - } + text = (string)ExecuteScript(function, false, false); + Assert.That(text, Is.EqualTo("FalseFalse")); + } - [Test] - public void ShouldBeAbleToPassMoreThanOneNumberAsArguments() - { - string function = "return arguments[0]+arguments[1];"; + [Test] + public void ShouldBeAbleToPassMoreThanOneNumberAsArguments() + { + string function = "return arguments[0]+arguments[1];"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - long result = (long)ExecuteScript(function, 30, 12); - Assert.That(result, Is.EqualTo(42)); + long result = (long)ExecuteScript(function, 30, 12); + Assert.That(result, Is.EqualTo(42)); - result = (long)ExecuteScript(function, -30, -12); - Assert.That(result, Is.EqualTo(-42)); + result = (long)ExecuteScript(function, -30, -12); + Assert.That(result, Is.EqualTo(-42)); - result = (long)ExecuteScript(function, 2147483646, 1); - Assert.That(result, Is.EqualTo(2147483647)); + result = (long)ExecuteScript(function, 2147483646, 1); + Assert.That(result, Is.EqualTo(2147483647)); - result = (long)ExecuteScript(function, -2147483646, -1); - Assert.That(result, Is.EqualTo(-2147483647)); + result = (long)ExecuteScript(function, -2147483646, -1); + Assert.That(result, Is.EqualTo(-2147483647)); - } + } - [Test] - public void ShouldBeAbleToPassADoubleAsAnArgument() - { - string function = "return arguments[0];"; + [Test] + public void ShouldBeAbleToPassADoubleAsAnArgument() + { + string function = "return arguments[0];"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - double result = (double)ExecuteScript(function, (double)4.2); - Assert.That(result, Is.EqualTo(4.2)); + double result = (double)ExecuteScript(function, (double)4.2); + Assert.That(result, Is.EqualTo(4.2)); - result = (double)ExecuteScript(function, (double)-4.2); - Assert.That(result, Is.EqualTo(-4.2)); + result = (double)ExecuteScript(function, (double)-4.2); + Assert.That(result, Is.EqualTo(-4.2)); - result = (double)ExecuteScript(function, (float)4.2); - Assert.That(result, Is.EqualTo(4.2)); + result = (double)ExecuteScript(function, (float)4.2); + Assert.That(result, Is.EqualTo(4.2)); - result = (double)ExecuteScript(function, (float)-4.2); - Assert.That(result, Is.EqualTo(-4.2)); + result = (double)ExecuteScript(function, (float)-4.2); + Assert.That(result, Is.EqualTo(-4.2)); - result = (long)ExecuteScript(function, (double)4.0); - Assert.That(result, Is.EqualTo(4)); + result = (long)ExecuteScript(function, (double)4.0); + Assert.That(result, Is.EqualTo(4)); - result = (long)ExecuteScript(function, (double)-4.0); - Assert.That(result, Is.EqualTo(-4)); - } + result = (long)ExecuteScript(function, (double)-4.0); + Assert.That(result, Is.EqualTo(-4)); + } - [Test] - public void ShouldBeAbleToPassMoreThanOneDoubleAsArguments() - { - String function = "return arguments[0]+arguments[1];"; + [Test] + public void ShouldBeAbleToPassMoreThanOneDoubleAsArguments() + { + String function = "return arguments[0]+arguments[1];"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - double result = (double)ExecuteScript(function, 30.1, 12.1); - Assert.That(result, Is.EqualTo(42.2)); + double result = (double)ExecuteScript(function, 30.1, 12.1); + Assert.That(result, Is.EqualTo(42.2)); - result = (double)ExecuteScript(function, -30.1, -12.1); - Assert.That(result, Is.EqualTo(-42.2)); + result = (double)ExecuteScript(function, -30.1, -12.1); + Assert.That(result, Is.EqualTo(-42.2)); - result = (double)ExecuteScript(function, 2147483646.1, 1.0); - Assert.That(result, Is.EqualTo(2147483647.1)); + result = (double)ExecuteScript(function, 2147483646.1, 1.0); + Assert.That(result, Is.EqualTo(2147483647.1)); - result = (double)ExecuteScript(function, -2147483646.1, -1.0); - Assert.That(result, Is.EqualTo(-2147483647.1)); + result = (double)ExecuteScript(function, -2147483646.1, -1.0); + Assert.That(result, Is.EqualTo(-2147483647.1)); - } + } - [Test] - public void ShouldBeAbleToPassMoreThanOneWebElementAsArguments() - { - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToPassMoreThanOneWebElementAsArguments() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; - IWebElement button = driver.FindElement(By.Id("plainButton")); - IWebElement dynamo = driver.FindElement(By.Id("dynamo")); - string value = (string)ExecuteScript("arguments[0]['flibble'] = arguments[0].getAttribute('id'); return arguments[0]['flibble'] + arguments[1].innerHTML;", button, dynamo); + driver.Url = javascriptPage; + IWebElement button = driver.FindElement(By.Id("plainButton")); + IWebElement dynamo = driver.FindElement(By.Id("dynamo")); + string value = (string)ExecuteScript("arguments[0]['flibble'] = arguments[0].getAttribute('id'); return arguments[0]['flibble'] + arguments[1].innerHTML;", button, dynamo); - Assert.That(value, Is.EqualTo("plainButtonWhat's for dinner?")); - } + Assert.That(value, Is.EqualTo("plainButtonWhat's for dinner?")); + } - [Test] - public void ShouldBeAbleToPassInMixedArguments() - { - if (!(driver is IJavaScriptExecutor)) - return; + [Test] + public void ShouldBeAbleToPassInMixedArguments() + { + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - IWebElement dynamo = driver.FindElement(By.Id("dynamo")); - string result = (string)ExecuteScript("return arguments[0].innerHTML + arguments[1].toString() + arguments[2].toString() + arguments[3] + arguments[4]", - dynamo, - 42, - 4.2, - "Hello, World!", - true); + IWebElement dynamo = driver.FindElement(By.Id("dynamo")); + string result = (string)ExecuteScript("return arguments[0].innerHTML + arguments[1].toString() + arguments[2].toString() + arguments[3] + arguments[4]", + dynamo, + 42, + 4.2, + "Hello, World!", + true); - Assert.That(result, Is.EqualTo("What's for dinner?424.2Hello, World!true")); + Assert.That(result, Is.EqualTo("What's for dinner?424.2Hello, World!true")); - } + } - [Test] - public void ShouldBeAbleToPassInAndRetrieveDates() - { - string function = "displayMessage(arguments[0]);"; + [Test] + public void ShouldBeAbleToPassInAndRetrieveDates() + { + string function = "displayMessage(arguments[0]);"; - if (!(driver is IJavaScriptExecutor)) - return; + if (!(driver is IJavaScriptExecutor)) + return; - driver.Url = javascriptPage; + driver.Url = javascriptPage; - ExecuteScript(function, "2014-05-20T20:00:00+08:00"); - IWebElement element = driver.FindElement(By.Id("result")); - string text = element.Text; - Assert.That(text, Is.EqualTo("2014-05-20T20:00:00+08:00")); - } + ExecuteScript(function, "2014-05-20T20:00:00+08:00"); + IWebElement element = driver.FindElement(By.Id("result")); + string text = element.Text; + Assert.That(text, Is.EqualTo("2014-05-20T20:00:00+08:00")); + } - private object ExecuteScript(String script, params Object[] args) - { - object toReturn = ((IJavaScriptExecutor)driver).ExecuteScript(script, args); - return toReturn; - } + private object ExecuteScript(String script, params Object[] args) + { + object toReturn = ((IJavaScriptExecutor)driver).ExecuteScript(script, args); + return toReturn; } } diff --git a/dotnet/test/common/FormHandlingTests.cs b/dotnet/test/common/FormHandlingTests.cs index 47382b7ebf8e1..6b0e9dea34a9e 100644 --- a/dotnet/test/common/FormHandlingTests.cs +++ b/dotnet/test/common/FormHandlingTests.cs @@ -21,418 +21,417 @@ using OpenQA.Selenium.Environment; using System; -namespace OpenQA.Selenium +namespace OpenQA.Selenium; + +[TestFixture] +public class FormHandlingTests : DriverTestFixture { - [TestFixture] - public class FormHandlingTests : DriverTestFixture + [Test] + public void ShouldClickOnSubmitInputElements() { - [Test] - public void ShouldClickOnSubmitInputElements() - { - driver.Url = formsPage; - driver.FindElement(By.Id("submitButton")).Click(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } - - [Test] - public void ClickingOnUnclickableElementsDoesNothing() - { - driver.Url = formsPage; - driver.FindElement(By.XPath("//body")).Click(); - } + driver.Url = formsPage; + driver.FindElement(By.Id("submitButton")).Click(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldBeAbleToClickImageButtons() - { - driver.Url = formsPage; - driver.FindElement(By.Id("imageButton")).Click(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ClickingOnUnclickableElementsDoesNothing() + { + driver.Url = formsPage; + driver.FindElement(By.XPath("//body")).Click(); + } - [Test] - public void ShouldBeAbleToSubmitForms() - { - driver.Url = formsPage; - driver.FindElement(By.Name("login")).Submit(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldBeAbleToClickImageButtons() + { + driver.Url = formsPage; + driver.FindElement(By.Id("imageButton")).Click(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldSubmitAFormWhenAnyInputElementWithinThatFormIsSubmitted() - { - driver.Url = formsPage; - driver.FindElement(By.Id("checky")).Submit(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldBeAbleToSubmitForms() + { + driver.Url = formsPage; + driver.FindElement(By.Name("login")).Submit(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldSubmitAFormWhenAnyElementWithinThatFormIsSubmitted() - { - driver.Url = formsPage; - driver.FindElement(By.XPath("//form/p")).Submit(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldSubmitAFormWhenAnyInputElementWithinThatFormIsSubmitted() + { + driver.Url = formsPage; + driver.FindElement(By.Id("checky")).Submit(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldSubmitAFormWithIdSubmit() - { - driver.Url = formsPage; - driver.FindElement(By.Id("submit")).Submit(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldSubmitAFormWhenAnyElementWithinThatFormIsSubmitted() + { + driver.Url = formsPage; + driver.FindElement(By.XPath("//form/p")).Submit(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - [IgnoreBrowser(Browser.IE, "Does not work")] - public void ShouldSubmitAFormWithNameSubmit() - { - driver.Url = formsPage; - driver.FindElement(By.Name("submit")).Submit(); - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - } + [Test] + public void ShouldSubmitAFormWithIdSubmit() + { + driver.Url = formsPage; + driver.FindElement(By.Id("submit")).Submit(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldNotBeAbleToSubmitAnInputOutsideAForm() - { - driver.Url = formsPage; - Assert.That(() => driver.FindElement(By.Name("SearchableText")).Submit(), Throws.InstanceOf()); - } + [Test] + [IgnoreBrowser(Browser.IE, "Does not work")] + public void ShouldSubmitAFormWithNameSubmit() + { + driver.Url = formsPage; + driver.FindElement(By.Name("submit")).Submit(); + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + } - [Test] - public void ShouldBeAbleToEnterTextIntoATextAreaBySettingItsValue() - { - driver.Url = javascriptPage; - IWebElement textarea = driver.FindElement(By.Id("keyUpArea")); - string cheesey = "Brie and cheddar"; - textarea.SendKeys(cheesey); - Assert.That(textarea.GetAttribute("value"), Is.EqualTo(cheesey)); - } + [Test] + public void ShouldNotBeAbleToSubmitAnInputOutsideAForm() + { + driver.Url = formsPage; + Assert.That(() => driver.FindElement(By.Name("SearchableText")).Submit(), Throws.InstanceOf()); + } - [Test] - public void SendKeysKeepsCapitalization() - { - driver.Url = javascriptPage; - IWebElement textarea = driver.FindElement(By.Id("keyUpArea")); - string cheesey = "BrIe And CheDdar"; - textarea.SendKeys(cheesey); - Assert.That(textarea.GetAttribute("value"), Is.EqualTo(cheesey)); - } + [Test] + public void ShouldBeAbleToEnterTextIntoATextAreaBySettingItsValue() + { + driver.Url = javascriptPage; + IWebElement textarea = driver.FindElement(By.Id("keyUpArea")); + string cheesey = "Brie and cheddar"; + textarea.SendKeys(cheesey); + Assert.That(textarea.GetAttribute("value"), Is.EqualTo(cheesey)); + } - [Test] - [IgnoreBrowser(Browser.Firefox)] - public void ShouldSubmitAFormUsingTheNewlineLiteral() - { - driver.Url = formsPage; - IWebElement nestedForm = driver.FindElement(By.Id("nested_form")); - IWebElement input = nestedForm.FindElement(By.Name("x")); - input.SendKeys("\n"); + [Test] + public void SendKeysKeepsCapitalization() + { + driver.Url = javascriptPage; + IWebElement textarea = driver.FindElement(By.Id("keyUpArea")); + string cheesey = "BrIe And CheDdar"; + textarea.SendKeys(cheesey); + Assert.That(textarea.GetAttribute("value"), Is.EqualTo(cheesey)); + } - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - Assert.That(driver.Url, Does.EndWith("?x=name")); - } + [Test] + [IgnoreBrowser(Browser.Firefox)] + public void ShouldSubmitAFormUsingTheNewlineLiteral() + { + driver.Url = formsPage; + IWebElement nestedForm = driver.FindElement(By.Id("nested_form")); + IWebElement input = nestedForm.FindElement(By.Name("x")); + input.SendKeys("\n"); + + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + Assert.That(driver.Url, Does.EndWith("?x=name")); + } - [Test] - public void ShouldSubmitAFormUsingTheEnterKey() - { - driver.Url = formsPage; - IWebElement nestedForm = driver.FindElement(By.Id("nested_form")); - IWebElement input = nestedForm.FindElement(By.Name("x")); - input.SendKeys(Keys.Enter); + [Test] + public void ShouldSubmitAFormUsingTheEnterKey() + { + driver.Url = formsPage; + IWebElement nestedForm = driver.FindElement(By.Id("nested_form")); + IWebElement input = nestedForm.FindElement(By.Name("x")); + input.SendKeys(Keys.Enter); + + WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); + Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); + Assert.That(driver.Url, Does.EndWith("?x=name")); + } - WaitFor(TitleToBe("We Arrive Here"), "Browser title is not 'We Arrive Here'"); - Assert.That(driver.Title, Is.EqualTo("We Arrive Here")); - Assert.That(driver.Url, Does.EndWith("?x=name")); - } + [Test] + public void ShouldEnterDataIntoFormFields() + { + driver.Url = xhtmlTestPage; + IWebElement element = driver.FindElement(By.XPath("//form[@name='someForm']/input[@id='username']")); + String originalValue = element.GetAttribute("value"); + Assert.That(originalValue, Is.EqualTo("change")); - [Test] - public void ShouldEnterDataIntoFormFields() - { - driver.Url = xhtmlTestPage; - IWebElement element = driver.FindElement(By.XPath("//form[@name='someForm']/input[@id='username']")); - String originalValue = element.GetAttribute("value"); - Assert.That(originalValue, Is.EqualTo("change")); + element.Clear(); + element.SendKeys("some text"); - element.Clear(); - element.SendKeys("some text"); + element = driver.FindElement(By.XPath("//form[@name='someForm']/input[@id='username']")); + String newFormValue = element.GetAttribute("value"); + Assert.That(newFormValue, Is.EqualTo("some text")); + } - element = driver.FindElement(By.XPath("//form[@name='someForm']/input[@id='username']")); - String newFormValue = element.GetAttribute("value"); - Assert.That(newFormValue, Is.EqualTo("some text")); - } + [Test] + public void ShouldBeAbleToAlterTheContentsOfAFileUploadInputElement() + { + string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); + driver.Url = formsPage; + IWebElement uploadElement = driver.FindElement(By.Id("upload")); + Assert.That(uploadElement.GetAttribute("value"), Is.Null.Or.Empty); + + string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); + System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); + System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); + inputFileWriter.WriteLine("Hello world"); + inputFileWriter.Close(); + + uploadElement.SendKeys(inputFile.FullName); + + string uploadElementValue = uploadElement.GetAttribute("value"); + System.IO.FileInfo outputFile = new System.IO.FileInfo(uploadElementValue.Replace('\\', System.IO.Path.DirectorySeparatorChar)); + Assert.That(inputFile.Name, Is.EqualTo(outputFile.Name)); + inputFile.Delete(); + } - [Test] - public void ShouldBeAbleToAlterTheContentsOfAFileUploadInputElement() + [Test] + public void ShouldBeAbleToSendKeysToAFileUploadInputElementInAnXhtmlDocument() + { + // IE before 9 doesn't handle pages served with an XHTML content type, and just prompts for to + // download it + if (TestUtilities.IsOldIE(driver)) { - string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); - driver.Url = formsPage; - IWebElement uploadElement = driver.FindElement(By.Id("upload")); - Assert.That(uploadElement.GetAttribute("value"), Is.Null.Or.Empty); + return; + } - string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); - System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); - System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); - inputFileWriter.WriteLine("Hello world"); - inputFileWriter.Close(); + driver.Url = xhtmlFormPage; + IWebElement uploadElement = driver.FindElement(By.Id("file")); + Assert.That(uploadElement.GetAttribute("value"), Is.Empty); - uploadElement.SendKeys(inputFile.FullName); + string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); + string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); + System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); + System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); + inputFileWriter.WriteLine("Hello world"); + inputFileWriter.Close(); - string uploadElementValue = uploadElement.GetAttribute("value"); - System.IO.FileInfo outputFile = new System.IO.FileInfo(uploadElementValue.Replace('\\', System.IO.Path.DirectorySeparatorChar)); - Assert.That(inputFile.Name, Is.EqualTo(outputFile.Name)); - inputFile.Delete(); - } + uploadElement.SendKeys(inputFile.FullName); - [Test] - public void ShouldBeAbleToSendKeysToAFileUploadInputElementInAnXhtmlDocument() - { - // IE before 9 doesn't handle pages served with an XHTML content type, and just prompts for to - // download it - if (TestUtilities.IsOldIE(driver)) - { - return; - } + string uploadElementValue = uploadElement.GetAttribute("value"); + System.IO.FileInfo outputFile = new System.IO.FileInfo(uploadElementValue.Replace('\\', System.IO.Path.DirectorySeparatorChar)); + Assert.That(outputFile.Name, Is.EqualTo(inputFile.Name)); + inputFile.Delete(); + } - driver.Url = xhtmlFormPage; - IWebElement uploadElement = driver.FindElement(By.Id("file")); - Assert.That(uploadElement.GetAttribute("value"), Is.Empty); + [Test] + [IgnoreBrowser(Browser.Safari, "Driver does not allow uploading same file multiple times.")] + public void ShouldBeAbleToUploadTheSameFileTwice() + { + string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); + string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); + System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); + System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); + inputFileWriter.WriteLine("Hello world"); + inputFileWriter.Close(); - string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); - string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); - System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); - System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); - inputFileWriter.WriteLine("Hello world"); - inputFileWriter.Close(); + for (int i = 0; i < 2; ++i) + { + driver.Url = formsPage; + IWebElement uploadElement = driver.FindElement(By.Id("upload")); + Assert.That(uploadElement.GetAttribute("value"), Is.Null.Or.EqualTo(string.Empty)); uploadElement.SendKeys(inputFile.FullName); + uploadElement.Submit(); - string uploadElementValue = uploadElement.GetAttribute("value"); - System.IO.FileInfo outputFile = new System.IO.FileInfo(uploadElementValue.Replace('\\', System.IO.Path.DirectorySeparatorChar)); - Assert.That(outputFile.Name, Is.EqualTo(inputFile.Name)); - inputFile.Delete(); + // Explicitly wait next page to be loaded, Firefox is not handling elements submitting + WaitFor(() => driver.Url.EndsWith("resultPage.html"), "We are not redirected to the resultPage after submitting web element"); } - [Test] - [IgnoreBrowser(Browser.Safari, "Driver does not allow uploading same file multiple times.")] - public void ShouldBeAbleToUploadTheSameFileTwice() - { - string testFileName = string.Format("test-{0}.txt", Guid.NewGuid().ToString("D")); - string filePath = System.IO.Path.Combine(EnvironmentManager.Instance.CurrentDirectory, testFileName); - System.IO.FileInfo inputFile = new System.IO.FileInfo(filePath); - System.IO.StreamWriter inputFileWriter = inputFile.CreateText(); - inputFileWriter.WriteLine("Hello world"); - inputFileWriter.Close(); - - for (int i = 0; i < 2; ++i) - { - driver.Url = formsPage; - IWebElement uploadElement = driver.FindElement(By.Id("upload")); - Assert.That(uploadElement.GetAttribute("value"), Is.Null.Or.EqualTo(string.Empty)); - - uploadElement.SendKeys(inputFile.FullName); - uploadElement.Submit(); + inputFile.Delete(); + // If we get this far, then we're all good. + } - // Explicitly wait next page to be loaded, Firefox is not handling elements submitting - WaitFor(() => driver.Url.EndsWith("resultPage.html"), "We are not redirected to the resultPage after submitting web element"); - } + [Test] + public void SendingKeyboardEventsShouldAppendTextInInputs() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("working")); + element.SendKeys("Some"); + String value = element.GetAttribute("value"); + Assert.That(value, Is.EqualTo("Some")); + + element.SendKeys(" text"); + value = element.GetAttribute("value"); + Assert.That(value, Is.EqualTo("Some text")); + } - inputFile.Delete(); - // If we get this far, then we're all good. - } + [Test] + public void SendingKeyboardEventsShouldAppendTextInInputsWithExistingValue() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("inputWithText")); + element.SendKeys(". Some text"); + string value = element.GetAttribute("value"); - [Test] - public void SendingKeyboardEventsShouldAppendTextInInputs() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("working")); - element.SendKeys("Some"); - String value = element.GetAttribute("value"); - Assert.That(value, Is.EqualTo("Some")); - - element.SendKeys(" text"); - value = element.GetAttribute("value"); - Assert.That(value, Is.EqualTo("Some text")); - } + Assert.That(value, Is.EqualTo("Example text. Some text")); + } - [Test] - public void SendingKeyboardEventsShouldAppendTextInInputsWithExistingValue() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("inputWithText")); - element.SendKeys(". Some text"); - string value = element.GetAttribute("value"); + [Test] + public void SendingKeyboardEventsShouldAppendTextInTextAreas() + { + driver.Url = formsPage; + IWebElement element = driver.FindElement(By.Id("withText")); - Assert.That(value, Is.EqualTo("Example text. Some text")); - } + element.SendKeys(". Some text"); + String value = element.GetAttribute("value"); - [Test] - public void SendingKeyboardEventsShouldAppendTextInTextAreas() - { - driver.Url = formsPage; - IWebElement element = driver.FindElement(By.Id("withText")); + Assert.That(value, Is.EqualTo("Example text. Some text")); + } - element.SendKeys(". Some text"); - String value = element.GetAttribute("value"); + [Test] + public void EmptyTextBoxesShouldReturnAnEmptyStringNotNull() + { + driver.Url = formsPage; + IWebElement emptyTextBox = driver.FindElement(By.Id("working")); + Assert.That(emptyTextBox.GetAttribute("value"), Is.Empty); - Assert.That(value, Is.EqualTo("Example text. Some text")); - } + IWebElement emptyTextArea = driver.FindElement(By.Id("emptyTextArea")); + Assert.That(emptyTextBox.GetAttribute("value"), Is.Empty); + } - [Test] - public void EmptyTextBoxesShouldReturnAnEmptyStringNotNull() + [Test] + public void HandleFormWithJavascriptAction() + { + string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("form_handling_js_submit.html"); + driver.Url = url; + IWebElement element = driver.FindElement(By.Id("theForm")); + element.Submit(); + IAlert alert = WaitFor(() => { - driver.Url = formsPage; - IWebElement emptyTextBox = driver.FindElement(By.Id("working")); - Assert.That(emptyTextBox.GetAttribute("value"), Is.Empty); + try + { + return driver.SwitchTo().Alert(); + } + catch (NoAlertPresentException) + { + return null; + } + }, "No alert found before timeout."); - IWebElement emptyTextArea = driver.FindElement(By.Id("emptyTextArea")); - Assert.That(emptyTextBox.GetAttribute("value"), Is.Empty); - } + string text = alert.Text; + alert.Dismiss(); - [Test] - public void HandleFormWithJavascriptAction() - { - string url = EnvironmentManager.Instance.UrlBuilder.WhereIs("form_handling_js_submit.html"); - driver.Url = url; - IWebElement element = driver.FindElement(By.Id("theForm")); - element.Submit(); - IAlert alert = WaitFor(() => - { - try - { - return driver.SwitchTo().Alert(); - } - catch (NoAlertPresentException) - { - return null; - } - }, "No alert found before timeout."); - - string text = alert.Text; - alert.Dismiss(); - - Assert.That(text, Is.EqualTo("Tasty cheese")); - } + Assert.That(text, Is.EqualTo("Tasty cheese")); + } - [Test] - public void CanClickOnASubmitButton() - { - CheckSubmitButton("internal_explicit_submit"); - } + [Test] + public void CanClickOnASubmitButton() + { + CheckSubmitButton("internal_explicit_submit"); + } - [Test] - public void CanClickOnASubmitButtonNestedSpan() - { - CheckSubmitButton("internal_span_submit"); - } + [Test] + public void CanClickOnASubmitButtonNestedSpan() + { + CheckSubmitButton("internal_span_submit"); + } - [Test] - public void CanClickOnAnImplicitSubmitButton() - { - CheckSubmitButton("internal_implicit_submit"); - } + [Test] + public void CanClickOnAnImplicitSubmitButton() + { + CheckSubmitButton("internal_implicit_submit"); + } - [Test] - [IgnoreBrowser(Browser.IE, "IE does not support the HTML5 'form' attribute on