File: PageInformation.cs
Web Access
Project: src\src\Shared\BrowserTesting\src\Microsoft.AspNetCore.BrowserTesting.csproj (Microsoft.AspNetCore.BrowserTesting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Extensions.Logging;
using Microsoft.Playwright;
 
namespace Microsoft.AspNetCore.BrowserTesting;
 
public class PageInformation : IDisposable
{
    private readonly IPage _page;
    private readonly ILogger<PageInformation> _logger;
 
    public List<string> FailedRequests { get; } = new();
 
    public List<LogEntry> BrowserConsoleLogs { get; } = new();
 
    public List<string> PageErrors { get; } = new();
 
    public List<IWebSocket> WebSockets { get; set; } = new();
 
    public PageInformation(IPage page, ILogger<PageInformation> logger)
    {
        page.Console += RecordConsoleMessage;
        page.PageError += RecordPageError;
        page.RequestFailed += RecordFailedRequest;
        page.WebSocket += CaptureWebSocket;
        _page = page;
        _logger = logger;
 
        _ = LogPageVideoPath();
    }
 
    private void CaptureWebSocket(object sender, IWebSocket e)
    {
        WebSockets.Add(e);
    }
 
    private async Task LogPageVideoPath()
    {
        try
        {
            var path = _page.Video != null ? await _page.Video.PathAsync() : null;
            if (path != null)
            {
                _logger.LogInformation($"Page video recorded at: {path}");
            }
        }
        catch
        {
            // Silently swallow since we don't have a good way to report it and its not critical.
            throw;
        }
    }
 
    public void Dispose()
    {
        _page.Console -= RecordConsoleMessage;
        _page.PageError -= RecordPageError;
        _page.RequestFailed -= RecordFailedRequest;
    }
 
    private void RecordFailedRequest(object sender, IRequest e)
    {
        try
        {
            _logger.LogError(e.Failure);
        }
        catch
        {
        }
        FailedRequests.Add(e.Failure);
    }
 
    private void RecordPageError(object sender, string e)
    {
        // There needs to be a bit of experimentation with this, but message should be a good start.
        try
        {
            _logger.LogError(e);
        }
        catch
        {
        }
 
        PageErrors.Add(e);
    }
 
    private void RecordConsoleMessage(object sender, IConsoleMessage message)
    {
        try
        {
            var messageText = message.Text.Replace(Environment.NewLine, $"{Environment.NewLine}      ");
            var location = message.Location;
 
            var logMessage = $"[{_page.Url}]{Environment.NewLine}      {messageText}{Environment.NewLine}      ({location})";
 
            var logLevel = message.Type switch
            {
                "info" => LogLevel.Information,
                "verbose" => LogLevel.Debug,
                "warning" => LogLevel.Warning,
                "error" => LogLevel.Error,
                _ => LogLevel.Information
            };
            _logger.Log(logLevel, logMessage);
 
            BrowserConsoleLogs.Add(new LogEntry(messageText, message.Type));
        }
        catch
        {
            // Logging after the test is finished should not cause the test to fail
        }
    }
 
    public sealed class LogEntry
    {
        public string Message { get; }
 
        public string Level { get; }
 
        public LogEntry(string message, string level)
        {
            Message = message;
            Level = level;
        }
    }
}