From 26b0139db802a20ff45c46035ff7b74d338841cd Mon Sep 17 00:00:00 2001 From: Craig Fowler Date: Sun, 8 Mar 2026 15:04:24 +0000 Subject: [PATCH 1/2] Provisionally resolve #305 - Improve BrowserStack reporting --- .../BrowserStack/BrowserStackExtension.cs | 49 +++++-------------- .../BrowserStackSessionIdProvider.cs | 21 -------- 2 files changed, 11 insertions(+), 59 deletions(-) delete mode 100644 Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackSessionIdProvider.cs diff --git a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackExtension.cs b/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackExtension.cs index eb139b02..5594cc64 100644 --- a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackExtension.cs +++ b/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackExtension.cs @@ -1,23 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Http.Json; using System.Reflection; -using System.Text; using BrowserStack; using CSF.Screenplay.Performances; +using Microsoft.Extensions.DependencyInjection; namespace CSF.Screenplay.Selenium.BrowserStack; public sealed class BrowserStackExtension : IDisposable { - const string urlPattern = "https://api.browserstack.com/automate/sessions/{0}.json"; - Local? browserStackLocal; IHasPerformanceEvents? eventBus; - HttpClient? httpClient; public async Task OnTestRunStarting() { @@ -33,8 +27,6 @@ public async Task OnTestRunStarting() } if(!browserStackLocal.isRunning()) throw new TimeoutException("BrowserStack Local is still not running after 20 seconds"); - httpClient = GetHttpClient(); - eventBus = ScreenplayLocator.GetScreenplay(Assembly.GetExecutingAssembly()).GetEventBus(); eventBus.PerformanceFinished += UpdateBrowserStackSession; @@ -44,43 +36,24 @@ void UpdateBrowserStackSession(object? sender, PerformanceFinishedEventArgs e) { if(!e.Success.HasValue) return; - var sessionId = BrowserStackSessionIdProvider.GetBrowserStackSessionId(e.Performance); - if(sessionId is null) return; - - var uri = GetApiUri(sessionId); - var requestMessage = GetRequestMessage(uri, e.Success.Value); - httpClient!.Send(requestMessage); - } - - static HttpRequestMessage GetRequestMessage(Uri requestUri, bool success) - { - return new (HttpMethod.Put, requestUri) - { - Content = JsonContent.Create(@$"{{""status"":""{ (success ? "passed" : "failed") }"", ""reason"":""""}}"), - }; - } - - static Uri GetApiUri(string sessionId) => new(string.Format(urlPattern, sessionId)); + var cast = e.Performance.ServiceProvider.GetRequiredService(); + var ability = (from actorName in cast.GetCastList() + let actor = cast.GetActor(actorName) + where ((IHasAbilities) actor).HasAbility() + select actor.GetAbility()) + .FirstOrDefault(); - static HttpClient GetHttpClient() - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = GetAuthenticationHeaderValue(); - return client; - } + if(ability is null) return; - static AuthenticationHeaderValue GetAuthenticationHeaderValue() - { - var headerBytes = Encoding.ASCII.GetBytes($"{BrowserStackEnvironment.GetBrowserStackUserName()}:{BrowserStackEnvironment.GetBrowserStackAccessKey()}"); - var headerValue = Convert.ToBase64String(headerBytes); - return new ("Basic", headerValue); + var jsExecutor = ability.GetJavaScriptExecutor(); + var json = $@"{{""action"":""setSessionStatus"",""arguments"":{{""status"":""{(e.Success.Value ? "passed" : "failed")}"",""reason"":""Test completion""}}}}"; + jsExecutor.ExecuteScript("browserstack_executor: " + json); } /// public void Dispose() { browserStackLocal?.stop(); - httpClient?.Dispose(); if(eventBus is null) return; eventBus.PerformanceFinished -= UpdateBrowserStackSession; diff --git a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackSessionIdProvider.cs b/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackSessionIdProvider.cs deleted file mode 100644 index 23a134c3..00000000 --- a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackSessionIdProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using System.Text.Json.Nodes; - -namespace CSF.Screenplay.Selenium.BrowserStack; - -public static class BrowserStackSessionIdProvider -{ - public static string? GetBrowserStackSessionId(IPerformance performance) - { - var performanceCast = performance.ServiceProvider.GetRequiredService(); - var actors = performanceCast.GetCastList().Select(name => performanceCast.GetActor(name)); - var browseTheWeb = actors.FirstOrDefault((ICanPerform actor) => actor.HasAbility())?.GetAbility(); - if(browseTheWeb is null) return null; - - var javascriptExecutor = browseTheWeb.GetJavaScriptExecutor(); - var sessionDetailsJson = (string) javascriptExecutor.ExecuteScript("browserstack_executor: {\"action\": \"getSessionDetails\"}"); - var sessionDetails = JsonNode.Parse(sessionDetailsJson)!; - return (string?) sessionDetails["hashed_id"]; - } -} \ No newline at end of file From 97ffaa0ab0fd96dddc58a6b52a458fa77e3cbc8e Mon Sep 17 00:00:00 2001 From: Craig Fowler Date: Sun, 8 Mar 2026 15:24:22 +0000 Subject: [PATCH 2/2] WIP #305 - Improve BS test naming --- .../BrowserStack/BrowserStackDriverFactory.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackDriverFactory.cs b/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackDriverFactory.cs index a03a7ad7..b76e3148 100644 --- a/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackDriverFactory.cs +++ b/Tests/CSF.Screenplay.Selenium.Tests/BrowserStack/BrowserStackDriverFactory.cs @@ -63,5 +63,14 @@ static DriverOptions GetDriverOptions() }; } - static string GetTestName() => TestContext.CurrentContext.Test.FullName; + static string GetTestName() + { + var className = TestContext.CurrentContext.Test.ClassName; + var methodName = TestContext.CurrentContext.Test.MethodName; + + return (className is null || methodName is null) + ? TestContext.CurrentContext.Test.FullName + : $"{className}.{methodName}"; + + } }