diff --git a/src/AxaFrance.AxeExtended.HtmlReport/AxaFrance.AxeExtended.HtmlReport.csproj b/src/AxaFrance.AxeExtended.HtmlReport/AxaFrance.AxeExtended.HtmlReport.csproj
index 37a88d0..6c2db60 100644
--- a/src/AxaFrance.AxeExtended.HtmlReport/AxaFrance.AxeExtended.HtmlReport.csproj
+++ b/src/AxaFrance.AxeExtended.HtmlReport/AxaFrance.AxeExtended.HtmlReport.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/AxaFrance.AxeExtended.Selenium/AxaFrance.AxeExtended.Selenium.csproj b/src/AxaFrance.AxeExtended.Selenium/AxaFrance.AxeExtended.Selenium.csproj
index a4c1477..4e8b17c 100644
--- a/src/AxaFrance.AxeExtended.Selenium/AxaFrance.AxeExtended.Selenium.csproj
+++ b/src/AxaFrance.AxeExtended.Selenium/AxaFrance.AxeExtended.Selenium.csproj
@@ -19,7 +19,8 @@
-
+
+
diff --git a/src/AxaFrance.WebEngine.Mcp/AxaFrance.WebEngine.Mcp.csproj b/src/AxaFrance.WebEngine.Mcp/AxaFrance.WebEngine.Mcp.csproj
index 137f257..e196bf8 100644
--- a/src/AxaFrance.WebEngine.Mcp/AxaFrance.WebEngine.Mcp.csproj
+++ b/src/AxaFrance.WebEngine.Mcp/AxaFrance.WebEngine.Mcp.csproj
@@ -10,8 +10,8 @@
-
-
+
+
diff --git a/src/AxaFrance.WebEngine.Mcp/Dockerfile b/src/AxaFrance.WebEngine.Mcp/Dockerfile
deleted file mode 100644
index e6e45e6..0000000
--- a/src/AxaFrance.WebEngine.Mcp/Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-# This stage is used when running from VS in fast mode (Default for Debug configuration)
-FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
-USER $APP_UID
-WORKDIR /app
-EXPOSE 8080
-EXPOSE 8081
-
-
-# This stage is used to build the service project
-FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
-ARG BUILD_CONFIGURATION=Release
-WORKDIR /src
-COPY ["McpGuildeTest/McpGuildeTest.csproj", "McpGuildeTest/"]
-RUN dotnet restore "./McpGuildeTest/McpGuildeTest.csproj"
-COPY . .
-WORKDIR "/src/McpGuildeTest"
-RUN dotnet build "./McpGuildeTest.csproj" -c $BUILD_CONFIGURATION -o /app/build
-
-# This stage is used to publish the service project to be copied to the final stage
-FROM build AS publish
-ARG BUILD_CONFIGURATION=Release
-RUN dotnet publish "./McpGuildeTest.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
-
-# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "McpGuildeTest.dll"]
\ No newline at end of file
diff --git a/src/AxaFrance.WebEngine.Mcp/Tools/Selenium/AccessibilitySnapshot.cs b/src/AxaFrance.WebEngine.Mcp/Tools/Selenium/AccessibilitySnapshot.cs
index 56fa96b..30d4c31 100644
--- a/src/AxaFrance.WebEngine.Mcp/Tools/Selenium/AccessibilitySnapshot.cs
+++ b/src/AxaFrance.WebEngine.Mcp/Tools/Selenium/AccessibilitySnapshot.cs
@@ -108,7 +108,7 @@ function getName(el) {
} catch(e) {}
}
- // 4. Enclosing � strip the control's own text
+ // 4. Enclosing & strip the control's own text
var pl = el.closest ? el.closest('label') : null;
if (pl) {
var clone = pl.cloneNode(true);
@@ -224,7 +224,7 @@ function walk(el, depth) {
return lines.length > 0
? lines.join('\n')
- : '(no accessibility tree found � the page likely has poor ARIA markup; use GetPageHtml as fallback)';
+ : '(no accessibility tree found. the page likely has poor ARIA markup; use GetPageHtml as fallback)';
})();
""";
diff --git a/src/AxaFrance.WebEngine.MobileApp/AppFactory.cs b/src/AxaFrance.WebEngine.MobileApp/AppFactory.cs
index b821c32..2d7ad95 100644
--- a/src/AxaFrance.WebEngine.MobileApp/AppFactory.cs
+++ b/src/AxaFrance.WebEngine.MobileApp/AppFactory.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2016-2022 AXA France IARD / AXA France VIE. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// Modified By: YUAN Huaxing, at: 2022-5-13 18:26
-using AngleSharp.Common;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
diff --git a/src/AxaFrance.WebEngine.MobileApp/AxaFrance.WebEngine.MobileApp.csproj b/src/AxaFrance.WebEngine.MobileApp/AxaFrance.WebEngine.MobileApp.csproj
index e67383e..a7c06e5 100644
--- a/src/AxaFrance.WebEngine.MobileApp/AxaFrance.WebEngine.MobileApp.csproj
+++ b/src/AxaFrance.WebEngine.MobileApp/AxaFrance.WebEngine.MobileApp.csproj
@@ -20,9 +20,10 @@
-
+
-
+
+
diff --git a/src/AxaFrance.WebEngine.ReportViewer/AxaFrance.WebEngine.ReportViewer.csproj b/src/AxaFrance.WebEngine.ReportViewer/AxaFrance.WebEngine.ReportViewer.csproj
index 3172063..054c0f4 100644
--- a/src/AxaFrance.WebEngine.ReportViewer/AxaFrance.WebEngine.ReportViewer.csproj
+++ b/src/AxaFrance.WebEngine.ReportViewer/AxaFrance.WebEngine.ReportViewer.csproj
@@ -46,8 +46,8 @@
-
-
+
+
diff --git a/src/AxaFrance.WebEngine.Web/AxaFrance.WebEngine.Web.csproj b/src/AxaFrance.WebEngine.Web/AxaFrance.WebEngine.Web.csproj
index 655fbaf..823f075 100644
--- a/src/AxaFrance.WebEngine.Web/AxaFrance.WebEngine.Web.csproj
+++ b/src/AxaFrance.WebEngine.Web/AxaFrance.WebEngine.Web.csproj
@@ -19,11 +19,11 @@
-
+
-
-
-
+
+
+
diff --git a/src/AxaFrance.WebEngine.Web/BrowserFactory.cs b/src/AxaFrance.WebEngine.Web/BrowserFactory.cs
index a7ce3f1..5797a85 100644
--- a/src/AxaFrance.WebEngine.Web/BrowserFactory.cs
+++ b/src/AxaFrance.WebEngine.Web/BrowserFactory.cs
@@ -15,8 +15,6 @@
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
-using WebDriverManager.DriverConfigs.Impl;
-using WebDriverManager.Helpers;
using static System.Environment;
[assembly: InternalsVisibleTo("WebRunner")]
@@ -426,7 +424,6 @@ private static object GetBrowserStackBrowserName(BrowserType browser)
private static WebDriver GetEdgeDriver(IEnumerable browserOptions)
{
- new WebDriverManager.DriverManager().SetUpDriver(new EdgeConfig(), "MatchingBrowser");
OpenQA.Selenium.Edge.EdgeOptions options = new OpenQA.Selenium.Edge.EdgeOptions()
{
AcceptInsecureCertificates = true,
@@ -439,7 +436,6 @@ private static WebDriver GetEdgeDriver(IEnumerable browserOptions)
private static WebDriver GetChromeDriver(IEnumerable browserOptions)
{
- string version = "MatchingBrowser";
var options = new OpenQA.Selenium.Chrome.ChromeOptions()
{
AcceptInsecureCertificates = true,
@@ -487,20 +483,17 @@ private static WebDriver GetChromeDriver(IEnumerable browserOptions)
options.AddArgument("no-sandbox");
if (browserOptions != null) options.AddArguments(browserOptions);
- new WebDriverManager.DriverManager().SetUpDriver(new ChromeConfig(), version);
OpenQA.Selenium.Chrome.ChromeDriver cd = new OpenQA.Selenium.Chrome.ChromeDriver(options);
return cd;
}
private static WebDriver GetFirefoxDriver(IEnumerable browserOptions)
{
- var binaryPath = new WebDriverManager.DriverManager().SetUpDriver(new FirefoxConfig(), VersionResolveStrategy.Latest);
var directories = System.IO.Directory.GetDirectories($"{Environment.GetEnvironmentVariable("APPDATA")}\\Mozilla\\Firefox\\Profiles");
var d = directories.First(x => x.EndsWith("default"));
OpenQA.Selenium.Firefox.FirefoxProfile profile = new OpenQA.Selenium.Firefox.FirefoxProfile(d);
OpenQA.Selenium.Firefox.FirefoxOptions firefoxOptions = new OpenQA.Selenium.Firefox.FirefoxOptions();
firefoxOptions.AcceptInsecureCertificates = true;
- firefoxOptions.BinaryLocation = binaryPath?.ToString();
firefoxOptions.Profile = profile;
if (browserOptions != null) firefoxOptions.AddArguments(browserOptions);
OpenQA.Selenium.Firefox.FirefoxDriver driver = new OpenQA.Selenium.Firefox.FirefoxDriver(firefoxOptions);
diff --git a/src/Samples.Gherkin/Samples.Gherkin.csproj b/src/Samples.Gherkin/Samples.Gherkin.csproj
index 5012366..f15c73d 100644
--- a/src/Samples.Gherkin/Samples.Gherkin.csproj
+++ b/src/Samples.Gherkin/Samples.Gherkin.csproj
@@ -7,10 +7,10 @@
-
+
-
+
diff --git a/src/Samples.LinearScripting/Samples.LinearScripting.csproj b/src/Samples.LinearScripting/Samples.LinearScripting.csproj
index db13a56..fbd508d 100644
--- a/src/Samples.LinearScripting/Samples.LinearScripting.csproj
+++ b/src/Samples.LinearScripting/Samples.LinearScripting.csproj
@@ -8,9 +8,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/WebEngine.Test/UnitTests/Passkey.cs b/src/WebEngine.Test/UnitTests/Passkey.cs
index b0dbf29..230d650 100644
--- a/src/WebEngine.Test/UnitTests/Passkey.cs
+++ b/src/WebEngine.Test/UnitTests/Passkey.cs
@@ -5,10 +5,10 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.VirtualAuth;
+using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
namespace WebEngine.Test.UnitTests
{
@@ -22,15 +22,9 @@ namespace WebEngine.Test.UnitTests
[TestClass]
public class Passkey
{
- static List storedCredentials = null;
- static string testEmail = null;
-
- [ClassInitialize]
- public static void Initialize(TestContext context)
+ private static string CreateTestEmail()
{
- // Generate unique test email for all tests
- testEmail = $"test.passkey.{DateTime.Now:yyyyMMddHHmmss}@example.com";
- DebugLogger.WriteLine($"Test email for this run: {testEmail}");
+ return $"test.passkey.{DateTime.UtcNow:yyyyMMddHHmmssfff}.{Guid.NewGuid():N}@example.com";
}
///
@@ -58,102 +52,81 @@ private static string AddVirtualAuthenticator(WebDriver driver)
}
///
- /// Gets and stores all credentials from the virtual authenticator
- /// This allows credentials to be restored in subsequent tests
+ /// Gets all credentials from the virtual authenticator.
///
- private static void GetAndStoreCredentials(WebDriver driver)
+ private static List GetCredentials(WebDriver driver)
{
- try
+ if (!(driver is IHasVirtualAuthenticator hasVirtualAuth))
{
- var seleniumDriver = driver as IWebDriver;
- if (seleniumDriver is IHasVirtualAuthenticator hasVirtualAuth)
- {
- // Get all credentials from the authenticator
- var credentials = hasVirtualAuth.GetCredentials();
- storedCredentials = credentials.ToList();
+ throw new InvalidOperationException("The WebDriver does not support virtual authenticators.");
+ }
- DebugLogger.WriteLine($"Stored {storedCredentials.Count} credential(s) from virtual authenticator");
+ var credentials = hasVirtualAuth.GetCredentials().ToList();
+ DebugLogger.WriteLine($"Stored {credentials.Count} credential(s) from virtual authenticator");
- foreach (var cred in storedCredentials)
- {
- DebugLogger.WriteLine($" - Credential ID: {BitConverter.ToString(cred.Id).Replace("-", "")}");
- DebugLogger.WriteLine($" RP ID: {cred.RpId}");
- DebugLogger.WriteLine($" Is Resident: {cred.IsResidentCredential}");
- }
- }
+ foreach (var cred in credentials)
+ {
+ DebugLogger.WriteLine($" - Credential ID: {BitConverter.ToString(cred.Id).Replace("-", "")}");
+ DebugLogger.WriteLine($" RP ID: {cred.RpId}");
+ DebugLogger.WriteLine($" Is Resident: {cred.IsResidentCredential}");
}
- catch (Exception ex)
+
+ return credentials;
+ }
+
+ private static void WaitForCredentials(WebDriver driver)
+ {
+ if (!(driver is IHasVirtualAuthenticator hasVirtualAuth))
{
- DebugLogger.WriteLine($"Error getting credentials: {ex.Message}");
- throw;
+ throw new InvalidOperationException("The WebDriver does not support virtual authenticators.");
}
+
+ var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
+ wait.Until(_ => hasVirtualAuth.GetCredentials().Any());
}
///
- /// Restores all previously stored credentials to the virtual authenticator
- /// This is used to simulate a returning user who has already registered
+ /// Restores credentials to the virtual authenticator.
///
- private static void RestoreCredentials(WebDriver driver)
+ private static void RestoreCredentials(WebDriver driver, IEnumerable credentials)
{
- try
+ if (!(driver is IHasVirtualAuthenticator hasVirtualAuth))
{
- if (storedCredentials != null && storedCredentials.Count > 0)
- {
- var seleniumDriver = driver as IWebDriver;
- if (seleniumDriver is IHasVirtualAuthenticator hasVirtualAuth)
- {
- foreach (var credential in storedCredentials)
- {
- hasVirtualAuth.AddCredential(credential);
- }
-
- DebugLogger.WriteLine($"Restored {storedCredentials.Count} credential(s) to virtual authenticator");
- }
- }
- else
- {
- DebugLogger.WriteLine("Warning: No credentials to restore");
- }
+ throw new InvalidOperationException("The WebDriver does not support virtual authenticators.");
}
- catch (Exception ex)
+
+ var credentialList = credentials.ToList();
+ foreach (var credential in credentialList)
{
- DebugLogger.WriteLine($"Error restoring credentials: {ex.Message}");
- throw;
+ hasVirtualAuth.AddCredential(credential);
}
+
+ DebugLogger.WriteLine($"Restored {credentialList.Count} credential(s) to virtual authenticator");
}
- [TestMethod]
- [TestCategory("Passkey")]
- [Priority(1)]
- public void Test01_RegisterPasskey()
+ private static List RegisterPasskey()
{
WebDriver driver = null;
- PasskeyPageModel pageModel = null;
try
{
- // Arrange
driver = BrowserFactory.GetDriver(AxaFrance.WebEngine.Platform.Windows, BrowserType.ChromiumEdge);
AddVirtualAuthenticator(driver);
- pageModel = new PasskeyPageModel(driver);
+ var pageModel = new PasskeyPageModel(driver);
+ var testEmail = CreateTestEmail();
+ DebugLogger.WriteLine($"Test email for registration: {testEmail}");
driver.Navigate().GoToUrl("https://www.passkeys.io/");
pageModel.CreateAccountLink.Click();
pageModel.CreateAccountEmailInput.SetValue(testEmail);
pageModel.CreateAccountContinueButton.Click();
pageModel.CreatePasskeyButton.Click();
- GetAndStoreCredentials(driver);
+ WaitForCredentials(driver);
- // Assert
- Assert.IsNotNull(storedCredentials, "Credentials should be stored after registration");
- Assert.IsTrue(storedCredentials.Count > 0, "At least one credential should be created during registration");
-
- DebugLogger.WriteLine($"Registration completed. Current URL: {driver.Url}");
- DebugLogger.WriteLine($"Total credentials stored: {storedCredentials.Count}");
+ return GetCredentials(driver);
}
finally
{
- // Cleanup: Dispose the driver after test
try
{
driver?.Quit();
@@ -172,71 +145,54 @@ public void Test01_RegisterPasskey()
}
}
+ [TestMethod]
+ [TestCategory("Passkey")]
+ [Priority(1)]
+ public void Test01_RegisterPasskey()
+ {
+ var credentials = RegisterPasskey();
+
+ Assert.IsTrue(credentials.Count > 0, "At least one credential should be created during registration");
+ }
+
[TestMethod]
[TestCategory("Passkey")]
[Priority(2)]
public void Test02_LoginWithPasskey()
{
WebDriver driver = null;
- PasskeyPageModel pageModel = null;
try
{
// Arrange
- // Verify that credentials were stored from the registration test
- Assert.IsNotNull(storedCredentials, "Credentials should have been stored from registration test");
- Assert.IsTrue(storedCredentials.Count > 0, "There should be at least one credential to use for login");
-
- DebugLogger.WriteLine($"Starting login test with {storedCredentials.Count} stored credential(s)");
+ var credentials = RegisterPasskey();
+ Assert.IsTrue(credentials.Count > 0, "There should be at least one credential to use for login");
+ DebugLogger.WriteLine($"Starting login test with {credentials.Count} stored credential(s)");
// Create a new WebDriver instance with virtual authenticator
driver = BrowserFactory.GetDriver(AxaFrance.WebEngine.Platform.Windows, BrowserType.Chrome);
AddVirtualAuthenticator(driver);
- // Restore the credentials to the virtual authenticator
- RestoreCredentials(driver);
+ RestoreCredentials(driver, credentials);
- pageModel = new PasskeyPageModel(driver);
+ var pageModel = new PasskeyPageModel(driver);
// Navigate to login page
driver.Navigate().GoToUrl("https://www.passkeys.io/");
- Thread.Sleep(2000); // Wait for page to fully load
- // Verify we're on Sign In page
- Assert.IsTrue(pageModel.IsOnSignInPage(), "Should be on Sign In page");
- DebugLogger.WriteLine("On Sign In page");
+ // Conditional mediation may complete the login before the button is available.
+ var authenticationStateWait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
+ authenticationStateWait.Until(_ => pageModel.IsOnSignInPage() || pageModel.IsOnProfilePage());
- // Act - Click "Sign in with a passkey" button
- DebugLogger.WriteLine("Clicking 'Sign in with a passkey' button");
- pageModel.SignInWithPasskeyButton.Click();
- Thread.Sleep(3000); // Wait for WebAuthn ceremony to complete
-
- // Assert
- // Verify that login flow was executed
- string currentUrl = driver.Url;
- DebugLogger.WriteLine($"Login completed. Current URL: {currentUrl}");
-
- // Check for successful login indicators
- bool isLoggedIn = currentUrl != "https://www.passkeys.io/" ||
- currentUrl.Contains("success") ||
- currentUrl.Contains("dashboard") ||
- currentUrl.Contains("welcome") ||
- currentUrl.Contains("profile");
-
- if (!isLoggedIn)
+ // Act - Click "Sign in with a passkey" button when conditional mediation did not log in.
+ if (pageModel.IsOnSignInPage())
{
- // Alternative check: look for error box being hidden (indicates success)
- bool errorBoxHidden = !pageModel.ErrorBox.IsDisplayed;
-
- // Check if we're no longer on the Sign In page
- bool notOnSignInPage = !pageModel.IsOnSignInPage();
-
- isLoggedIn = errorBoxHidden || notOnSignInPage;
+ DebugLogger.WriteLine("Clicking 'Sign in with a passkey' button");
+ pageModel.SignInWithPasskeyButton.Click();
+ authenticationStateWait.Until(_ => pageModel.IsOnProfilePage());
}
- DebugLogger.WriteLine($"Login status: {(isLoggedIn ? "Success" : "Completed")}");
- DebugLogger.WriteLine($"Error box visible: {pageModel.ErrorBox.IsDisplayed}");
- // Login flow with passkey completed successfully
+ Assert.IsTrue(pageModel.IsOnProfilePage(), "The passkey login should display the profile page");
}
finally
{
diff --git a/src/WebEngine.Test/UnitTests/PasskeyPageModel.cs b/src/WebEngine.Test/UnitTests/PasskeyPageModel.cs
index 8f833fd..486bf58 100644
--- a/src/WebEngine.Test/UnitTests/PasskeyPageModel.cs
+++ b/src/WebEngine.Test/UnitTests/PasskeyPageModel.cs
@@ -208,10 +208,18 @@ public PasskeyPageModel(WebDriver driver) : base(driver)
ClassName = "hanko_footer"
};
+ ///
+ /// Profile section displayed after a successful authentication.
+ ///
+ public WebElementDescription ProfileSection { get; set; } = new WebElementDescription()
+ {
+ CssSelector = "#profile-div:not([hidden])"
+ };
+
///
/// Sign In page headline
///
- private WebElementDescription SignInHeadline { get; set; } = new WebElementDescription()
+ public WebElementDescription SignInHeadline { get; set; } = new WebElementDescription()
{
TagName = "h1",
ClassName = "hanko_headline hanko_grade1",
@@ -227,7 +235,15 @@ public PasskeyPageModel(WebDriver driver) : base(driver)
///
public bool IsOnSignInPage()
{
- return SignInHeadline.Exists();
+ return SignInHeadline.Exists(1);
+ }
+
+ ///
+ /// Checks if the authenticated profile is displayed.
+ ///
+ public bool IsOnProfilePage()
+ {
+ return ProfileSection.Exists(1);
}
///
diff --git a/src/WebEngine.Test/WebEngine.Test.csproj b/src/WebEngine.Test/WebEngine.Test.csproj
index d9403d0..3baf8ce 100644
--- a/src/WebEngine.Test/WebEngine.Test.csproj
+++ b/src/WebEngine.Test/WebEngine.Test.csproj
@@ -27,9 +27,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive