TestRenderer.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Runtime.ExceptionServices;
  7. using System.Threading.Tasks;
  8. using Microsoft.AspNetCore.Components.Rendering;
  9. using Xunit;
  10. namespace Microsoft.AspNetCore.Components.Test.Helpers
  11. {
  12. public class TestRenderer : Renderer
  13. {
  14. public TestRenderer() : this(new TestServiceProvider())
  15. {
  16. }
  17. public TestRenderer(IDispatcher dispatcher) : base(new TestServiceProvider(), dispatcher)
  18. {
  19. }
  20. public TestRenderer(IServiceProvider serviceProvider) : base(serviceProvider, new RendererSynchronizationContext())
  21. {
  22. }
  23. public Action OnExceptionHandled { get; set; }
  24. public Action<RenderBatch> OnUpdateDisplay { get; set; }
  25. public Action OnUpdateDisplayComplete { get; set; }
  26. public List<CapturedBatch> Batches { get; }
  27. = new List<CapturedBatch>();
  28. public List<Exception> HandledExceptions { get; } = new List<Exception>();
  29. public bool ShouldHandleExceptions { get; set; }
  30. public new int AssignRootComponentId(IComponent component)
  31. => base.AssignRootComponentId(component);
  32. public void RenderRootComponent(int componentId, ParameterCollection? parameters = default)
  33. {
  34. var task = InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters ?? ParameterCollection.Empty));
  35. UnwrapTask(task);
  36. }
  37. public new Task RenderRootComponentAsync(int componentId)
  38. => InvokeAsync(() => base.RenderRootComponentAsync(componentId));
  39. public new Task RenderRootComponentAsync(int componentId, ParameterCollection parameters)
  40. => InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters));
  41. public new Task DispatchEventAsync(int eventHandlerId, UIEventArgs args)
  42. => InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, args));
  43. private static Task UnwrapTask(Task task)
  44. {
  45. // This should always be run synchronously
  46. Assert.True(task.IsCompleted);
  47. if (task.IsFaulted)
  48. {
  49. var exception = task.Exception.Flatten().InnerException;
  50. while (exception is AggregateException e)
  51. {
  52. exception = e.InnerException;
  53. }
  54. ExceptionDispatchInfo.Capture(exception).Throw();
  55. }
  56. return task;
  57. }
  58. public T InstantiateComponent<T>() where T : IComponent
  59. => (T)InstantiateComponent(typeof(T));
  60. protected override void HandleException(Exception exception)
  61. {
  62. if (!ShouldHandleExceptions)
  63. {
  64. ExceptionDispatchInfo.Capture(exception).Throw();
  65. }
  66. HandledExceptions.Add(exception);
  67. OnExceptionHandled?.Invoke();
  68. }
  69. protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
  70. {
  71. OnUpdateDisplay?.Invoke(renderBatch);
  72. var capturedBatch = new CapturedBatch();
  73. Batches.Add(capturedBatch);
  74. for (var i = 0; i < renderBatch.UpdatedComponents.Count; i++)
  75. {
  76. ref var renderTreeDiff = ref renderBatch.UpdatedComponents.Array[i];
  77. capturedBatch.AddDiff(renderTreeDiff);
  78. }
  79. // Clone other data, as underlying storage will get reused by later batches
  80. capturedBatch.ReferenceFrames = renderBatch.ReferenceFrames.AsEnumerable().ToArray();
  81. capturedBatch.DisposedComponentIDs = renderBatch.DisposedComponentIDs.AsEnumerable().ToList();
  82. // This renderer updates the UI synchronously, like the WebAssembly one.
  83. // To test async UI updates, subclass TestRenderer and override UpdateDisplayAsync.
  84. OnUpdateDisplayComplete?.Invoke();
  85. return Task.CompletedTask;
  86. }
  87. }
  88. }