Как протестировать сервер Blazor от Playwright?C#

Место общения программистов C#
Ответить
Anonymous
 Как протестировать сервер Blazor от Playwright?

Сообщение Anonymous »

У меня есть следующий код для запуска сервера Kestrel на случайном порту.

Код: Выделить всё

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Abc;

public sealed class KestrelWebAppFactory : WebApplicationFactory
{
protected override IHost CreateHost(IHostBuilder builder)
{
// Build the TestServer host first (required by WebApplicationFactory)
var testHost = builder.Build();

// Reconfigure the builder to use a real Kestrel server on a dynamic port
builder.ConfigureWebHost(webHost =>
{
webHost.UseKestrel();
webHost.UseUrls("http://127.0.0.1:0");
});

// Build & start the Kestrel host
var kestrelHost = builder.Build();
kestrelHost.Start();

// Wait for Kestrel to replace :0 with the actual port
var server = kestrelHost.Services.GetRequiredService();
var addressesFeature = server.Features.Get()!;

// Small spin-wait until Kestrel publishes a concrete port
var sw = System.Diagnostics.Stopwatch.StartNew();
string bound = null;
while (sw.Elapsed < TimeSpan.FromSeconds(5))
{
bound = addressesFeature.Addresses.FirstOrDefault(a => !a.EndsWith(":0", StringComparison.Ordinal));
if (bound is not null) break;
Thread.Sleep(25);
}
if (bound is null)
throw new InvalidOperationException("Kestrel did not publish a bound address.");

// Point the factory’s HttpClient at the real server so Playwright can use it
ClientOptions.BaseAddress = new Uri(bound);

// Start and return the TestServer host (WAF expects this)
testHost.Start();
return testHost;
}
}
Затем у меня есть следующий прибор, который я использую в своем тесте

Код: Выделить всё

namespace AbcE2ETests;

// Hosts the Kestrel server and Playwright browser for a test class.
public class ServerFixture : IAsyncLifetime
{
private const string PlaywrightContextStoragePath = "/temp/playwrightstate.json";

public KestrelWebAppFactory Factory { get; private set; }

public Uri BaseUrl => Factory.ClientOptions.BaseAddress;
public IPlaywright Playwright { get; private set; }
public IBrowser Browser { get; private set; }
public IBrowserContext Context { get; private set; }
public IPage Page { get; private set; }

public virtual async Task InitializeAsync()
{
Factory = new KestrelWebAppFactory();
await WaitForServerStartAsync();

// Initialize Playwright and a fresh browser context/page for this fixture
await SignInAsync();
}

public string GetFullUrl(string relativeUrl) => new Uri(BaseUrl, relativeUrl).ToString();

public async ValueTask EnterValuesByNameAsync(Dictionary namesAndValues)
{
foreach (var kvp in namesAndValues)
{
ILocator input = Page.Locator($"input[name='{kvp.Key}']").First;
await input.WaitForAsync();
await input.FocusAsync();
Task setValueTask = kvp.Value switch
{
BrowserConstants.FormValues.Checkbox.Checked => input.CheckAsync(),
BrowserConstants.FormValues.Checkbox.Unchecked => input.UncheckAsync(),
_ =>  input.FillAsync(kvp.Value)
};
await setValueTask;
}
}

public async ValueTask PressEnterAsync() =>
await Page.Keyboard.PressAsync(BrowserConstants.Keys.Enter);

public async ValueTask NavigateToAsync(string relativeUrl) =>
await Page.GotoAsync(GetFullUrl(relativeUrl));

private async Task SignInAsync()
{
await InitializePlaywrightAsync(readFromStoragePath: false);
await NavigateToAsync("/Account/LogIn");
await EnterValuesByNameAsync(new Dictionary
{
["Input.TenantCode"] = "ASL",
["Input.Email"] = "mrpmorris@home.com",
["Input.Password"] = "SuperSecretPassword"
});

await PressEnterAsync();
await Page.WaitForURLAsync(GetFullUrl("/"));
await Context.StorageStateAsync(new()
{
// relative to test bin folder
Path = PlaywrightContextStoragePath
});
await DisposePlaywrightAsync();

await InitializePlaywrightAsync(readFromStoragePath: true);
await Task.Delay(300_000);
}

// Let descendants tweak context options (e.g., viewport, locale, storage state)
protected virtual void ConfigureContextOptions(BrowserNewContextOptions options) { }

protected async Task WaitForServerStartAsync()
{
HttpClient http = Factory.CreateClient();
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.Elapsed < TimeSpan.FromSeconds(5))
{
try
{
HttpResponseMessage response = await http.GetAsync(Factory.ClientOptions.BaseAddress);
if (response.IsSuccessStatusCode)
return;
}
catch { }
}
throw new InvalidOperationException("Server failed to start");
}

public virtual async Task DisposeAsync()
{
await DisposePlaywrightAsync();
Factory?.Dispose();
Factory = null;
}

private async ValueTask DisposePlaywrightAsync()
{
try
{
if (Context is not null)
await Context.CloseAsync();
if (Browser is not null)
await Browser.CloseAsync();
}
finally
{
Playwright?.Dispose();

Context = null;
Browser = null;
Playwright = null;
}
}

private async Task InitializePlaywrightAsync(bool readFromStoragePath)
{
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = false
});

var ctxOptions = new BrowserNewContextOptions
{
BaseURL = Factory.ClientOptions.BaseAddress.ToString(),
StorageStatePath = readFromStoragePath ? PlaywrightContextStoragePath : null
};
ConfigureContextOptions(ctxOptions);

Context = await Browser.NewContextAsync(ctxOptions);
Page = await Context.NewPageAsync();
}

}
Это прекрасно работает, за исключением случаев, когда вход в систему завершен и меня перенаправляют на /, я просто получаю пустой экран.

Подробнее здесь: https://stackoverflow.com/questions/798 ... playwright
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»