Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for chrome for testing apis #254

Merged
merged 5 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WebDriverManager.Tests/ChromeConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void VersionTest()
}

[Fact]
public void DriverDownloadTest()
public void DriverDownloadLatestTest()
{
new DriverManager().SetUpDriver(new ChromeConfig());
Assert.NotEmpty(WebDriverFinder.FindFile(GetBinaryName()));
Expand Down
59 changes: 59 additions & 0 deletions WebDriverManager/Clients/ChromeForTestingClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebDriverManager.Models.Chrome;

namespace WebDriverManager.Clients
{
public static class ChromeForTestingClient
{
private static readonly string BaseUrl = "https://googlechromelabs.github.io/chrome-for-testing/";
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};

private static readonly HttpClient _httpClient;

static ChromeForTestingClient()
{
_httpClient = new HttpClient
{
BaseAddress = new Uri(BaseUrl)
};
}

public static ChromeVersions GetKnownGoodVersionsWithDownloads()
{
return GetResultFromHttpTask<ChromeVersions>(
_httpClient.GetAsync("known-good-versions-with-downloads.json")
);
}

public static ChromeVersions GetLastKnownGoodVersions()
{
return GetResultFromHttpTask<ChromeVersions>(
_httpClient.GetAsync("last-known-good-versions-with-downloads.json")
);
}

/// <summary>
/// Get a HTTP result without causing any deadlocks
/// <para>See: https://learn.microsoft.com/en-us/archive/blogs/jpsanders/asp-net-do-not-use-task-result-in-main-context</para>
/// </summary>
/// <typeparam name="TResult">The type of result to convert the HTTP response to</typeparam>
/// <param name="taskToRun">The <see cref="HttpResponseMessage"/> task to run</param>
private static TResult GetResultFromHttpTask<TResult>(Task<HttpResponseMessage> taskToRun)
where TResult : class
{
var httpTask = Task.Run(() => taskToRun);
httpTask.Wait();

var readStringTask = Task.Run(() => httpTask.Result.Content.ReadAsStringAsync());
readStringTask.Wait();

return JsonSerializer.Deserialize<TResult>(readStringTask.Result, JsonSerializerOptions);
}
}
}
163 changes: 137 additions & 26 deletions WebDriverManager/DriverConfigs/Impl/ChromeConfig.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using WebDriverManager.Clients;
using WebDriverManager.Helpers;
using WebDriverManager.Models.Chrome;

namespace WebDriverManager.DriverConfigs.Impl
{
public class ChromeConfig : IDriverConfig
{
private const string BaseVersionPatternUrl = "https://chromedriver.storage.googleapis.com/<version>/";
private const string LatestReleaseVersionUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE";
private const string ExactReleaseVersionPatternUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_<version>";

private const string ExactReleaseVersionPatternUrl =
"https://chromedriver.storage.googleapis.com/LATEST_RELEASE_<version>";
/// <summary>
/// The minimum version required to download chrome drivers from Chrome for Testing API's
/// </summary>
private static readonly Version MinChromeForTestingDriverVersion = new Version("115.0.5763.0");

/// <summary>
/// The minimum version of chrome driver required to reference download URLs via the "arm64" extension
/// </summary>
private static readonly Version MinArm64ExtensionVersion = new Version("106.0.5249.61");

private ChromeVersionInfo _chromeVersionInfo;
private string _chromeVersion;

public virtual string GetName()
{
Expand All @@ -31,23 +44,14 @@ public virtual string GetUrl64()

private string GetUrl()
{
#if NETSTANDARD
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
// Handle newer versions of chrome driver only being available for download via the Chrome for Testing API's
// whilst retaining backwards compatibility for older versions of chrome/chrome driver.
if (_chromeVersionInfo != null)
{
var architectureExtension =
RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.Arm64
? "_arm64"
: "64";
return $"{BaseVersionPatternUrl}chromedriver_mac{architectureExtension}.zip";
return GetUrlFromChromeForTestingApi();
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return $"{BaseVersionPatternUrl}chromedriver_linux64.zip";
}
#endif

return $"{BaseVersionPatternUrl}chromedriver_win32.zip";
return GetUrlFromChromeStorage();
}

public virtual string GetBinaryName()
Expand All @@ -63,10 +67,49 @@ public virtual string GetBinaryName()

public virtual string GetLatestVersion()
{
return GetLatestVersion(LatestReleaseVersionUrl);
var chromeReleases = ChromeForTestingClient.GetLastKnownGoodVersions();
var chromeStable = chromeReleases.Channels.Stable;

_chromeVersionInfo = new ChromeVersionInfo
{
Downloads = chromeStable.Downloads
};

return chromeStable.Version;
}

private static string GetLatestVersion(string url)
public virtual string GetMatchingBrowserVersion()
{
var rawChromeBrowserVersion = GetRawBrowserVersion();
if (string.IsNullOrEmpty(rawChromeBrowserVersion))
{
throw new Exception("Not able to get chrome version or not installed");
}

var chromeVersion = VersionHelper.GetVersionWithoutRevision(rawChromeBrowserVersion);

// Handle downloading versions of the chrome webdriver less than what's supported by the Chrome for Testing known good versions API
// See https://googlechromelabs.github.io/chrome-for-testing for more info
var matchedVersion = new Version(rawChromeBrowserVersion);
if (matchedVersion < MinChromeForTestingDriverVersion)
{
var url = ExactReleaseVersionPatternUrl.Replace("<version>", chromeVersion);
_chromeVersion = GetVersionFromChromeStorage(url);
}
else
{
_chromeVersion = GetVersionFromChromeForTestingApi(chromeVersion).Version;
iouym marked this conversation as resolved.
Show resolved Hide resolved
}

return _chromeVersion;
}

/// <summary>
/// Retrieves a chrome driver version string from https://chromedriver.storage.googleapis.com
/// </summary>
/// <param name="url">The request URL</param>
/// <returns>A chrome driver version string</returns>
private static string GetVersionFromChromeStorage(string url)
{
var uri = new Uri(url);
var webRequest = WebRequest.Create(uri);
Expand All @@ -84,17 +127,85 @@ private static string GetLatestVersion(string url)
}
}

public virtual string GetMatchingBrowserVersion()
/// <summary>
/// Retrieves a download URL for a chrome driver from the https://chromedriver.storage.googleapis.com API's
/// </summary>
/// <returns>A chrome driver download URL</returns>
private string GetUrlFromChromeStorage()
{
var rawChromeBrowserVersion = GetRawBrowserVersion();
if (string.IsNullOrEmpty(rawChromeBrowserVersion))
#if NETSTANDARD
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
throw new Exception("Not able to get chrome version or not installed");
// Handle older versions of chrome driver arm64 builds that are tagged with 64_m1 instead of arm64.
// See: https://chromedriver.storage.googleapis.com/index.html?path=106.0.5249.21/
var useM1Prefix = new Version(_chromeVersion) < MinArm64ExtensionVersion;
var armArchitectureExtension = useM1Prefix
? "64_m1"
: "_arm64";

var architectureExtension = RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.Arm64
? armArchitectureExtension
: "64";

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

var chromeBrowserVersion = VersionHelper.GetVersionWithoutRevision(rawChromeBrowserVersion);
var url = ExactReleaseVersionPatternUrl.Replace("<version>", chromeBrowserVersion);
return GetLatestVersion(url);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return $"{BaseVersionPatternUrl}chromedriver_linux64.zip";
}
#endif

return $"{BaseVersionPatternUrl}chromedriver_win32.zip";
}

/// <summary>
/// Retrieves a chrome driver version string from https://googlechromelabs.github.io/chrome-for-testing
/// </summary>
/// <param name="version">The desired version to download</param>
/// <returns>Chrome driver version info (version number, revision number, download URLs)</returns>
private ChromeVersionInfo GetVersionFromChromeForTestingApi(string noRevisionVersion)
{
var knownGoodVersions = ChromeForTestingClient.GetKnownGoodVersionsWithDownloads();

// Pull latest patch version
_chromeVersionInfo = knownGoodVersions.Versions.LastOrDefault(
cV => cV.Version.Contains(noRevisionVersion)
);

return _chromeVersionInfo;
}

/// <summary>
/// Retrieves a chrome driver download URL from Chrome for Testing API's
/// </summary>
/// <returns>A chrome driver download URL</returns>
private string GetUrlFromChromeForTestingApi()
{
var platform = "win32";

#if NETSTANDARD
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
platform = RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.Arm64
? "mac-arm64"
: "mac-x64";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
platform = "linux64";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
platform = RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.X64
? "win64"
: "win32";
}
#endif
var result = _chromeVersionInfo.Downloads.ChromeDriver
.FirstOrDefault(driver => driver.Platform == platform);

return result.Url;
}

private string GetRawBrowserVersion()
Expand Down
9 changes: 9 additions & 0 deletions WebDriverManager/Models/Chrome/ChromeDownload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace WebDriverManager.Models.Chrome
{
public class ChromeDownload
{
public IEnumerable<ChromePlatformInfo> ChromeDriver { get; set; }
}
}
9 changes: 9 additions & 0 deletions WebDriverManager/Models/Chrome/ChromePlatformInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace WebDriverManager.Models.Chrome
{
public class ChromePlatformInfo
{
public string Platform { get; set; }

public string Url { get; set; }
}
}
13 changes: 13 additions & 0 deletions WebDriverManager/Models/Chrome/ChromeReleaseChannels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace WebDriverManager.Models.Chrome
{
public class ChromeReleaseChannels
{
public ChromeReleaseTrack Stable { get; set; }

public ChromeReleaseTrack Beta { get; set; }

public ChromeReleaseTrack Dev { get; set; }

public ChromeReleaseTrack Canary { get; set; }
}
}
13 changes: 13 additions & 0 deletions WebDriverManager/Models/Chrome/ChromeReleaseTrack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace WebDriverManager.Models.Chrome
{
public class ChromeReleaseTrack
{
public string Channel { get; set; }

public string Version { get; set; }

public string Revision { get; set; }

public ChromeDownload Downloads { get; set; }
}
}
11 changes: 11 additions & 0 deletions WebDriverManager/Models/Chrome/ChromeVersionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace WebDriverManager.Models.Chrome
{
public class ChromeVersionInfo
{
public string Version { get; set; }

public string Revision { get; set; }

public ChromeDownload Downloads { get; set; }
}
}
13 changes: 13 additions & 0 deletions WebDriverManager/Models/Chrome/ChromeVersions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;

namespace WebDriverManager.Models.Chrome
{
public class ChromeVersions
{
public string Timestamp { get; set; }

public ChromeReleaseChannels Channels { get; set; }

public IEnumerable<ChromeVersionInfo> Versions { get; set; }
}
}
1 change: 1 addition & 0 deletions WebDriverManager/WebDriverManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<PackageReference Include="AngleSharp" Version="1.0.4" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
Expand Down