Skip to content

Commit

Permalink
Merge pull request #3 from maik-hasler/develop
Browse files Browse the repository at this point in the history
Added IWebDriverManager<T> & extension method to fire DOM events
  • Loading branch information
maik-hasler committed Apr 21, 2023
2 parents 5d52f53 + 20d4991 commit 68f0da5
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 92 deletions.
22 changes: 22 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
root = True

# Default settings
[*]
insert_final_newline = true
indent_style = space
indent_size = 4

# Xml project files
[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
indent_size = 2

# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2

# Xml config files
[*.{props,targets,config,nuspec}]
indent_size = 2

[*.yml]
indent_size = 2
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/maik-hasler/SeleniumSharper/compare/v1.1.0...HEAD)

## [v1.1.0](https://github.com/maik-hasler/SeleniumSharper/releases/tag/v1.1.0)
**Published:** 21th April 2023
### Added
- `IWebDriverManager<TOptions>` and first implementation for Chrome to automatically install driver binaries
- Helper classes related to `IWebDriverManager<TOptions>`
- Enum and extension method to fire JavaScript event
### Changed
- Upgrade dependencies: Selenium.WebDriver, Selenium.Support etc.
### Removed
- Custom result classes

## [v1.0.0](https://github.com/maik-hasler/SeleniumSharper/releases/tag/v1.0.0)
**Published:** 19th April 2023
### Added
- `WebElementsConditionBuilder<TSearchContext, TSearchResult>` to build wait conditions for a `ReadOnlyCollection<IWebElement>`
- `WebElementConditionBuilder<TSearchContext, TSearchResult>` to build wait conditions for a `IWebElement`
- `ClassConditionBuilder<TSearchContext, TSearchResult>` to build wait conditions for a `IEquatable<string>`
- Custom result classes `WebElementsVisibilityResult` and `WebElementVisibilityResult`
### Changed
- Changed `Waiter<T>` to `ContextualWait<TSearchContext>`, which supports more generic method chaining
### Removed
- Collection of commonly used selenium wait conditions

## [v1.0.0-preview.0](https://github.com/maik-hasler/SeleniumSharper/releases/tag/v1.0.0-preview.0)
**Published:** 18th April 2023
<br />
**Disclaimer:** This is a pre-release. It was published in order to verify, that the nuget.yml workflow works fine.
### Added
- `Waiter<T>` to wait for a specified condition to be satisfied
- Collection of commonly used selenium wait conditions
- Extension method to create a `Waiter<T>` object from an `ISearchContext`
- Various `IJavaScriptExecutor` extension methods
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using OpenQA.Selenium;

namespace SeleniumSharper;
namespace SeleniumSharper.Conditions;

public class ClassConditionBuilder<TSearchContext, TSearchResult>
public class StringConditionBuilder<TSearchContext, TSearchResult>
where TSearchContext : ISearchContext
where TSearchResult : IEquatable<string>
{
private readonly ContextualWait<TSearchContext> _contextualWait;

private readonly Func<TSearchContext, string> _action;

public ClassConditionBuilder(ContextualWait<TSearchContext> fluentWait, Func<TSearchContext, string> action)
public StringConditionBuilder(ContextualWait<TSearchContext> fluentWait, Func<TSearchContext, string> action)
{
_contextualWait = fluentWait;
_action = action;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using OpenQA.Selenium;

namespace SeleniumSharper;
namespace SeleniumSharper.Conditions;

public class WebElementConditionBuilder<TSearchContext, TSearchResult>
where TSearchContext : ISearchContext
Expand All @@ -16,32 +16,24 @@ public WebElementConditionBuilder(ContextualWait<TSearchContext> fluentWait, Fun
_action = action;
}

public WebElementVisibilityResult IsVisible()
public IWebElement? IsVisible()
{
IWebElement? webElement = null;

try
{
IWebElement? webElement = null;

var isDisplayed = _contextualWait.Wait.Until(ctx =>
_contextualWait.Wait.Until(ctx =>
{
webElement = _action.Invoke(ctx);
return webElement.Displayed;
});

return new WebElementVisibilityResult
{
WebElement = webElement,
IsVisible = isDisplayed
};
return webElement;
}
catch (WebDriverTimeoutException)
{
return new WebElementVisibilityResult
{
WebElement = null,
IsVisible = false
};
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using OpenQA.Selenium;
using SeleniumSharper.Models;
using System.Collections.ObjectModel;

namespace SeleniumSharper;
namespace SeleniumSharper.Conditions;

public sealed class WebElementsConditionBuilder<TSearchContext, TSearchResult>
where TSearchContext : ISearchContext
Expand All @@ -18,32 +17,24 @@ public WebElementsConditionBuilder(ContextualWait<TSearchContext> fluentWait, Fu
_action = action;
}

public WebElementsVisibilityResult AreVisible()
public ReadOnlyCollection<IWebElement>? AreVisible()
{
ReadOnlyCollection<IWebElement>? webElements = null;

try
{
ReadOnlyCollection<IWebElement>? webElements = null;

var areDisplayed = _contextualWait.Wait.Until(ctx =>
_contextualWait.Wait.Until(ctx =>
{
webElements = _action.Invoke(ctx);
return webElements.All(e => e.Displayed);
});

return new WebElementsVisibilityResult
{
WebElements = webElements,
AreDisplayed = areDisplayed
};
return webElements;
}
catch (WebDriverTimeoutException)
{
return new WebElementsVisibilityResult
{
WebElements = null,
AreDisplayed = false
};
return null;
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/ContextualWait.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using SeleniumSharper.Conditions;

namespace SeleniumSharper;

Expand Down Expand Up @@ -27,8 +28,8 @@ public ContextualWait(TSearchContext searchContext, TimeSpan timeout)
return new WebElementConditionBuilder<TSearchContext, IWebElement>(this, action);
}

public ClassConditionBuilder<TSearchContext, string> Until(Func<TSearchContext, string> action)
public StringConditionBuilder<TSearchContext, string> Until(Func<TSearchContext, string> action)
{
return new ClassConditionBuilder<TSearchContext, string>(this, action);
return new StringConditionBuilder<TSearchContext, string>(this, action);
}
}
11 changes: 11 additions & 0 deletions src/DomEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace SeleniumSharper;

public enum DomEvent
{
Click,
DoubleClick,
MouseDown,
MouseUp,
KeyDown,
KeyUp
}
7 changes: 7 additions & 0 deletions src/JavaScriptExecutorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ public static void Click(this IJavaScriptExecutor executor, IWebElement elementT
{
executor.ExecuteScript("arguments[0].click();", elementToBeClicked);
}

public static void DispatchEvent(this IJavaScriptExecutor executor, IWebElement webElement, DomEvent domEvent)
{
var domEventName = domEvent.ToString().ToLower();

executor.ExecuteScript("[0].dispatchEvent(new Event('[1]'));", webElement, domEventName);
}
}
101 changes: 101 additions & 0 deletions src/Managers/ChromeDriverManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using SeleniumSharper.Managers.Interfaces;
using SeleniumSharper.Managers.Services;
using System.Runtime.InteropServices;

namespace SeleniumSharper.Managers;

public sealed class ChromeDriverManager : IWebDriverManager<ChromeOptions>
{
public IWebDriver Setup()
{
var driverPath = InstallBinary();

var chromeDriverService = ChromeDriverService.CreateDefaultService(driverPath);

return new ChromeDriver(chromeDriverService);
}

public IWebDriver Setup(ChromeOptions chromeOptions)
{
var driverPath = InstallBinary();

var chromeDriverService = ChromeDriverService.CreateDefaultService(driverPath);

return new ChromeDriver(chromeDriverService, chromeOptions);
}

private static string GetBinaryName()
{
var suffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;

return $"chromedriver{suffix}";
}

private static Uri GetDownloadUrl(string version, string fileName)
{
var url = $"https://chromedriver.storage.googleapis.com/{version}/{fileName}";

return new Uri(url);
}

private static string GetFileName()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var architectureExtension = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? "_arm64" : "64";

return $"chromedriver_mac{architectureExtension}.zip";
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "chromedriver_linux64.zip";
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "chromedriver_win32.zip";
}

throw new PlatformNotSupportedException();
}

private static string GetLatestVersion()
{
var url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE";

using var httpClient = new HttpClient();

var response = httpClient.GetStringAsync(url).Result;

return response.Trim();
}

private static string GetBinaryPath(string version)
{
var architecture = Environment.Is64BitOperatingSystem ? "64" : "32";

return Path.Combine(
Directory.GetCurrentDirectory(),
"Binaries",
"Chrome",
version,
architecture,
GetBinaryName());
}

private static string InstallBinary()
{
var version = GetLatestVersion();

var fileName = GetFileName();

var downloadUrl = GetDownloadUrl(version, fileName);

var binaryPath = GetBinaryPath(version);

return WebDriverManagerUtils.InstallBinary(fileName, downloadUrl, binaryPath, GetBinaryName());
}
}
11 changes: 11 additions & 0 deletions src/Managers/Interfaces/IWebDriverManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using OpenQA.Selenium;

namespace SeleniumSharper.Managers.Interfaces;

public interface IWebDriverManager<TOptions>
where TOptions : DriverOptions
{
public IWebDriver Setup();

public IWebDriver Setup(TOptions options);
}

0 comments on commit 68f0da5

Please sign in to comment.