4
0

BlazorLatency.cs 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. using System.Text.RegularExpressions;
  2. using System.Threading.Channels;
  3. using Microsoft.Playwright;
  4. namespace Tests.E2e.Infra;
  5. /// <summary>
  6. /// Simulates a slow network between the browser and the Blazor Server circuit
  7. /// by delaying every SignalR WebSocket frame in both directions. Frame order is preserved.
  8. /// Must be called before the page navigates, so the circuit's socket is routed.
  9. /// </summary>
  10. public static partial class BlazorLatency {
  11. public static Task AddAsync(IPage page, TimeSpan oneWayDelay) =>
  12. page.RouteWebSocketAsync(BlazorSocket(), ws => {
  13. IWebSocketRoute server = ws.ConnectToServer();
  14. ws.OnMessage(DelayedForwarder(oneWayDelay, server));
  15. server.OnMessage(DelayedForwarder(oneWayDelay, ws));
  16. });
  17. private static Action<IWebSocketFrame> DelayedForwarder(TimeSpan delay, IWebSocketRoute target) {
  18. var queue = Channel.CreateUnbounded<(DateTime Due, string? Text, byte[]? Binary)>();
  19. _ = Task.Run(async () => {
  20. await foreach ((DateTime due, var text, var binary) in queue.Reader.ReadAllAsync()) {
  21. TimeSpan wait = due - DateTime.UtcNow;
  22. if (wait > TimeSpan.Zero)
  23. await Task.Delay(wait);
  24. if (binary is not null)
  25. target.Send(binary);
  26. else
  27. target.Send(text ?? string.Empty);
  28. }
  29. });
  30. return frame => queue.Writer.TryWrite((DateTime.UtcNow + delay, frame.Text, frame.Binary));
  31. }
  32. [GeneratedRegex("/_blazor")]
  33. private static partial Regex BlazorSocket();
  34. }