Skip to content

Commit

Permalink
Merge pull request #90 from FlaUI/error-handling
Browse files Browse the repository at this point in the history
Improve error handling of unhandled exceptions and timeouts on loading the main window
  • Loading branch information
aristotelos authored Sep 27, 2024
2 parents b4ca676 + b230eb9 commit 814e059
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 4 deletions.
10 changes: 10 additions & 0 deletions src/FlaUI.WebDriver.UITests/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,5 +359,15 @@ public void NewCommandTimeout_InvalidValue_Throws(object value)
Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:newCommandTimeout must be a number"));
}

[Test]
public void UnknownCommand_Default_ReturnsError()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

Assert.That(() => driver.Manage().Cookies.DeleteAllCookies(),
Throws.TypeOf<System.NotImplementedException>().With.Message.EqualTo("Unknown command"));
}
}
}
33 changes: 33 additions & 0 deletions src/FlaUI.WebDriver/Controllers/ErrorController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using FlaUI.WebDriver.Models;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace FlaUI.WebDriver.Controllers
{
[ApiController]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorController : ControllerBase
{
private readonly ILogger<ErrorController> _logger;


public ErrorController(ILogger<ErrorController> logger) {
_logger = logger;
}

[Route("/error")]
public IActionResult HandleError() {
var exceptionHandlerFeature = HttpContext.Features.Get<IExceptionHandlerFeature>()!;

_logger.LogError(exceptionHandlerFeature.Error, "Returning WebDriver error response with error code 'unknown error'");

return new ObjectResult(new ResponseWithValue<ErrorResponse>(new ErrorResponse {
ErrorCode = "unknown error",
Message = exceptionHandlerFeature.Error.Message,
StackTrace = exceptionHandlerFeature.Error.StackTrace ?? "" }))
{
StatusCode = 500
};
}
}
}
47 changes: 47 additions & 0 deletions src/FlaUI.WebDriver/NotFoundMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FlaUI.WebDriver.Models;

namespace FlaUI.WebDriver
{
public class NotFoundMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;

public NotFoundMiddleware(RequestDelegate next, ILogger<NotFoundMiddleware> logger)
{
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext httpContext)
{
await _next(httpContext);
if (!httpContext.Response.HasStarted)
{
if (httpContext.Response.StatusCode == StatusCodes.Status404NotFound)
{
_logger.LogError("Unknown endpoint {Path}", SanitizeForLog(httpContext.Request.Path.ToString()));
await httpContext.Response.WriteAsJsonAsync(new ResponseWithValue<ErrorResponse>(new ErrorResponse
{
ErrorCode = "unknown command",
Message = "Unknown command"
}));
}
else if (httpContext.Response.StatusCode == StatusCodes.Status405MethodNotAllowed)
{
_logger.LogError("Unknown method {Method} for endpoint {Path}", SanitizeForLog(httpContext.Request.Method), SanitizeForLog(httpContext.Request.Path.ToString()));
await httpContext.Response.WriteAsJsonAsync(new ResponseWithValue<ErrorResponse>(new ErrorResponse
{
ErrorCode = "unknown method",
Message = "Unknown method for this endpoint"
}));
}
}
}

private static string SanitizeForLog(string str)
{
return str.Replace("\r", "").Replace("\n", "");
}
}
}
4 changes: 4 additions & 0 deletions src/FlaUI.WebDriver/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@

var app = builder.Build();

app.UseMiddleware<NotFoundMiddleware>();

app.UseExceptionHandler("/error");

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
Expand Down
7 changes: 6 additions & 1 deletion src/FlaUI.WebDriver/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ public Session(Application? app, bool isAppOwnedBySession)
if (app != null)
{
// We have to capture the initial window handle to be able to keep it stable
CurrentWindowWithHandle = GetOrAddKnownWindow(app.GetMainWindow(Automation, PageLoadTimeout));
var mainWindow = app.GetMainWindow(Automation, PageLoadTimeout);
if (mainWindow == null)
{
throw WebDriverResponseException.Timeout($"Could not get the main window of the app within the page load timeout (${PageLoadTimeout.TotalMilliseconds}ms)");
}
CurrentWindowWithHandle = GetOrAddKnownWindow(mainWindow);
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/FlaUI.WebDriver/WebDriverExceptionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ public void OnActionExecuted(ActionExecutedContext context)
if (context.Exception is WebDriverResponseException exception)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<WebDriverResponseExceptionFilter>>();
logger.LogError(exception, "Returning WebDriver error response with error code {ErrorCode}", exception.ErrorCode);
logger.LogError(exception, "Returning WebDriver error response with error code '{ErrorCode}'", exception.ErrorCode);

context.Result = new ObjectResult(new ResponseWithValue<ErrorResponse>(new ErrorResponse { ErrorCode = exception.ErrorCode, Message = exception.Message })) {
context.Result = new ObjectResult(new ResponseWithValue<ErrorResponse>(new ErrorResponse { ErrorCode = exception.ErrorCode, Message = exception.Message }))
{
StatusCode = exception.StatusCode
};
context.ExceptionHandled = true;
}
}
}
}
}
2 changes: 2 additions & 0 deletions src/FlaUI.WebDriver/WebDriverResponseException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ private WebDriverResponseException(string message, string errorCode, int statusC
public static WebDriverResponseException WindowNotFoundByHandle(string windowHandle) => new WebDriverResponseException($"No window found with handle '{windowHandle}'", "no such window", 404);

public static WebDriverResponseException NoWindowsOpenForSession() => new WebDriverResponseException($"No windows are open for the current session", "no such window", 404);

public static WebDriverResponseException Timeout(string message) => new WebDriverResponseException($"Timeout: ${message}", "timeout", 500);
}
}

0 comments on commit 814e059

Please sign in to comment.