From 53440d354fa85f95c82bcbc86d418736e8974b3e Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Thu, 13 Feb 2020 11:18:32 +0000
Subject: [PATCH 01/27] Initial changes
---
src/Extensions/ElementQueryExtensions.cs | 2 +-
.../RenderedFragmentQueryExtensions.cs | 44 +++++++++++++++++++
src/Rendering/IRenderedFragment.cs | 32 +++-----------
3 files changed, 51 insertions(+), 27 deletions(-)
create mode 100644 src/Extensions/RenderedFragmentQueryExtensions.cs
diff --git a/src/Extensions/ElementQueryExtensions.cs b/src/Extensions/ElementQueryExtensions.cs
index b82d136a5..258a7768b 100644
--- a/src/Extensions/ElementQueryExtensions.cs
+++ b/src/Extensions/ElementQueryExtensions.cs
@@ -11,7 +11,7 @@ namespace Bunit
/// Helper methods for querying types.
///
public static class ElementQueryExtensions
- {
+ {
///
/// Returns the first element within this element (using depth-first pre-order traversal
/// of the document's nodes) that matches the specified group of selectors.
diff --git a/src/Extensions/RenderedFragmentQueryExtensions.cs b/src/Extensions/RenderedFragmentQueryExtensions.cs
new file mode 100644
index 000000000..e85b986dc
--- /dev/null
+++ b/src/Extensions/RenderedFragmentQueryExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using AngleSharp.Dom;
+using Xunit.Sdk;
+
+namespace Bunit
+{
+ ///
+ /// Helper methods for querying .
+ ///
+ public static class RenderedFragmentQueryExtensions
+ {
+ ///
+ /// Returns the first element from the rendered fragment or component under test,
+ /// using the provided , in a depth-first pre-order traversal
+ /// of the rendered nodes.
+ ///
+ /// The group of selectors to use.
+ public static IElement Find(this IRenderedFragment renderedFragment, string cssSelector)
+ {
+ if (renderedFragment is null) throw new ArgumentNullException(nameof(renderedFragment));
+ var result = renderedFragment.Nodes.QuerySelector(cssSelector);
+ if (result is null)
+ throw new ElementNotFoundException(cssSelector);
+ else
+ return result;
+ }
+
+ ///
+ /// Returns a list of elements from the rendered fragment or component under test,
+ /// using the provided , in a depth-first pre-order traversal
+ /// of the rendered nodes.
+ ///
+ /// The group of selectors to use.
+ public static IHtmlCollection FindAll(this IRenderedFragment renderedFragment, string cssSelector)
+ {
+ if (renderedFragment is null) throw new ArgumentNullException(nameof(renderedFragment));
+ return renderedFragment.Nodes.QuerySelectorAll(cssSelector);
+ }
+ }
+}
diff --git a/src/Rendering/IRenderedFragment.cs b/src/Rendering/IRenderedFragment.cs
index 39fb7e549..a159bcc7b 100644
--- a/src/Rendering/IRenderedFragment.cs
+++ b/src/Rendering/IRenderedFragment.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Xunit.Sdk;
@@ -12,6 +13,11 @@ namespace Bunit
///
public interface IRenderedFragment
{
+ ///
+ /// Gets a which will complete when the is rendered again.
+ ///
+ Task NextRender { get; }
+
///
/// Gets the which this rendered fragment belongs to.
///
@@ -50,31 +56,5 @@ public interface IRenderedFragment
/// the snapshot and the rendered markup at that time.
///
void SaveSnapshot();
-
- ///
- /// Returns the first element from the rendered fragment or component under test,
- /// using the provided , in a depth-first pre-order traversal
- /// of the rendered nodes.
- ///
- /// The group of selectors to use.
- public IElement Find(string cssSelector)
- {
- var result = Nodes.QuerySelector(cssSelector);
- if (result is null)
- throw new ElementNotFoundException(cssSelector);
- else
- return result;
- }
-
- ///
- /// Returns a list of elements from the rendered fragment or component under test,
- /// using the provided , in a depth-first pre-order traversal
- /// of the rendered nodes.
- ///
- /// The group of selectors to use.
- public IHtmlCollection FindAll(string cssSelector)
- {
- return Nodes.QuerySelectorAll(cssSelector);
- }
}
}
\ No newline at end of file
From 5565063ce55b2c0e96e5fc21a936a11bc2f870fd Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Thu, 13 Feb 2020 21:50:42 +0000
Subject: [PATCH 02/27] Switched to IObservable instead of Task
NextRender
---
src/ComponentTestFixture.cs | 5 ++
src/Components/TestComponentBase.cs | 16 ++--
.../ElementNotFoundException.cs | 0
src/Extensions/TestContextExtensions.cs | 44 ++++++++++
src/ITestContext.cs | 11 +--
src/RenderEventSubscriber.cs | 86 +++++++++++++++++++
src/Rendering/IRenderedFragment.cs | 9 +-
src/Rendering/RenderEvent.cs | 23 +++++
src/Rendering/RenderEventPublisher.cs | 53 ++++++++++++
src/Rendering/RenderedFragmentBase.cs | 9 +-
src/Rendering/StructAction.cs | 16 ----
src/Rendering/TestRenderer.cs | 44 ++--------
src/TestContext.cs | 14 ---
.../RenderedComponentTest.cs} | 2 +-
tests/{ => Rendering}/RenderedFragmentTest.cs | 8 +-
tests/SampleComponents/TwoChildren.razor | 6 ++
tests/TestContextTest.cs | 26 ++++++
17 files changed, 278 insertions(+), 94 deletions(-)
rename src/{ => Extensions}/ElementNotFoundException.cs (100%)
create mode 100644 src/Extensions/TestContextExtensions.cs
create mode 100644 src/RenderEventSubscriber.cs
create mode 100644 src/Rendering/RenderEvent.cs
create mode 100644 src/Rendering/RenderEventPublisher.cs
delete mode 100644 src/Rendering/StructAction.cs
rename tests/{RenderComponentTest.cs => Rendering/RenderedComponentTest.cs} (95%)
rename tests/{ => Rendering}/RenderedFragmentTest.cs (93%)
create mode 100644 tests/SampleComponents/TwoChildren.razor
create mode 100644 tests/TestContextTest.cs
diff --git a/src/ComponentTestFixture.cs b/src/ComponentTestFixture.cs
index 796b34b88..fbb8f75b5 100644
--- a/src/ComponentTestFixture.cs
+++ b/src/ComponentTestFixture.cs
@@ -13,6 +13,11 @@ namespace Bunit
///
public abstract class ComponentTestFixture : TestContext
{
+ protected void WaitForNextRender(Action? renderTrigger = null, TimeSpan? timeout = null)
+ {
+ TestContextExtensions.WaitForNextRender(this, renderTrigger, timeout);
+ }
+
///
/// Creates a with an as parameter value
/// for this and
diff --git a/src/Components/TestComponentBase.cs b/src/Components/TestComponentBase.cs
index 086abb423..13687d882 100644
--- a/src/Components/TestComponentBase.cs
+++ b/src/Components/TestComponentBase.cs
@@ -86,14 +86,14 @@ public override IRenderedComponent RenderComponent(param
? _testContextAdapter.RenderComponent(parameters)
: base.RenderComponent(parameters);
- ///
- public override void WaitForNextRender(Action renderTrigger, TimeSpan? timeout = null)
- {
- if (_testContextAdapter.HasActiveContext)
- _testContextAdapter.WaitForNextRender(renderTrigger, timeout);
- else
- base.WaitForNextRender(renderTrigger, timeout);
- }
+ /////
+ //public override void WaitForNextRender(Action renderTrigger, TimeSpan? timeout = null)
+ //{
+ // if (_testContextAdapter.HasActiveContext)
+ // _testContextAdapter.WaitForNextRender(renderTrigger, timeout);
+ // else
+ // base.WaitForNextRender(renderTrigger, timeout);
+ //}
private async Task ExecuteFixtureTests(ContainerComponent container)
{
diff --git a/src/ElementNotFoundException.cs b/src/Extensions/ElementNotFoundException.cs
similarity index 100%
rename from src/ElementNotFoundException.cs
rename to src/Extensions/ElementNotFoundException.cs
diff --git a/src/Extensions/TestContextExtensions.cs b/src/Extensions/TestContextExtensions.cs
new file mode 100644
index 000000000..c9641b262
--- /dev/null
+++ b/src/Extensions/TestContextExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Bunit
+{
+ ///
+ /// Helper methods for .
+ ///
+ public static class TestContextExtensions
+ {
+ ///
+ /// Executes the provided action and waits for a render to occur.
+ /// Use this when you have a component that is awaiting e.g. a service to return data to it before rendering again.
+ ///
+ /// The context to wait against.
+ /// The action that somehow causes one or more components to render.
+ /// The maximum time to wait for the next render. If not provided the default is 1 second.
+ /// Thrown when the next render did not happen within the specified .
+ public static void WaitForNextRender(this ITestContext testContext, Action? renderTrigger = null, TimeSpan? timeout = null)
+ {
+ if (testContext is null) throw new ArgumentNullException(nameof(testContext));
+
+ var waitTime = Debugger.IsAttached ? Timeout.InfiniteTimeSpan : timeout ?? TimeSpan.FromSeconds(1);
+
+ using var rvs = new RenderEventSubscriber(testContext.Renderer.RenderEvents);
+
+ renderTrigger?.Invoke();
+
+ if (rvs.RenderCount >= 1) return;
+
+ if (SpinWait.SpinUntil(() => rvs.RenderCount >= 1, waitTime))
+ return;
+ else
+ throw new TimeoutException("No render occurred within the timeout period.");
+ }
+ }
+
+
+}
diff --git a/src/ITestContext.cs b/src/ITestContext.cs
index c3b0f7af0..fa65dc41c 100644
--- a/src/ITestContext.cs
+++ b/src/ITestContext.cs
@@ -1,6 +1,6 @@
using System;
+using System.Threading.Tasks;
using AngleSharp.Dom;
-using Bunit.Diffing;
using Microsoft.AspNetCore.Components;
namespace Bunit
@@ -36,14 +36,5 @@ public interface ITestContext : IDisposable
/// Parameters to pass to the component when it is rendered
/// The rendered
IRenderedComponent RenderComponent(params ComponentParameter[] parameters) where TComponent : class, IComponent;
-
- ///
- /// Executes the provided action and waits for a render to occur.
- /// Use this when you have a component that is awaiting e.g. a service to return data to it before rendering again.
- ///
- /// The action that somehow causes one or more components to render.
- /// The maximum time to wait for the next render. If not provided the default is 1 second.
- /// Thrown when the next render did not happen within the specified .
- void WaitForNextRender(Action renderTrigger, TimeSpan? timeout = null);
}
}
\ No newline at end of file
diff --git a/src/RenderEventSubscriber.cs b/src/RenderEventSubscriber.cs
new file mode 100644
index 000000000..660144428
--- /dev/null
+++ b/src/RenderEventSubscriber.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bunit
+{
+ ///
+ /// Represents a subscriber to s, published by
+ /// the .
+ ///
+ public sealed class RenderEventSubscriber : IObserver, IDisposable
+ {
+ private IDisposable _unsubscriber;
+
+ ///
+ /// Gets the number of renders that have occurred since subscribing.
+ ///
+ public int RenderCount { get; private set; }
+
+ ///
+ /// Gets whether the is disposed an no more
+ /// renders will happen.
+ ///
+ public bool IsCompleted { get; private set; }
+
+ ///
+ /// Gets the latests received by the .
+ ///
+ public RenderEvent? LatestRenderEvent { get; private set; }
+
+ ///
+ /// Gets or sets a callback to invoke when a is received.
+ ///
+ public Action? OnRender { get; set; }
+
+ ///
+ /// Gets or sets a callback to invoke when the is
+ /// disposed and no more renders will happen.
+ ///
+ public Action? OnCompleted { get; set; }
+
+ ///
+ /// Creates an instance of the , and
+ /// subscribes to the provided .
+ ///
+ public RenderEventSubscriber(IObservable observable)
+ {
+ if (observable is null) throw new ArgumentNullException(nameof(observable));
+ _unsubscriber = observable.Subscribe(this);
+ }
+
+ ///
+ /// Unsubscribes from the observable.
+ ///
+ public void Unsubscribe()
+ {
+ _unsubscriber.Dispose();
+ }
+
+ ///
+ /// Unsubscribes from the observable.
+ ///
+ public void Dispose() => Unsubscribe();
+
+ ///
+ void IObserver.OnNext(RenderEvent value)
+ {
+ RenderCount += 1;
+ LatestRenderEvent = value;
+ OnRender?.Invoke(value);
+ }
+
+ ///
+ void IObserver.OnCompleted()
+ {
+ IsCompleted = true;
+ OnCompleted?.Invoke();
+ }
+
+ ///
+ void IObserver.OnError(Exception error)
+ => throw new AggregateException("The renderer throw an error", error);
+ }
+}
diff --git a/src/Rendering/IRenderedFragment.cs b/src/Rendering/IRenderedFragment.cs
index a159bcc7b..caedeed38 100644
--- a/src/Rendering/IRenderedFragment.cs
+++ b/src/Rendering/IRenderedFragment.cs
@@ -13,10 +13,11 @@ namespace Bunit
///
public interface IRenderedFragment
{
- ///
- /// Gets a which will complete when the is rendered again.
- ///
- Task NextRender { get; }
+ /////
+ ///// Gets an which will provide subscribers with s from the
+ ///// during its life time.
+ /////
+ //IObservable RenderEvents { get; }
///
/// Gets the which this rendered fragment belongs to.
diff --git a/src/Rendering/RenderEvent.cs b/src/Rendering/RenderEvent.cs
new file mode 100644
index 000000000..370614399
--- /dev/null
+++ b/src/Rendering/RenderEvent.cs
@@ -0,0 +1,23 @@
+using Microsoft.AspNetCore.Components.RenderTree;
+
+namespace Bunit
+{
+ ///
+ /// Represents a render event for a or generally from the .
+ ///
+ public readonly struct RenderEvent
+ {
+ ///
+ /// Gets the related from the render.
+ ///
+ public RenderBatch RenderBatch { get; }
+
+ ///
+ /// Creates an instance of the type.
+ ///
+ public RenderEvent(in RenderBatch renderBatch)
+ {
+ RenderBatch = renderBatch;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Rendering/RenderEventPublisher.cs b/src/Rendering/RenderEventPublisher.cs
new file mode 100644
index 000000000..35a3b11d4
--- /dev/null
+++ b/src/Rendering/RenderEventPublisher.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+namespace Bunit
+{
+ ///
+ /// Represents a publisher.
+ ///
+ internal class RenderEventPublisher : IObservable
+ {
+ private readonly HashSet> _observers = new HashSet>();
+
+ public IDisposable Subscribe(IObserver observer)
+ {
+ if (!_observers.Contains(observer))
+ _observers.Add(observer);
+ return new Unsubscriber(_observers, observer);
+ }
+
+ public void OnRender(in RenderEvent renderEvent)
+ {
+ foreach (var observer in _observers)
+ {
+ observer.OnNext(renderEvent);
+ }
+ }
+
+ public void OnCompleted()
+ {
+ foreach (var observer in _observers)
+ {
+ observer.OnCompleted();
+ }
+ }
+
+ private sealed class Unsubscriber : IDisposable
+ {
+ private HashSet> _observers;
+ private IObserver _observer;
+
+ public Unsubscriber(HashSet> observers, IObserver observer)
+ {
+ _observers = observers;
+ _observer = observer;
+ }
+
+ public void Dispose()
+ {
+ _observers.Remove(_observer);
+ }
+ }
+ }
+}
diff --git a/src/Rendering/RenderedFragmentBase.cs b/src/Rendering/RenderedFragmentBase.cs
index f5db47ba2..2d5a58c22 100644
--- a/src/Rendering/RenderedFragmentBase.cs
+++ b/src/Rendering/RenderedFragmentBase.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components;
@@ -13,6 +14,7 @@ namespace Bunit
///
public abstract class RenderedFragmentBase : IRenderedFragment
{
+ private readonly RenderEventSubscriber _renderEventSubscriber;
private string? _snapshotMarkup;
private string? _latestRenderMarkup;
private INodeList? _firstRenderNodes;
@@ -69,7 +71,8 @@ public RenderedFragmentBase(ITestContext testContext, RenderFragment renderFragm
TestContext = testContext;
Container = new ContainerComponent(testContext.Renderer);
Container.Render(renderFragment);
- testContext.Renderer.OnRenderingHasComponentUpdates += ComponentMarkupChanged;
+ _renderEventSubscriber = new RenderEventSubscriber(testContext.Renderer.RenderEvents);
+ _renderEventSubscriber.OnRender = ComponentMarkupChanged;
}
///
@@ -99,9 +102,9 @@ public IReadOnlyList GetChangesSinceFirstRender()
return Nodes.CompareTo(_firstRenderNodes);
}
- private void ComponentMarkupChanged(in RenderBatch renderBatch)
+ private void ComponentMarkupChanged(RenderEvent renderBatch)
{
- if (renderBatch.HasUpdatesTo(ComponentId) || HasChildComponentUpdated(renderBatch, ComponentId))
+ if (renderBatch.RenderBatch.HasUpdatesTo(ComponentId) || HasChildComponentUpdated(renderBatch.RenderBatch, ComponentId))
{
ResetLatestRenderCache();
}
diff --git a/src/Rendering/StructAction.cs b/src/Rendering/StructAction.cs
deleted file mode 100644
index 9d0afed48..000000000
--- a/src/Rendering/StructAction.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Bunit
-{
- ///
- /// Represents an Action delegate, that allows readonly struct to be passed using
- /// the in argument modifier.
- ///
- /// The struct type.
- /// The input argument
- public delegate void StructAction(in T input) where T : struct;
-}
diff --git a/src/Rendering/TestRenderer.cs b/src/Rendering/TestRenderer.cs
index 647370559..f8bcfad48 100644
--- a/src/Rendering/TestRenderer.cs
+++ b/src/Rendering/TestRenderer.cs
@@ -3,7 +3,6 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.ExceptionServices;
@@ -17,29 +16,24 @@ namespace Bunit
[SuppressMessage("Usage", "BL0006:Do not use RenderTree types", Justification = "")]
public class TestRenderer : Renderer
{
+ private readonly RenderEventPublisher _renderEventPublisher;
private Exception? _unhandledException;
- private TaskCompletionSource
; // spaced over multiple lines, of course
+ // Failing that, we could have an C#-based representation, e.g.,
+ // new Element.P { "Some text", new Element.Div { "Child text" } }, etc.
+ builder.OpenElement(100, "p");
+ builder.AddAttribute(101, "name", "fragment-element");
+ builder.AddAttribute(102, "style", "color: red");
+ builder.AddContent(103, "This is from the fragment");
+ builder.CloseElement();
+ };
+}
diff --git a/tests/BlazorE2E/BasicTestApp/SvgCircleComponent.razor b/tests/BlazorE2E/BasicTestApp/SvgCircleComponent.razor
new file mode 100644
index 000000000..1c2849b7c
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/SvgCircleComponent.razor
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/BlazorE2E/BasicTestApp/SvgComponent.razor b/tests/BlazorE2E/BasicTestApp/SvgComponent.razor
new file mode 100644
index 000000000..2cece5930
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/SvgComponent.razor
@@ -0,0 +1,9 @@
+
+
+
+
+@code {
+ int radius = 10;
+}
diff --git a/tests/BlazorE2E/BasicTestApp/SvgWithChildComponent.razor b/tests/BlazorE2E/BasicTestApp/SvgWithChildComponent.razor
new file mode 100644
index 000000000..8ef5bd204
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/SvgWithChildComponent.razor
@@ -0,0 +1,5 @@
+
SVG with Child Component
+
+
\ No newline at end of file
diff --git a/tests/BlazorE2E/BasicTestApp/TextOnlyComponent.razor b/tests/BlazorE2E/BasicTestApp/TextOnlyComponent.razor
new file mode 100644
index 000000000..e1225d91c
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/TextOnlyComponent.razor
@@ -0,0 +1 @@
+Hello from TextOnlyComponent
\ No newline at end of file
diff --git a/tests/BlazorE2E/BasicTestApp/_Imports.razor b/tests/BlazorE2E/BasicTestApp/_Imports.razor
new file mode 100644
index 000000000..2b9557ce5
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/_Imports.razor
@@ -0,0 +1 @@
+@using Microsoft.AspNetCore.Components.Web
\ No newline at end of file
diff --git a/tests/BlazorE2E/ComponentRenderingTest.cs b/tests/BlazorE2E/ComponentRenderingTest.cs
new file mode 100644
index 000000000..051a897f0
--- /dev/null
+++ b/tests/BlazorE2E/ComponentRenderingTest.cs
@@ -0,0 +1,639 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration.Assemblies;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+using AngleSharp.Dom;
+using Bunit.BlazorE2E.BasicTestApp;
+using Bunit.BlazorE2E.BasicTestApp.HierarchicalImportsTest.Subdir;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Shouldly;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Bunit.BlazorE2E
+{
+ ///
+ /// This tests are based on the tests from the following AspNetCore tests class.
+ /// The aim is to only modify the original tests to not use Selenium, and instead use the
+ /// .
+ /// https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs
+ ///
+ public class ComponentRenderingTest : ComponentTestFixture
+ {
+ public ComponentRenderingTest(ITestOutputHelper output)
+ {
+ Services.AddXunitLogger(output);
+ }
+
+ [Fact]
+ public void CanRenderTextOnlyComponent()
+ {
+ var cut = RenderComponent();
+ Assert.Equal("Hello from TextOnlyComponent", cut.Markup);
+ }
+
+ // This verifies that we've correctly configured the Razor language version via MSBuild.
+ // See #974
+ [Fact]
+ public void CanRenderComponentWithDataDash()
+ {
+ var cut = RenderComponent();
+ var element = cut.Find("#cool_beans");
+ Assert.Equal("17", element.GetAttribute("data-tab"));
+ Assert.Equal("17", element.TextContent);
+ }
+
+ [Fact]
+ public void CanRenderComponentWithAttributes()
+ {
+ var cut = RenderComponent();
+ var styledElement = cut.Find("h1");
+ Assert.Equal("Hello, world!", styledElement.TextContent);
+ Assert.Equal("color: red;", styledElement.GetAttribute("style"));
+ Assert.Equal("somevalue", styledElement.GetAttribute("customattribute"));
+ }
+
+ [Fact]
+ public void CanTriggerEvents()
+ {
+ // Initial count is zero
+ var cut = RenderComponent();
+ var countDisplayElement = cut.Find("p");
+ Assert.Equal("Current count: 0", countDisplayElement.TextContent);
+
+ // Clicking button increments count
+ cut.Find("button").Click();
+ Assert.Equal("Current count: 1", countDisplayElement.TextContent);
+ }
+
+ [Fact]
+ public void CanTriggerAsyncEventHandlers()
+ {
+ // Initial state is stopped
+ var cut = RenderComponent();
+ var stateElement = cut.Find("#state");
+ Assert.Equal("Stopped", stateElement.TextContent);
+
+ // Clicking 'tick' changes the state, and starts a task
+ cut.Find("#tick").Click();
+ Assert.Equal("Started", stateElement.TextContent);
+
+ cut.Find("#tock").Click();
+ cut.WaitForAssertion(() => Assert.Equal("Stopped", stateElement.TextContent));
+ }
+
+ [Fact]
+ public void CanTriggerKeyPressEvents()
+ {
+ // List is initially empty
+ var cut = RenderComponent();
+ var inputElement = cut.Find("input");
+ var liElements = cut.FindAll("li", enableAutoRefresh: true);
+ liElements.ShouldBeEmpty();
+
+ // Typing adds element
+ inputElement.KeyPress("a");
+ liElements.ShouldAllBe(li => Assert.Equal("a", li.TextContent));
+
+ // Typing again adds another element
+ inputElement.KeyPress("b");
+ liElements.ShouldAllBe(
+ li => Assert.Equal("a", li.TextContent),
+ li => Assert.Equal("b", li.TextContent)
+ );
+ }
+
+ [Fact(DisplayName = "After KeyPress event is triggered, contains keys passed to KeyPress", Skip = "Issue #46 - https://github.com/egil/razor-components-testing-library/issues/46")]
+ public void Test001()
+ {
+ var cut = RenderComponent();
+
+ cut.Find("input").KeyPress("abc");
+
+ cut.Find("input").GetAttribute("value").ShouldBe("abc");
+ }
+
+ [Fact]
+ public void CanAddAndRemoveEventHandlersDynamically()
+ {
+ var cut = RenderComponent();
+ var countDisplayElement = cut.Find("p");
+ var incrementButton = cut.Find("button");
+ var toggleClickHandlerCheckbox = cut.Find("[type=checkbox]");
+
+ // Initial count is zero; clicking button increments count
+ Assert.Equal("Current count: 0", countDisplayElement.TextContent);
+ incrementButton.Click();
+ Assert.Equal("Current count: 1", countDisplayElement.TextContent);
+
+ // We can remove an event handler
+ toggleClickHandlerCheckbox.Change(false);
+ Assert.Empty(cut.FindAll("#listening-message"));
+ incrementButton.Click();
+ Assert.Equal("Current count: 1", countDisplayElement.TextContent);
+
+ // We can add an event handler
+ toggleClickHandlerCheckbox.Change(true);
+ cut.Find("#listening-message"); // throws if non is found.
+ incrementButton.Click();
+ Assert.Equal("Current count: 2", countDisplayElement.TextContent);
+ }
+
+ [Fact]
+ public void CanRenderChildComponents()
+ {
+ var cut = RenderComponent();
+ Assert.Equal("Parent component", cut.Find("fieldset > legend").TextContent);
+
+ var styledElement = cut.Find("fieldset > h1");
+ Assert.Equal("Hello, world!", styledElement.TextContent);
+ Assert.Equal("color: red;", styledElement.GetAttribute("style"));
+ Assert.Equal("somevalue", styledElement.GetAttribute("customattribute"));
+ }
+
+ [Fact(DisplayName = "Verifies we can render HTML content as a single block")]
+ public void CanRenderChildContent_StaticHtmlBlock()
+ {
+ var cut = RenderComponent();
+ Assert.Equal("
Some-Static-Text
", cut.Find("#foo").InnerHtml);
+ }
+
+ [Fact(DisplayName = "Verifies we can rewite more complex HTML content into blocks")]
+ public void CanRenderChildContent_MixedHtmlBlock()
+ {
+ var cut = RenderComponent();
+
+ var one = cut.Find("#one");
+ Assert.Equal("
Some-Static-Text
", one.InnerHtml);
+
+ var two = cut.Find("#two");
+ Assert.Equal("More-Static-Text", two.InnerHtml);
+
+ var three = cut.Find("#three");
+ Assert.Equal("Some-Dynamic-Text", three.InnerHtml);
+
+ var four = cut.Find("#four");
+ Assert.Equal("But this is static", four.InnerHtml);
+ }
+
+ [Fact(DisplayName = "Verifies we can rewrite HTML blocks with encoded HTML")]
+ public void CanRenderChildContent_EncodedHtmlInBlock()
+ {
+ var cut = RenderComponent();
+
+ var one = cut.Find("#one");
+ Assert.Equal("
Some-Static-Text
", one.InnerHtml);
+
+ var two = cut.Find("#two");
+ Assert.Equal("<span>More-Static-Text</span>", two.InnerHtml);
+
+ var three = cut.Find("#three");
+ Assert.Equal("Some-Dynamic-Text", three.InnerHtml);
+
+ var four = cut.Find("#four");
+ Assert.Equal("But this is static", four.InnerHtml);
+ }
+
+ [Fact]
+ public void CanTriggerEventsOnChildComponents()
+ {
+ // Counter is displayed as child component. Initial count is zero.
+ var cut = RenderComponent();
+
+ // Clicking increments count in child component
+ cut.Find("button").Click();
+
+ Assert.Equal("Current count: 1", cut.Find("h1+p").TextContent);
+ }
+
+ [Fact]
+ public void ChildComponentsRerenderWhenPropertiesChanged()
+ {
+ // Count value is displayed in child component with initial value zero
+ var cut = RenderComponent();
+ var wholeCounterElement = cut.Find("p");
+ var messageElementInChild = cut.Find("p .message");
+ Assert.Equal("Current count: 0", wholeCounterElement.TextContent);
+ Assert.Equal("0", messageElementInChild.TextContent);
+
+ // Clicking increments count in child element
+ cut.Find("button").Click();
+ Assert.Equal("1", messageElementInChild.TextContent);
+ }
+
+ [Fact]
+ public void CanAddAndRemoveChildComponentsDynamically()
+ {
+ // Initially there are zero child components
+ var cut = RenderComponent();
+ var addButton = cut.Find(".addChild");
+ var removeButton = cut.Find(".removeChild");
+ Assert.Empty(cut.FindAll("p"));
+
+ // Click to add/remove some child components
+ addButton.Click();
+ Assert.Collection(cut.FindAll("p .message"),
+ msg => Assert.Equal("Child 1", msg.TextContent));
+
+ addButton.Click();
+ Assert.Collection(cut.FindAll("p .message"),
+ msg => Assert.Equal("Child 1", msg.TextContent),
+ msg => Assert.Equal("Child 2", msg.TextContent));
+
+ removeButton.Click();
+ Assert.Collection(cut.FindAll("p .message"),
+ msg => Assert.Equal("Child 1", msg.TextContent));
+
+ addButton.Click();
+ Assert.Collection(cut.FindAll("p .message"),
+ msg => Assert.Equal("Child 1", msg.TextContent),
+ msg => Assert.Equal("Child 3", msg.TextContent));
+ }
+
+ [Fact]
+ public void ChildComponentsNotifiedWhenPropertiesChanged()
+ {
+ // Child component receives notification that lets it compute a property before first render
+ var cut = RenderComponent();
+ var suppliedValueElement = cut.Find(".supplied");
+ var computedValueElement = cut.Find(".computed");
+ var incrementButton = cut.Find("button");
+ Assert.Equal("You supplied: 100", suppliedValueElement.TextContent);
+ Assert.Equal("I computed: 200", computedValueElement.TextContent);
+
+ // When property changes, child is renotified before rerender
+ incrementButton.Click();
+ Assert.Equal("You supplied: 101", suppliedValueElement.TextContent);
+ Assert.Equal("I computed: 202", computedValueElement.TextContent);
+ }
+
+ [Fact]
+ public void CanRenderFragmentsWhilePreservingSurroundingElements()
+ {
+ // Initially, the region isn't shown
+ var cut = RenderComponent();
+ var originalButton = cut.Find("button");
+
+ var fragmentElements = cut.FindAll("p[name=fragment-element]", enableAutoRefresh: true);
+ Assert.Empty(fragmentElements);
+
+ // The JS-side DOM builder handles regions correctly, placing elements
+ // after the region after the corresponding elements
+ Assert.Equal("The end", cut.Find("div > *:last-child").TextContent);
+
+ // When we click the button, the region is shown
+ originalButton.Click();
+ fragmentElements.Single();
+
+ // The button itself was preserved, so we can click it again and see the effect
+ originalButton.Click();
+ Assert.Empty(fragmentElements);
+ }
+
+ [Fact]
+ public void CanUseViewImportsHierarchically()
+ {
+ // The component is able to compile and output these type names only because
+ // of the _ViewImports.cshtml files at the same and ancestor levels
+ var cut = RenderComponent();
+ Assert.Collection(cut.FindAll("p"),
+ elem => Assert.Equal(typeof(Complex).FullName, elem.TextContent),
+ elem => Assert.Equal(typeof(AssemblyHashAlgorithm).FullName, elem.TextContent));
+ }
+
+ [Fact(Skip = "Test doesn't make sense in this context")]
+ public void CanUseComponentAndStaticContentFromExternalNuGetPackage()
+ {
+ //var appElement = Browser.MountTestComponent();
+
+ //// NuGet packages can use JS interop features to provide
+ //// .NET code access to browser APIs
+ //var showPromptButton = appElement.FindElements(By.TagName("button")).First();
+ //showPromptButton.Click();
+
+ //var modal = new WebDriverWait(Browser, TimeSpan.FromSeconds(3))
+ // .Until(SwitchToAlert);
+ //modal.SendKeys("Some value from test");
+ //modal.Accept();
+ //var promptResult = appElement.FindElement(By.TagName("strong"));
+ //Browser.Equal("Some value from test", () => promptResult.Text);
+
+ //// NuGet packages can also embed entire components (themselves
+ //// authored as Razor files), including static content. The CSS value
+ //// here is in a .css file, so if it's correct we know that static content
+ //// file was loaded.
+ //var specialStyleDiv = appElement.FindElement(By.ClassName("special-style"));
+ //Assert.Equal("50px", specialStyleDiv.GetCssValue("padding"));
+
+ //// The external components are fully functional, not just static HTML
+ //var externalComponentButton = specialStyleDiv.FindElement(By.TagName("button"));
+ //Assert.Equal("Click me", externalComponentButton.Text);
+ //externalComponentButton.Click();
+ //Browser.Equal("It works", () => externalComponentButton.Text);
+ }
+
+ [Fact]
+ public void CanRenderSvgWithCorrectNamespace()
+ {
+ var cut = RenderComponent();
+
+ var svgElement = cut.Find("svg");
+ Assert.NotNull(svgElement);
+
+ var svgCircleElement = cut.Find("svg circle");
+ Assert.NotNull(svgCircleElement);
+ Assert.Equal("10", svgCircleElement.GetAttribute("r"));
+
+ cut.Find("button").Click();
+ Assert.Equal("20", svgCircleElement.GetAttribute("r"));
+ }
+
+ [Fact]
+ public void CanRenderSvgChildComponentWithCorrectNamespace()
+ {
+ var cut = RenderComponent();
+
+ var svgElement = cut.Find("svg");
+ Assert.NotNull(svgElement);
+
+ var svgCircleElement = cut.Find("svg circle");
+ Assert.NotNull(svgCircleElement);
+ }
+
+ [Fact]
+ public void LogicalElementInsertionWorksHierarchically()
+ {
+ var cut = RenderComponent();
+ cut.MarkupMatches("First Second Third");
+ }
+
+ [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ public void CanUseJsInteropToReferenceElements()
+ {
+ //var cut = RenderComponent();
+ //var inputElement = cut.Find("#capturedElement");
+ //var buttonElement = cut.Find("button");
+
+ //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
+
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 1", inputElement.GetAttribute("value"));
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 2", inputElement.GetAttribute("value"));
+ }
+
+ [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ public void CanCaptureReferencesToDynamicallyAddedElements()
+ {
+ //var cut = RenderComponent();
+ //var buttonElement = cut.Find("button");
+ //var checkbox = cut.Find("input[type=checkbox]");
+
+ //// We're going to remove the input. But first, put in some contents
+ //// so we can observe it's not the same instance later
+ //cut.Find("#capturedElement").SendKeys("some text");
+
+ //// Remove the captured element
+ //checkbox.Click();
+ //Browser.Empty(() => cut.FindAll("#capturedElement"));
+
+ //// Re-add it; observe it starts empty again
+ //checkbox.Click();
+ //var inputElement = cut.Find("#capturedElement");
+ //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
+
+ //// See that the capture variable was automatically updated to reference the new instance
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 1", () => inputElement.GetAttribute("value"));
+ }
+
+ [Fact]
+ public void CanCaptureReferencesToDynamicallyAddedComponents()
+ {
+ var cut = RenderComponent();
+ var incrementButtonSelector = "#child-component button";
+ var currentCountTextSelector = "#child-component p:first-of-type";
+ var resetButton = cut.Find("#reset-child");
+ var toggleChildCheckbox = cut.Find("#toggle-child");
+ Func currentCountText = () => cut.Find(currentCountTextSelector).TextContent;
+
+ // Verify the reference was captured initially
+ cut.Find(incrementButtonSelector).Click();
+ Assert.Equal("Current count: 1", currentCountText());
+ resetButton.Click();
+ Assert.Equal("Current count: 0", currentCountText());
+ cut.Find(incrementButtonSelector).Click();
+ Assert.Equal("Current count: 1", currentCountText());
+
+ // Remove and re-add a new instance of the child, checking the text was reset
+ toggleChildCheckbox.Change(false);
+ Assert.Empty(cut.FindAll(incrementButtonSelector));
+ toggleChildCheckbox.Change(true);
+ Assert.Equal("Current count: 0", currentCountText());
+
+ // Verify we have a new working reference
+ cut.Find(incrementButtonSelector).Click();
+ Assert.Equal("Current count: 1", currentCountText());
+ resetButton.Click();
+ Assert.Equal("Current count: 0", currentCountText());
+ }
+
+ //[Fact]
+ //public void CanUseJsInteropForRefElementsDuringOnAfterRender()
+ //{
+ // var cut = RenderComponent();
+ // Assert.Equal("Value set after render", () => Browser.Find("input").GetAttribute("value"));
+ //}
+
+ //[Fact]
+ //public void CanRenderMarkupBlocks()
+ //{
+ // var cut = RenderComponent();
+
+ // // Static markup
+ // Assert.Equal(
+ // "attributes",
+ // cut.FindElement(By.CssSelector("p span#attribute-example")).TextContent);
+
+ // // Dynamic markup (from a custom RenderFragment)
+ // Assert.Equal(
+ // "[Here is an example. We support multiple-top-level nodes.]",
+ // cut.Find("#dynamic-markup-block").TextContent);
+ // Assert.Equal(
+ // "example",
+ // cut.FindElement(By.CssSelector("#dynamic-markup-block strong#dynamic-element em")).TextContent);
+
+ // // Dynamic markup (from a MarkupString)
+ // Assert.Equal(
+ // "This is a markup string.",
+ // cut.FindElement(By.ClassName("markup-string-value")).TextContent);
+ // Assert.Equal(
+ // "markup string",
+ // cut.Find(".markup-string-value em").TextContent);
+
+ // // Updating markup blocks
+ // cut.Find("button").Click();
+ // Browser.Equal(
+ // "[The output was changed completely.]",
+ // () => cut.Find("#dynamic-markup-block").TextContent);
+ // Assert.Equal(
+ // "changed",
+ // cut.FindElement(By.CssSelector("#dynamic-markup-block span em")).TextContent);
+ //}
+
+ //[Fact]
+ //public void CanRenderRazorTemplates()
+ //{
+ // var cut = RenderComponent();
+
+ // // code block template (component parameter)
+ // var element = cut.FindElement(By.CssSelector("div#codeblocktemplate ol"));
+ // Assert.Collection(
+ // element.FindAll("li"),
+ // e => Assert.Equal("#1 - a", e.TextContent),
+ // e => Assert.Equal("#2 - b", e.TextContent),
+ // e => Assert.Equal("#3 - c", e.TextContent));
+ //}
+
+ //[Fact]
+ //public void CanRenderMultipleChildContent()
+ //{
+ // var cut = RenderComponent();
+
+ // var table = cut.Find("table");
+
+ // var thead = table.Find("thead");
+ // Assert.Collection(
+ // thead.FindAll("th"),
+ // e => Assert.Equal("Col1", e.TextContent),
+ // e => Assert.Equal("Col2", e.TextContent),
+ // e => Assert.Equal("Col3", e.TextContent));
+
+ // var tfoot = table.Find("tfoot");
+ // Assert.Empty(tfoot.FindAll("td"));
+
+ // var toggle = cut.Find("#toggle");
+ // toggle.Click();
+
+ // Browser.Collection(
+ // () => tfoot.FindAll("td"),
+ // e => Assert.Equal("The", e.TextContent),
+ // e => Assert.Equal("", e.TextContent),
+ // e => Assert.Equal("End", e.TextContent));
+ //}
+
+ //[Fact]
+ //public async Task CanAcceptSimultaneousRenderRequests()
+ //{
+ // var expectedOutput = string.Join(
+ // string.Empty,
+ // Enumerable.Range(0, 100).Select(_ => "😊"));
+
+ // var cut = RenderComponent();
+
+ // // It's supposed to pause the rendering for this long. The WaitAssert below
+ // // allows it to take up extra time if needed.
+ // await Task.Delay(1000);
+
+ // var outputElement = cut.Find("#concurrent-render-output");
+ // Assert.Equal(expectedOutput, () => outputElement.TextContent);
+ //}
+
+ //[Fact]
+ //public void CanDispatchRenderToSyncContext()
+ //{
+ // var cut = RenderComponent();
+ // var result = cut.Find("#result");
+
+ // cut.Find("#run-with-dispatch").Click();
+
+ // Assert.Equal("Success (completed synchronously)", () => result.TextContent);
+ //}
+
+ //[Fact]
+ //public void CanDoubleDispatchRenderToSyncContext()
+ //{
+ // var cut = RenderComponent();
+ // var result = cut.Find("#result");
+
+ // cut.Find("#run-with-double-dispatch").Click();
+
+ // Assert.Equal("Success (completed synchronously)", () => result.TextContent);
+ //}
+
+ //[Fact]
+ //public void CanDispatchAsyncWorkToSyncContext()
+ //{
+ // var cut = RenderComponent();
+ // var result = cut.Find("#result");
+
+ // cut.Find("#run-async-with-dispatch").Click();
+
+ // Assert.Equal("First Second Third Fourth Fifth", () => result.TextContent);
+ //}
+
+ //[Fact]
+ //public void CanPerformInteropImmediatelyOnComponentInsertion()
+ //{
+ // var cut = RenderComponent();
+ // Assert.Equal("Hello from interop call", () => cut.Find("#val-get-by-interop").TextContent);
+ // Assert.Equal("Hello from interop call", () => cut.Find("#val-set-by-interop").GetAttribute("value"));
+ //}
+
+ //[Fact]
+ //public void CanUseAddMultipleAttributes()
+ //{
+ // var cut = RenderComponent();
+
+ // var selector = By.CssSelector("#duplicate-on-element > div");
+ // Browser.Exists(selector);
+
+ // var element = cut.FindElement(selector);
+ // Assert.Equal(string.Empty, element.GetAttribute("bool")); // attribute is present
+ // Assert.Equal("middle-value", element.GetAttribute("string"));
+ // Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
+
+ // selector = By.CssSelector("#duplicate-on-element-override > div");
+ // element = cut.FindElement(selector);
+ // Assert.Null(element.GetAttribute("bool")); // attribute is not present
+ // Assert.Equal("other-text", element.GetAttribute("string"));
+ // Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
+ //}
+
+ //[Fact]
+ //public void CanPatchRenderTreeToMatchLatestDOMState()
+ //{
+ // var cut = RenderComponent();
+ // var incompleteItemsSelector = By.CssSelector(".incomplete-items li");
+ // var completeItemsSelector = By.CssSelector(".complete-items li");
+ // Browser.Exists(incompleteItemsSelector);
+
+ // // Mark first item as done; observe the remaining incomplete item appears unchecked
+ // // because the diff algorithm explicitly unchecks it
+ // cut.Find(".incomplete-items .item-isdone").Click();
+ // Browser.True(() =>
+ // {
+ // var incompleteLIs = cut.FindElements(incompleteItemsSelector);
+ // return incompleteLIs.Count == 1
+ // && !incompleteLIs[0].Find(".item-isdone").Selected;
+ // });
+
+ // // Mark first done item as not done; observe the remaining complete item appears checked
+ // // because the diff algorithm explicitly re-checks it
+ // cut.Find(".complete-items .item-isdone").Click();
+ // Browser.True(() =>
+ // {
+ // var completeLIs = cut.FindElements(completeItemsSelector);
+ // return completeLIs.Count == 2
+ // && completeLIs[0].Find(".item-isdone").Selected;
+ // });
+ //}
+
+ }
+}
diff --git a/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
index dab70c9bf..abd6bf4fc 100644
--- a/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
@@ -41,7 +41,7 @@ public void Test002()
var elmMock = new Mock();
elmMock.Setup(x => x.GetAttribute(It.IsAny())).Returns(() => null!);
- Should.Throw(() => elmMock.Object.TriggerEventAsync("click", EventArgs.Empty));
+ Should.Throw(() => elmMock.Object.Click());
}
[Fact(DisplayName = "TriggerEventAsync throws if element was not rendered through blazor (has a TestRendere in its context)")]
diff --git a/tests/GlobalSuppressions.cs b/tests/GlobalSuppressions.cs
index d61cd78fc..c0a1020ee 100644
--- a/tests/GlobalSuppressions.cs
+++ b/tests/GlobalSuppressions.cs
@@ -6,3 +6,4 @@
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "In tests its ok to catch the general exception type")]
[assembly: SuppressMessage("Usage", "CA2234:Pass system uri objects instead of strings", Justification = "")]
[assembly: SuppressMessage("Usage", "BL0006:Do not use RenderTree types", Justification = "")]
+[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")]
diff --git a/tests/RefreshableQueryCollectionTest.cs b/tests/RefreshableQueryCollectionTest.cs
new file mode 100644
index 000000000..f96c249da
--- /dev/null
+++ b/tests/RefreshableQueryCollectionTest.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Bunit.SampleComponents;
+using Shouldly;
+using Xunit;
+
+namespace Bunit
+{
+ public class RefreshableQueryCollectionTest : ComponentTestFixture
+ {
+ [Fact(DisplayName = "When the query returns no elements, the collection is empty")]
+ public void Test001()
+ {
+ var cut = RenderComponent();
+
+ var sut = new RefreshableElementCollection(cut, ".foo");
+
+ sut.ShouldBeEmpty();
+ }
+
+ [Fact(DisplayName = "When the query returns elements, the collection contains those elements")]
+ public void Test002()
+ {
+ var cut = RenderComponent();
+
+ var sut = new RefreshableElementCollection(cut, "h1");
+
+ sut.Count.ShouldBe(1);
+ sut[0].TagName.ShouldBe("H1");
+ }
+
+
+ [Fact(DisplayName = "When Refresh is called, the query is run again and new elements are made available")]
+ public void Test003()
+ {
+ var cut = RenderComponent();
+ var sut = new RefreshableElementCollection(cut, "li");
+ sut.Count.ShouldBe(0);
+
+ cut.Find("button").Click();
+
+ sut.Refresh();
+ sut.Count.ShouldBe(1);
+ }
+
+ [Fact(DisplayName = "Enabling auto refresh automatically refreshes query when the rendered fragment renders and has changes")]
+ public void Test004()
+ {
+ var cut = RenderComponent();
+ var sut = new RefreshableElementCollection(cut, "li") { EnableAutoRefresh = true };
+ sut.Count.ShouldBe(0);
+
+ cut.Find("button").Click();
+
+ sut.Count.ShouldBe(1);
+ }
+
+ [Fact(DisplayName = "Disabling auto refresh turns off automatic refreshing queries on when rendered fragment changes")]
+ public void Test005()
+ {
+ var cut = RenderComponent();
+ var sut = new RefreshableElementCollection(cut, "li") { EnableAutoRefresh = true };
+
+ sut.EnableAutoRefresh = false;
+
+ cut.Find("button").Click();
+
+ sut.Count.ShouldBe(0);
+ }
+ }
+}
diff --git a/tests/SampleComponents/ClickAddsLi.razor b/tests/SampleComponents/ClickAddsLi.razor
new file mode 100644
index 000000000..d50d19b92
--- /dev/null
+++ b/tests/SampleComponents/ClickAddsLi.razor
@@ -0,0 +1,10 @@
+
+ @foreach (var x in Enumerable.Range(0, Counter))
+ {
+
@x
+ }
+
+
+@code {
+ public int Counter { get; set; } = 0;
+}
\ No newline at end of file
diff --git a/tests/SampleComponents/Simple1.razor b/tests/SampleComponents/Simple1.razor
index 5dd10f31a..a59cab706 100644
--- a/tests/SampleComponents/Simple1.razor
+++ b/tests/SampleComponents/Simple1.razor
@@ -2,4 +2,4 @@
[Parameter] public string Header { get; set; } = string.Empty;
[Parameter] public string AttrValue { get; set; } = string.Empty;
}
-
@Header
\ No newline at end of file
+
@Header
\ No newline at end of file
diff --git a/tests/TestRendererTest.cs b/tests/TestRendererTest.cs
index 0a260a227..f82998372 100644
--- a/tests/TestRendererTest.cs
+++ b/tests/TestRendererTest.cs
@@ -26,5 +26,45 @@ public void Test001()
res.RenderCount.ShouldBe(2);
}
+ [Fact(DisplayName = "Can pass reference to elements to JsInterop")]
+ public void Test010()
+ {
+ //var cut = RenderComponent();
+ //var inputElement = cut.Find("#capturedElement");
+ //var buttonElement = cut.Find("button");
+
+ //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
+
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 1", inputElement.GetAttribute("value"));
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 2", inputElement.GetAttribute("value"));
+ }
+
+ [Fact(DisplayName = "Can capture reference to dynamically added elements and pass to JsInterop")]
+ public void Test011()
+ {
+ //var cut = RenderComponent();
+ //var buttonElement = cut.Find("button");
+ //var checkbox = cut.Find("input[type=checkbox]");
+
+ //// We're going to remove the input. But first, put in some contents
+ //// so we can observe it's not the same instance later
+ //cut.Find("#capturedElement").SendKeys("some text");
+
+ //// Remove the captured element
+ //checkbox.Click();
+ //Browser.Empty(() => cut.FindAll("#capturedElement"));
+
+ //// Re-add it; observe it starts empty again
+ //checkbox.Click();
+ //var inputElement = cut.Find("#capturedElement");
+ //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
+
+ //// See that the capture variable was automatically updated to reference the new instance
+ //buttonElement.Click();
+ //Assert.Equal("Clicks: 1", () => inputElement.GetAttribute("value"));
+ }
+
}
}
From b87c312a0002849165484cf688c582cd306cbe28 Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 09:01:43 +0000
Subject: [PATCH 22/27] Using cleanup
---
src/Asserting/CollectionAssertExtensions.cs | 1 -
src/Asserting/DiffAssertExtensions.cs | 4 -
src/Asserting/HtmlEqualException.cs | 2 -
.../ShouldBeAdditionAssertExtensions.cs | 1 -
.../ShouldBeRemovalAssertExtensions.cs | 1 -
.../ShouldBeTextChangeAssertExtensions.cs | 1 -
src/ComponentTestFixture.cs | 4 -
src/Components/ComponentUnderTest.cs | 3 +-
src/Components/ContainerComponent.cs | 3 -
src/Components/FixtureFailedException.cs | 4 -
src/Components/SnapshotTest.cs | 3 -
src/Components/TestComponentBase.cs | 4 -
src/Components/TestContextAdapter.cs | 1 -
src/Diffing/BlazorDiffingHelpers.cs | 3 +-
src/Diffing/HtmlComparer.cs | 2 -
src/Diffing/TestHtmlParser.cs | 1 -
.../ClipboardEventDispatchExtensions.cs | 3 -
.../DragEventDispatchExtensions.cs | 6 +-
.../FocusEventDispatchExtensions.cs | 1 -
.../GeneralEventDispatchExtensions.cs | 6 +-
.../InputEventDispatchExtensions.cs | 3 -
.../KeyboardEventDispatchExtensions.cs | 6 +-
.../MediaEventDispatchExtensions.cs | 3 -
.../MissingEventHandlerException.cs | 7 +-
.../MouseEventDispatchExtensions.cs | 6 +-
.../PointerEventDispatchExtensions.cs | 6 +-
.../ProgressEventDispatchExtensions.cs | 6 +-
.../TouchEventDispatchExtensions.cs | 3 -
src/Extensions/ElementNotFoundException.cs | 5 -
.../Internal/AngleSharpExtensions.cs | 10 +-
src/Extensions/Internal/BlazorExtensions.cs | 2 -
src/Extensions/Internal/TimeSpanExtensions.cs | 4 -
src/Extensions/NodePrintExtensions.cs | 2 -
.../RenderWaitingHelperExtensions.cs | 2 -
.../RenderedFragmentQueryExtensions.cs | 4 -
src/Extensions/Xunit/XunitLogger.cs | 4 -
src/Extensions/Xunit/XunitLoggerProvider.cs | 7 +-
src/ITestContext.cs | 1 -
.../JsInvokeCountExpectedException.cs | 4 +-
.../JSInterop/JsRuntimePlannedInvocation.cs | 1 -
.../MissingMockJsRuntimeException.cs | 3 -
.../JSInterop/MockJsRuntimeExtensions.cs | 1 -
.../JSInterop/MockJsRuntimeInvokeHandler.cs | 1 -
src/Mocking/JSInterop/PlaceholderJsRuntime.cs | 6 +-
.../UnplannedJsInvocationException.cs | 2 -
src/Rendering/ComponentParameter.cs | 4 -
src/Rendering/IRenderedComponent.cs | 3 +-
src/Rendering/IRenderedFragment.cs | 3 -
src/Rendering/RenderedComponent.cs | 2 -
src/Rendering/RenderedFragment.cs | 8 +-
src/Rendering/RenderedFragmentBase.cs | 3 -
src/Rendering/TestRenderer.cs | 3 -
src/SnapshotTestContext.cs | 1 -
src/TestContext.cs | 5 -
src/bunit.csproj | 2 +-
.../CompareToDiffingExtensionsTest.cs | 3 -
tests/Asserting/DiffAssertExtensionsTest.cs | 4 -
.../GenericCollectionAssertExtensionsTest.cs | 4 -
.../BasicTestApp/ConcurrentRenderChild.razor | 16 +
.../BasicTestApp/ConcurrentRenderParent.razor | 7 +
.../BasicTestApp/DispatchingComponent.razor | 83 ++++
.../DuplicateAttributesComponent.razor | 22 ++
...ateAttributesOnElementChildComponent.razor | 24 ++
.../BasicTestApp/MarkupBlockComponent.razor | 45 +++
.../MovingCheckboxesComponent.razor | 57 +++
.../BasicTestApp/MultipleChildContent.razor | 33 ++
.../BlazorE2E/BasicTestApp/OrderedList.razor | 25 ++
.../BasicTestApp/RazorTemplates.razor | 10 +
.../BasicTestApp/TemplatedTable.razor | 36 ++
tests/BlazorE2E/ComponentRenderingTest.cs | 365 ++++++++----------
tests/ComponentTestFixtureTest.cs | 2 -
.../ClipboardEventDispatchExtensionsTest.cs | 9 +-
.../EventDispatchExtensionsTest.cs | 1 -
.../FocusEventDispatchExtensionsTest.cs | 1 -
.../GeneralEventDispatchExtensionsTest.cs | 3 -
.../MediaEventDispatchExtensionsTest.cs | 1 -
.../PointerEventDispatchExtensionsTest.cs | 6 +-
.../ProgressEventDispatchExtensionsTest.cs | 6 +-
.../TouchEventDispatchExtensionsTest.cs | 6 +-
.../TriggerEventSpy.cs | 1 -
.../JsRuntimeAssertExtensionsTest.cs | 2 -
.../JSInterop/JsRuntimeInvocationTest.cs | 3 -
.../MockJsRuntimeInvokeHandlerTest.cs | 2 -
tests/RefreshableQueryCollectionTest.cs | 7 +-
tests/Rendering/ComponentParameterTest.cs | 3 -
tests/Rendering/RenderEventPubSubTest.cs | 5 -
.../RenderWaitingHelperExtensionsTest.cs | 4 -
tests/Rendering/RenderedFragmentTest.cs | 11 +-
tests/SampleComponents/Data/AsyncNameDep.cs | 6 +-
tests/SampleComponents/Data/IAsyncTestDep.cs | 6 +-
tests/SampleComponents/Data/ITestDep.cs | 8 +-
tests/SampleComponents/TriggerTester.cs | 4 -
tests/TestRendererTest.cs | 9 +-
tests/TestServiceProviderTest.cs | 6 +-
94 files changed, 554 insertions(+), 479 deletions(-)
create mode 100644 tests/BlazorE2E/BasicTestApp/ConcurrentRenderChild.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/ConcurrentRenderParent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/DispatchingComponent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/DuplicateAttributesComponent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/DuplicateAttributesOnElementChildComponent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/MarkupBlockComponent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/MovingCheckboxesComponent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/MultipleChildContent.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/OrderedList.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/RazorTemplates.razor
create mode 100644 tests/BlazorE2E/BasicTestApp/TemplatedTable.razor
diff --git a/src/Asserting/CollectionAssertExtensions.cs b/src/Asserting/CollectionAssertExtensions.cs
index 8f8fdf8b3..17d3a7968 100644
--- a/src/Asserting/CollectionAssertExtensions.cs
+++ b/src/Asserting/CollectionAssertExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Xunit;
using Xunit.Sdk;
diff --git a/src/Asserting/DiffAssertExtensions.cs b/src/Asserting/DiffAssertExtensions.cs
index 73c9c2fc9..2bbefeff7 100644
--- a/src/Asserting/DiffAssertExtensions.cs
+++ b/src/Asserting/DiffAssertExtensions.cs
@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Text;
-using System.Threading.Tasks;
-using AngleSharp;
using AngleSharp.Diffing.Core;
using Xunit;
diff --git a/src/Asserting/HtmlEqualException.cs b/src/Asserting/HtmlEqualException.cs
index 276c8436d..506a6ea2d 100644
--- a/src/Asserting/HtmlEqualException.cs
+++ b/src/Asserting/HtmlEqualException.cs
@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
using System.Linq;
using AngleSharp;
using AngleSharp.Diffing.Core;
-using AngleSharp.Dom;
using Bunit;
namespace Xunit.Sdk
diff --git a/src/Asserting/ShouldBeAdditionAssertExtensions.cs b/src/Asserting/ShouldBeAdditionAssertExtensions.cs
index d00d3499f..c50a5c455 100644
--- a/src/Asserting/ShouldBeAdditionAssertExtensions.cs
+++ b/src/Asserting/ShouldBeAdditionAssertExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using AngleSharp;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Bunit.Diffing;
diff --git a/src/Asserting/ShouldBeRemovalAssertExtensions.cs b/src/Asserting/ShouldBeRemovalAssertExtensions.cs
index 38c1b303c..06d215b6f 100644
--- a/src/Asserting/ShouldBeRemovalAssertExtensions.cs
+++ b/src/Asserting/ShouldBeRemovalAssertExtensions.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using AngleSharp;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Bunit.Diffing;
diff --git a/src/Asserting/ShouldBeTextChangeAssertExtensions.cs b/src/Asserting/ShouldBeTextChangeAssertExtensions.cs
index 36b842df9..8b74e92e8 100644
--- a/src/Asserting/ShouldBeTextChangeAssertExtensions.cs
+++ b/src/Asserting/ShouldBeTextChangeAssertExtensions.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using AngleSharp;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Bunit.Diffing;
diff --git a/src/ComponentTestFixture.cs b/src/ComponentTestFixture.cs
index d2e70de27..95200128b 100644
--- a/src/ComponentTestFixture.cs
+++ b/src/ComponentTestFixture.cs
@@ -1,11 +1,7 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
-using Xunit.Abstractions;
using EC = Microsoft.AspNetCore.Components.EventCallback;
namespace Bunit
diff --git a/src/Components/ComponentUnderTest.cs b/src/Components/ComponentUnderTest.cs
index 2a444819b..86dab541b 100644
--- a/src/Components/ComponentUnderTest.cs
+++ b/src/Components/ComponentUnderTest.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Bunit
diff --git a/src/Components/ContainerComponent.cs b/src/Components/ContainerComponent.cs
index 60fc76d42..2480a4099 100644
--- a/src/Components/ContainerComponent.cs
+++ b/src/Components/ContainerComponent.cs
@@ -2,11 +2,8 @@
using Microsoft.AspNetCore.Components.RenderTree;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Threading.Tasks;
-using AngleSharp.Css.Dom;
namespace Bunit
{
diff --git a/src/Components/FixtureFailedException.cs b/src/Components/FixtureFailedException.cs
index 9803d7842..70eb2ab8e 100644
--- a/src/Components/FixtureFailedException.cs
+++ b/src/Components/FixtureFailedException.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Bunit;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/Components/SnapshotTest.cs b/src/Components/SnapshotTest.cs
index 68f06c058..a39db2d76 100644
--- a/src/Components/SnapshotTest.cs
+++ b/src/Components/SnapshotTest.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
diff --git a/src/Components/TestComponentBase.cs b/src/Components/TestComponentBase.cs
index f836ee961..56f54f9fe 100644
--- a/src/Components/TestComponentBase.cs
+++ b/src/Components/TestComponentBase.cs
@@ -1,17 +1,13 @@
using System;
using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
-using Bunit.Diffing;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
-using Xunit.Abstractions;
using Xunit.Sdk;
namespace Bunit
diff --git a/src/Components/TestContextAdapter.cs b/src/Components/TestContextAdapter.cs
index 5f3082a2a..cec0ed727 100644
--- a/src/Components/TestContextAdapter.cs
+++ b/src/Components/TestContextAdapter.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using AngleSharp.Dom;
-using Bunit.Diffing;
using Microsoft.AspNetCore.Components;
namespace Bunit
diff --git a/src/Diffing/BlazorDiffingHelpers.cs b/src/Diffing/BlazorDiffingHelpers.cs
index daed7dc01..10335862f 100644
--- a/src/Diffing/BlazorDiffingHelpers.cs
+++ b/src/Diffing/BlazorDiffingHelpers.cs
@@ -1,5 +1,4 @@
-using System;
-using AngleSharp.Diffing.Core;
+using AngleSharp.Diffing.Core;
namespace Bunit.Diffing
{
diff --git a/src/Diffing/HtmlComparer.cs b/src/Diffing/HtmlComparer.cs
index be7e51019..735609522 100644
--- a/src/Diffing/HtmlComparer.cs
+++ b/src/Diffing/HtmlComparer.cs
@@ -3,8 +3,6 @@
using AngleSharp.Diffing;
using AngleSharp.Dom;
using AngleSharp.Diffing.Core;
-using Xunit.Abstractions;
-using Bunit.Diffing;
using AngleSharpWrappers;
namespace Bunit.Diffing
diff --git a/src/Diffing/TestHtmlParser.cs b/src/Diffing/TestHtmlParser.cs
index 13d7076cf..98d438c01 100644
--- a/src/Diffing/TestHtmlParser.cs
+++ b/src/Diffing/TestHtmlParser.cs
@@ -2,7 +2,6 @@
using AngleSharp;
using AngleSharp.Dom;
using System;
-using Bunit.Diffing;
namespace Bunit.Diffing
{
diff --git a/src/EventDispatchExtensions/ClipboardEventDispatchExtensions.cs b/src/EventDispatchExtensions/ClipboardEventDispatchExtensions.cs
index d0880ec91..9174ba27d 100644
--- a/src/EventDispatchExtensions/ClipboardEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/ClipboardEventDispatchExtensions.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/DragEventDispatchExtensions.cs b/src/EventDispatchExtensions/DragEventDispatchExtensions.cs
index 2b6b23dae..b5ea59508 100644
--- a/src/EventDispatchExtensions/DragEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/DragEventDispatchExtensions.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/FocusEventDispatchExtensions.cs b/src/EventDispatchExtensions/FocusEventDispatchExtensions.cs
index 9705f8269..5a031f0bc 100644
--- a/src/EventDispatchExtensions/FocusEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/FocusEventDispatchExtensions.cs
@@ -1,5 +1,4 @@
using AngleSharp.Dom;
-using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.Threading.Tasks;
diff --git a/src/EventDispatchExtensions/GeneralEventDispatchExtensions.cs b/src/EventDispatchExtensions/GeneralEventDispatchExtensions.cs
index a3f9b6c8f..3f1e10d6e 100644
--- a/src/EventDispatchExtensions/GeneralEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/GeneralEventDispatchExtensions.cs
@@ -1,12 +1,8 @@
-using AngleSharp;
-using AngleSharp.Dom;
-using Microsoft.AspNetCore.Components;
+using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.RenderTree;
-using Microsoft.AspNetCore.Components.Web;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
-using System.Linq;
using System.Threading.Tasks;
namespace Bunit
diff --git a/src/EventDispatchExtensions/InputEventDispatchExtensions.cs b/src/EventDispatchExtensions/InputEventDispatchExtensions.cs
index e94af59f3..aa112fe60 100644
--- a/src/EventDispatchExtensions/InputEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/InputEventDispatchExtensions.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components;
diff --git a/src/EventDispatchExtensions/KeyboardEventDispatchExtensions.cs b/src/EventDispatchExtensions/KeyboardEventDispatchExtensions.cs
index 636657f62..5749dcb1e 100644
--- a/src/EventDispatchExtensions/KeyboardEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/KeyboardEventDispatchExtensions.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/MediaEventDispatchExtensions.cs b/src/EventDispatchExtensions/MediaEventDispatchExtensions.cs
index 23a16376a..d21641325 100644
--- a/src/EventDispatchExtensions/MediaEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/MediaEventDispatchExtensions.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
diff --git a/src/EventDispatchExtensions/MissingEventHandlerException.cs b/src/EventDispatchExtensions/MissingEventHandlerException.cs
index 8a613230c..366788e76 100644
--- a/src/EventDispatchExtensions/MissingEventHandlerException.cs
+++ b/src/EventDispatchExtensions/MissingEventHandlerException.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using System.Runtime.Serialization;
using AngleSharp.Dom;
namespace Bunit
@@ -16,7 +15,7 @@ public MissingEventHandlerException(IElement element, string missingEventName) :
private static string CreateErrorMessage(IElement element, string missingEventName)
{
- var result = $"The element does not have an event handler for the event '{missingEventName}";
+ var result = $"The element does not have an event handler for the event '{missingEventName}'";
var eventHandlers = element.Attributes?
.Where(x => x.Name.StartsWith(Htmlizer.BLAZOR_ATTR_PREFIX, StringComparison.Ordinal) && !x.Name.StartsWith(Htmlizer.ELEMENT_REFERENCE_ATTR_NAME, StringComparison.Ordinal))
.Select(x => $"'{x.Name.Remove(0, Htmlizer.BLAZOR_ATTR_PREFIX.Length)}'")
@@ -25,9 +24,9 @@ private static string CreateErrorMessage(IElement element, string missingEventNa
var suggestAlternatives = ", nor any other events.";
if (eventHandlers.Length > 1)
- suggestAlternatives = $". The element has event handlers for these events, {string.Join(", ", eventHandlers)}, that you can try instead.";
+ suggestAlternatives = $". It does however have event handlers for these events, {string.Join(", ", eventHandlers)}.";
if (eventHandlers.Length == 1)
- suggestAlternatives = $". The element has an event handler for {eventHandlers[0]} event, that you can try instead.";
+ suggestAlternatives = $". It does however have an event handler for the {eventHandlers[0]} event.";
return $"{result}{suggestAlternatives}";
}
diff --git a/src/EventDispatchExtensions/MouseEventDispatchExtensions.cs b/src/EventDispatchExtensions/MouseEventDispatchExtensions.cs
index 44872af97..63e6ca3be 100644
--- a/src/EventDispatchExtensions/MouseEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/MouseEventDispatchExtensions.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/PointerEventDispatchExtensions.cs b/src/EventDispatchExtensions/PointerEventDispatchExtensions.cs
index 632eb3ca1..f9f8fa206 100644
--- a/src/EventDispatchExtensions/PointerEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/PointerEventDispatchExtensions.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/ProgressEventDispatchExtensions.cs b/src/EventDispatchExtensions/ProgressEventDispatchExtensions.cs
index f7b3ddd5b..a8d55aa28 100644
--- a/src/EventDispatchExtensions/ProgressEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/ProgressEventDispatchExtensions.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/EventDispatchExtensions/TouchEventDispatchExtensions.cs b/src/EventDispatchExtensions/TouchEventDispatchExtensions.cs
index e392c2043..124f2a1fd 100644
--- a/src/EventDispatchExtensions/TouchEventDispatchExtensions.cs
+++ b/src/EventDispatchExtensions/TouchEventDispatchExtensions.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
diff --git a/src/Extensions/ElementNotFoundException.cs b/src/Extensions/ElementNotFoundException.cs
index a4792322c..3886349ac 100644
--- a/src/Extensions/ElementNotFoundException.cs
+++ b/src/Extensions/ElementNotFoundException.cs
@@ -1,9 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Threading.Tasks;
namespace Xunit.Sdk
{
diff --git a/src/Extensions/Internal/AngleSharpExtensions.cs b/src/Extensions/Internal/AngleSharpExtensions.cs
index fd6dd942a..75c2e83e6 100644
--- a/src/Extensions/Internal/AngleSharpExtensions.cs
+++ b/src/Extensions/Internal/AngleSharpExtensions.cs
@@ -1,14 +1,6 @@
-using AngleSharp;
-using AngleSharp.Dom;
-using AngleSharp.Html;
-using AngleSharp.Text;
+using AngleSharp.Dom;
using Bunit.Diffing;
-using System;
using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Bunit
{
diff --git a/src/Extensions/Internal/BlazorExtensions.cs b/src/Extensions/Internal/BlazorExtensions.cs
index 84d7edbf4..5e77d1bae 100644
--- a/src/Extensions/Internal/BlazorExtensions.cs
+++ b/src/Extensions/Internal/BlazorExtensions.cs
@@ -1,6 +1,4 @@
using System;
-using System.Text;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Bunit
diff --git a/src/Extensions/Internal/TimeSpanExtensions.cs b/src/Extensions/Internal/TimeSpanExtensions.cs
index 4c547921f..21399917f 100644
--- a/src/Extensions/Internal/TimeSpanExtensions.cs
+++ b/src/Extensions/Internal/TimeSpanExtensions.cs
@@ -1,10 +1,6 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
-using System.Text;
using System.Threading;
-using System.Threading.Tasks;
namespace Bunit
{
diff --git a/src/Extensions/NodePrintExtensions.cs b/src/Extensions/NodePrintExtensions.cs
index d0e71610f..d6134b45d 100644
--- a/src/Extensions/NodePrintExtensions.cs
+++ b/src/Extensions/NodePrintExtensions.cs
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Html;
diff --git a/src/Extensions/RenderWaitingHelperExtensions.cs b/src/Extensions/RenderWaitingHelperExtensions.cs
index 9ccd18c31..56c7723d1 100644
--- a/src/Extensions/RenderWaitingHelperExtensions.cs
+++ b/src/Extensions/RenderWaitingHelperExtensions.cs
@@ -1,6 +1,4 @@
using System;
-using System.Diagnostics;
-using System.Runtime.ExceptionServices;
using System.Threading;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/Extensions/RenderedFragmentQueryExtensions.cs b/src/Extensions/RenderedFragmentQueryExtensions.cs
index c1895a8a5..aac22efdc 100644
--- a/src/Extensions/RenderedFragmentQueryExtensions.cs
+++ b/src/Extensions/RenderedFragmentQueryExtensions.cs
@@ -1,9 +1,5 @@
using System;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using AngleSharp.Dom;
-using AngleSharp.Html.Dom;
using AngleSharpWrappers;
using Xunit.Sdk;
diff --git a/src/Extensions/Xunit/XunitLogger.cs b/src/Extensions/Xunit/XunitLogger.cs
index 13efd8171..3b594ae8e 100644
--- a/src/Extensions/Xunit/XunitLogger.cs
+++ b/src/Extensions/Xunit/XunitLogger.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
diff --git a/src/Extensions/Xunit/XunitLoggerProvider.cs b/src/Extensions/Xunit/XunitLoggerProvider.cs
index d525519ad..22612cb56 100644
--- a/src/Extensions/Xunit/XunitLoggerProvider.cs
+++ b/src/Extensions/Xunit/XunitLoggerProvider.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
namespace Bunit.Extensions.Xunit
diff --git a/src/ITestContext.cs b/src/ITestContext.cs
index fa65dc41c..068a813ce 100644
--- a/src/ITestContext.cs
+++ b/src/ITestContext.cs
@@ -1,5 +1,4 @@
using System;
-using System.Threading.Tasks;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components;
diff --git a/src/Mocking/JSInterop/JsInvokeCountExpectedException.cs b/src/Mocking/JSInterop/JsInvokeCountExpectedException.cs
index 486e4ed90..9729f3af1 100644
--- a/src/Mocking/JSInterop/JsInvokeCountExpectedException.cs
+++ b/src/Mocking/JSInterop/JsInvokeCountExpectedException.cs
@@ -1,7 +1,5 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using Bunit.Mocking.JSInterop;
-using Xunit.Sdk;
namespace Xunit.Sdk
{
diff --git a/src/Mocking/JSInterop/JsRuntimePlannedInvocation.cs b/src/Mocking/JSInterop/JsRuntimePlannedInvocation.cs
index f5b5b1e90..36f57625a 100644
--- a/src/Mocking/JSInterop/JsRuntimePlannedInvocation.cs
+++ b/src/Mocking/JSInterop/JsRuntimePlannedInvocation.cs
@@ -1,7 +1,6 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System;
-using System.Diagnostics.CodeAnalysis;
namespace Bunit.Mocking.JSInterop
{
diff --git a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs
index 894f16729..9e4451f83 100644
--- a/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs
+++ b/src/Mocking/JSInterop/MissingMockJsRuntimeException.cs
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
namespace Xunit.Sdk
diff --git a/src/Mocking/JSInterop/MockJsRuntimeExtensions.cs b/src/Mocking/JSInterop/MockJsRuntimeExtensions.cs
index 1da9475e7..3d5c5ba04 100644
--- a/src/Mocking/JSInterop/MockJsRuntimeExtensions.cs
+++ b/src/Mocking/JSInterop/MockJsRuntimeExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using Bunit.Mocking.JSInterop;
using Microsoft.Extensions.DependencyInjection;
namespace Bunit.Mocking.JSInterop
diff --git a/src/Mocking/JSInterop/MockJsRuntimeInvokeHandler.cs b/src/Mocking/JSInterop/MockJsRuntimeInvokeHandler.cs
index 69f208200..92a59fc11 100644
--- a/src/Mocking/JSInterop/MockJsRuntimeInvokeHandler.cs
+++ b/src/Mocking/JSInterop/MockJsRuntimeInvokeHandler.cs
@@ -2,7 +2,6 @@
using Microsoft.JSInterop;
using System.Threading;
using System.Collections.Generic;
-using Xunit;
using System;
using System.Linq;
using Xunit.Sdk;
diff --git a/src/Mocking/JSInterop/PlaceholderJsRuntime.cs b/src/Mocking/JSInterop/PlaceholderJsRuntime.cs
index 6eff60eca..16bf978d9 100644
--- a/src/Mocking/JSInterop/PlaceholderJsRuntime.cs
+++ b/src/Mocking/JSInterop/PlaceholderJsRuntime.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop;
using Xunit.Sdk;
diff --git a/src/Mocking/JSInterop/UnplannedJsInvocationException.cs b/src/Mocking/JSInterop/UnplannedJsInvocationException.cs
index e43b619b9..edc0c0f2d 100644
--- a/src/Mocking/JSInterop/UnplannedJsInvocationException.cs
+++ b/src/Mocking/JSInterop/UnplannedJsInvocationException.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
using Bunit.Mocking.JSInterop;
diff --git a/src/Rendering/ComponentParameter.cs b/src/Rendering/ComponentParameter.cs
index 842c21eb8..29f1f974b 100644
--- a/src/Rendering/ComponentParameter.cs
+++ b/src/Rendering/ComponentParameter.cs
@@ -1,9 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Bunit
{
diff --git a/src/Rendering/IRenderedComponent.cs b/src/Rendering/IRenderedComponent.cs
index a4d47818f..eec8d931b 100644
--- a/src/Rendering/IRenderedComponent.cs
+++ b/src/Rendering/IRenderedComponent.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components;
namespace Bunit
{
diff --git a/src/Rendering/IRenderedFragment.cs b/src/Rendering/IRenderedFragment.cs
index 12a968ca7..86e2f4607 100644
--- a/src/Rendering/IRenderedFragment.cs
+++ b/src/Rendering/IRenderedFragment.cs
@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components;
-using Xunit.Sdk;
namespace Bunit
{
diff --git a/src/Rendering/RenderedComponent.cs b/src/Rendering/RenderedComponent.cs
index 2af7fe8e8..a6e5587d4 100644
--- a/src/Rendering/RenderedComponent.cs
+++ b/src/Rendering/RenderedComponent.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using AngleSharp.Diffing.Core;
using Microsoft.AspNetCore.Components;
namespace Bunit
diff --git a/src/Rendering/RenderedFragment.cs b/src/Rendering/RenderedFragment.cs
index 4bd0d9a1d..ca02193d8 100644
--- a/src/Rendering/RenderedFragment.cs
+++ b/src/Rendering/RenderedFragment.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using AngleSharp.Diffing.Core;
-using AngleSharp.Dom;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Rendering;
+using Microsoft.AspNetCore.Components;
namespace Bunit
{
diff --git a/src/Rendering/RenderedFragmentBase.cs b/src/Rendering/RenderedFragmentBase.cs
index c35a7388d..52d98c45b 100644
--- a/src/Rendering/RenderedFragmentBase.cs
+++ b/src/Rendering/RenderedFragmentBase.cs
@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using AngleSharp.Diffing.Core;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.RenderTree;
namespace Bunit
{
diff --git a/src/Rendering/TestRenderer.cs b/src/Rendering/TestRenderer.cs
index 2a19cd79d..c077707cd 100644
--- a/src/Rendering/TestRenderer.cs
+++ b/src/Rendering/TestRenderer.cs
@@ -2,12 +2,9 @@
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging;
using System;
-using System.Collections;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
-using System.Threading;
using Microsoft.Extensions.Logging.Abstractions;
namespace Bunit
diff --git a/src/SnapshotTestContext.cs b/src/SnapshotTestContext.cs
index b2597fa1e..4db40b2a9 100644
--- a/src/SnapshotTestContext.cs
+++ b/src/SnapshotTestContext.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
-using Microsoft.AspNetCore.Components;
namespace Bunit
{
diff --git a/src/TestContext.cs b/src/TestContext.cs
index 6732cdd8a..b4da0f2cb 100644
--- a/src/TestContext.cs
+++ b/src/TestContext.cs
@@ -2,16 +2,11 @@
using Bunit.Diffing;
using Bunit.Mocking.JSInterop;
using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.JSInterop;
using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Threading.Tasks;
namespace Bunit
{
diff --git a/src/bunit.csproj b/src/bunit.csproj
index 41da2590c..dc0c32470 100644
--- a/src/bunit.csproj
+++ b/src/bunit.csproj
@@ -49,7 +49,7 @@
-
+
diff --git a/tests/Asserting/CompareToDiffingExtensionsTest.cs b/tests/Asserting/CompareToDiffingExtensionsTest.cs
index 9b50d45bf..dc42e81f3 100644
--- a/tests/Asserting/CompareToDiffingExtensionsTest.cs
+++ b/tests/Asserting/CompareToDiffingExtensionsTest.cs
@@ -2,9 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using AngleSharp.Dom;
using Bunit.SampleComponents;
using Bunit.TestUtililities;
using Shouldly;
diff --git a/tests/Asserting/DiffAssertExtensionsTest.cs b/tests/Asserting/DiffAssertExtensionsTest.cs
index 2fa418894..c4341f21e 100644
--- a/tests/Asserting/DiffAssertExtensionsTest.cs
+++ b/tests/Asserting/DiffAssertExtensionsTest.cs
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using AngleSharp.Diffing.Core;
-using Bunit.Diffing;
using Moq;
using Shouldly;
using Xunit;
diff --git a/tests/Asserting/GenericCollectionAssertExtensionsTest.cs b/tests/Asserting/GenericCollectionAssertExtensionsTest.cs
index c8ca4dea4..d8a90a2d3 100644
--- a/tests/Asserting/GenericCollectionAssertExtensionsTest.cs
+++ b/tests/Asserting/GenericCollectionAssertExtensionsTest.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Shouldly;
using Xunit;
using Xunit.Sdk;
diff --git a/tests/BlazorE2E/BasicTestApp/ConcurrentRenderChild.razor b/tests/BlazorE2E/BasicTestApp/ConcurrentRenderChild.razor
new file mode 100644
index 000000000..980343734
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/ConcurrentRenderChild.razor
@@ -0,0 +1,16 @@
+@(isAfterDelay ? "😊" :"WAITING")
+@code
+{
+ protected bool isAfterDelay;
+
+ protected override async Task OnInitializedAsync()
+ {
+ // If there are lots of instances of this component, the following delay
+ // will result in a lot of them triggering a re-render simultaneously
+ // on different threads.
+ // This test is to verify that the renderer correctly accepts all the
+ // simultaneous render requests.
+ await Task.Delay(1000);
+ isAfterDelay = true;
+ }
+}
diff --git a/tests/BlazorE2E/BasicTestApp/ConcurrentRenderParent.razor b/tests/BlazorE2E/BasicTestApp/ConcurrentRenderParent.razor
new file mode 100644
index 000000000..1c219bcbe
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/ConcurrentRenderParent.razor
@@ -0,0 +1,7 @@
+
+ After a 1 second delay, the output should be 100x😊, with no remaining "WAITING" markers.
+
+
+
+ @for (var i = 0; i < 100; i++) {}
+
diff --git a/tests/BlazorE2E/BasicTestApp/DispatchingComponent.razor b/tests/BlazorE2E/BasicTestApp/DispatchingComponent.razor
new file mode 100644
index 000000000..519f1c2f7
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/DispatchingComponent.razor
@@ -0,0 +1,83 @@
+
Dispatching
+
+
+ Sometimes, renders need to be triggered in response to non-lifecyle events.
+ The current thread will not be associated with the renderer's sync context,
+ so the render request has to be marshalled onto that sync context.
+
+
+
+ Result: @result
+
+
+
+
+
+
+
+@code {
+ #nullable disable
+ string result;
+
+ async Task RunWithoutDispatch()
+ {
+ await Task.Delay(1).ConfigureAwait(false);
+ AttemptToRender();
+ }
+
+ async Task RunWithDispatch()
+ {
+ await Task.Delay(1).ConfigureAwait(false);
+ await InvokeAsync(AttemptToRender);
+
+ // So we can observe that the dispatched work item completed by now
+ if (result == "Success")
+ {
+ result += " (completed synchronously)";
+ }
+ }
+
+ async Task RunWithDoubleDispatch()
+ {
+ await Task.Delay(1).ConfigureAwait(false);
+ await InvokeAsync(() => InvokeAsync(AttemptToRender));
+
+ // So we can observe that the dispatched work item completed by now
+ if (result == "Success")
+ {
+ result += " (completed synchronously)";
+ }
+ }
+
+ async Task RunAsyncWorkWithDispatch()
+ {
+ await Task.Delay(1).ConfigureAwait(false);
+
+ result = "First";
+
+ var invokeTask = InvokeAsync(async () =>
+ {
+ // When the sync context is idle, queued work items start synchronously
+ result += " Second";
+ await Task.Delay(250);
+ result += " Fourth";
+ });
+
+ result += " Third";
+ await invokeTask;
+ result += " Fifth";
+ }
+
+ void AttemptToRender()
+ {
+ try
+ {
+ result = "Success";
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ result = ex.ToString();
+ }
+ }
+}
diff --git a/tests/BlazorE2E/BasicTestApp/DuplicateAttributesComponent.razor b/tests/BlazorE2E/BasicTestApp/DuplicateAttributesComponent.razor
new file mode 100644
index 000000000..8d7bcc2e3
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/DuplicateAttributesComponent.razor
@@ -0,0 +1,22 @@
+
+ This component contains blocks of static HTML markup that will be
+ represented in the render instructions as single frames.
+
+ This includes nested elements with attributes.
+
+
+
Dynamic markup
+
+
It's also possible to emit markup blocks from render fragments:
+
+
+ [@((RenderFragment)EmitMarkupBlock)]
+
+
+
+
+
Markup string
+
+
It's also possible to declare a value of a special type that renders as markup:
";
+
+ void EmitMarkupBlock(RenderTreeBuilder builder)
+ {
+ // To show we detect and apply changes to markup blocks
+ if (!changeOutput)
+ {
+ builder.AddMarkupContent(0, "Here is an example. We support multiple-top-level nodes.");
+ }
+ else
+ {
+ builder.AddMarkupContent(0, "The output was changed completely.");
+ }
+ }
+}
diff --git a/tests/BlazorE2E/BasicTestApp/MovingCheckboxesComponent.razor b/tests/BlazorE2E/BasicTestApp/MovingCheckboxesComponent.razor
new file mode 100644
index 000000000..fbb1f4cf5
--- /dev/null
+++ b/tests/BlazorE2E/BasicTestApp/MovingCheckboxesComponent.razor
@@ -0,0 +1,57 @@
+
+ This component represents a case that's difficult for the diff algorithm if it doesn't
+ understand how the underlying DOM gets mutated when you check a box.
+
+
+ If we didn't have the RenderTreeUpdater, then if you checked the first incomplete item,
+ the diff algoritm would see the subsequent render has only one "todo" item left, and would
+ match it with the existing 'li' element. Since that's still not done, the algorithm would
+ think no change was needed to the checkbox. But since you just clicked that checkbox, the
+ UI would show it as checked. It would look as if you have completed all four items instead
+ of just three.
+
+
+ RenderTreeUpdater fixes this by patching the old render tree to match the latest state of
+ the DOM, so the diff algoritm sees it must explicitly uncheck the remaining 'todo' box.
+
+
+@code {
+ #nullable disable
+ [Parameter]
+ public RenderFragment Header { get; set; }
+
+ [Parameter]
+ public RenderFragment ItemTemplate { get; set; }
+
+ [Parameter]
+ public RenderFragment Footer { get; set; }
+
+ [Parameter]
+ public IReadOnlyList Items { get; set; }
+}
diff --git a/tests/BlazorE2E/ComponentRenderingTest.cs b/tests/BlazorE2E/ComponentRenderingTest.cs
index 051a897f0..ffa757af4 100644
--- a/tests/BlazorE2E/ComponentRenderingTest.cs
+++ b/tests/BlazorE2E/ComponentRenderingTest.cs
@@ -1,17 +1,9 @@
using System;
-using System.Collections.Generic;
using System.Configuration.Assemblies;
using System.Linq;
using System.Numerics;
-using System.Text;
-using System.Threading.Tasks;
-using AngleSharp.Dom;
using Bunit.BlazorE2E.BasicTestApp;
using Bunit.BlazorE2E.BasicTestApp.HierarchicalImportsTest.Subdir;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Web;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
@@ -444,196 +436,173 @@ public void CanCaptureReferencesToDynamicallyAddedComponents()
Assert.Equal("Current count: 0", currentCountText());
}
- //[Fact]
- //public void CanUseJsInteropForRefElementsDuringOnAfterRender()
- //{
- // var cut = RenderComponent();
- // Assert.Equal("Value set after render", () => Browser.Find("input").GetAttribute("value"));
- //}
-
- //[Fact]
- //public void CanRenderMarkupBlocks()
- //{
- // var cut = RenderComponent();
-
- // // Static markup
- // Assert.Equal(
- // "attributes",
- // cut.FindElement(By.CssSelector("p span#attribute-example")).TextContent);
-
- // // Dynamic markup (from a custom RenderFragment)
- // Assert.Equal(
- // "[Here is an example. We support multiple-top-level nodes.]",
- // cut.Find("#dynamic-markup-block").TextContent);
- // Assert.Equal(
- // "example",
- // cut.FindElement(By.CssSelector("#dynamic-markup-block strong#dynamic-element em")).TextContent);
-
- // // Dynamic markup (from a MarkupString)
- // Assert.Equal(
- // "This is a markup string.",
- // cut.FindElement(By.ClassName("markup-string-value")).TextContent);
- // Assert.Equal(
- // "markup string",
- // cut.Find(".markup-string-value em").TextContent);
-
- // // Updating markup blocks
- // cut.Find("button").Click();
- // Browser.Equal(
- // "[The output was changed completely.]",
- // () => cut.Find("#dynamic-markup-block").TextContent);
- // Assert.Equal(
- // "changed",
- // cut.FindElement(By.CssSelector("#dynamic-markup-block span em")).TextContent);
- //}
-
- //[Fact]
- //public void CanRenderRazorTemplates()
- //{
- // var cut = RenderComponent();
-
- // // code block template (component parameter)
- // var element = cut.FindElement(By.CssSelector("div#codeblocktemplate ol"));
- // Assert.Collection(
- // element.FindAll("li"),
- // e => Assert.Equal("#1 - a", e.TextContent),
- // e => Assert.Equal("#2 - b", e.TextContent),
- // e => Assert.Equal("#3 - c", e.TextContent));
- //}
-
- //[Fact]
- //public void CanRenderMultipleChildContent()
- //{
- // var cut = RenderComponent();
-
- // var table = cut.Find("table");
-
- // var thead = table.Find("thead");
- // Assert.Collection(
- // thead.FindAll("th"),
- // e => Assert.Equal("Col1", e.TextContent),
- // e => Assert.Equal("Col2", e.TextContent),
- // e => Assert.Equal("Col3", e.TextContent));
-
- // var tfoot = table.Find("tfoot");
- // Assert.Empty(tfoot.FindAll("td"));
-
- // var toggle = cut.Find("#toggle");
- // toggle.Click();
-
- // Browser.Collection(
- // () => tfoot.FindAll("td"),
- // e => Assert.Equal("The", e.TextContent),
- // e => Assert.Equal("", e.TextContent),
- // e => Assert.Equal("End", e.TextContent));
- //}
-
- //[Fact]
- //public async Task CanAcceptSimultaneousRenderRequests()
- //{
- // var expectedOutput = string.Join(
- // string.Empty,
- // Enumerable.Range(0, 100).Select(_ => "😊"));
-
- // var cut = RenderComponent();
-
- // // It's supposed to pause the rendering for this long. The WaitAssert below
- // // allows it to take up extra time if needed.
- // await Task.Delay(1000);
-
- // var outputElement = cut.Find("#concurrent-render-output");
- // Assert.Equal(expectedOutput, () => outputElement.TextContent);
- //}
-
- //[Fact]
- //public void CanDispatchRenderToSyncContext()
- //{
- // var cut = RenderComponent();
- // var result = cut.Find("#result");
-
- // cut.Find("#run-with-dispatch").Click();
-
- // Assert.Equal("Success (completed synchronously)", () => result.TextContent);
- //}
-
- //[Fact]
- //public void CanDoubleDispatchRenderToSyncContext()
- //{
- // var cut = RenderComponent();
- // var result = cut.Find("#result");
-
- // cut.Find("#run-with-double-dispatch").Click();
-
- // Assert.Equal("Success (completed synchronously)", () => result.TextContent);
- //}
-
- //[Fact]
- //public void CanDispatchAsyncWorkToSyncContext()
- //{
- // var cut = RenderComponent();
- // var result = cut.Find("#result");
-
- // cut.Find("#run-async-with-dispatch").Click();
-
- // Assert.Equal("First Second Third Fourth Fifth", () => result.TextContent);
- //}
-
- //[Fact]
- //public void CanPerformInteropImmediatelyOnComponentInsertion()
- //{
- // var cut = RenderComponent();
- // Assert.Equal("Hello from interop call", () => cut.Find("#val-get-by-interop").TextContent);
- // Assert.Equal("Hello from interop call", () => cut.Find("#val-set-by-interop").GetAttribute("value"));
- //}
-
- //[Fact]
- //public void CanUseAddMultipleAttributes()
- //{
- // var cut = RenderComponent();
-
- // var selector = By.CssSelector("#duplicate-on-element > div");
- // Browser.Exists(selector);
-
- // var element = cut.FindElement(selector);
- // Assert.Equal(string.Empty, element.GetAttribute("bool")); // attribute is present
- // Assert.Equal("middle-value", element.GetAttribute("string"));
- // Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
-
- // selector = By.CssSelector("#duplicate-on-element-override > div");
- // element = cut.FindElement(selector);
- // Assert.Null(element.GetAttribute("bool")); // attribute is not present
- // Assert.Equal("other-text", element.GetAttribute("string"));
- // Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
- //}
-
- //[Fact]
- //public void CanPatchRenderTreeToMatchLatestDOMState()
- //{
- // var cut = RenderComponent();
- // var incompleteItemsSelector = By.CssSelector(".incomplete-items li");
- // var completeItemsSelector = By.CssSelector(".complete-items li");
- // Browser.Exists(incompleteItemsSelector);
-
- // // Mark first item as done; observe the remaining incomplete item appears unchecked
- // // because the diff algorithm explicitly unchecks it
- // cut.Find(".incomplete-items .item-isdone").Click();
- // Browser.True(() =>
- // {
- // var incompleteLIs = cut.FindElements(incompleteItemsSelector);
- // return incompleteLIs.Count == 1
- // && !incompleteLIs[0].Find(".item-isdone").Selected;
- // });
-
- // // Mark first done item as not done; observe the remaining complete item appears checked
- // // because the diff algorithm explicitly re-checks it
- // cut.Find(".complete-items .item-isdone").Click();
- // Browser.True(() =>
- // {
- // var completeLIs = cut.FindElements(completeItemsSelector);
- // return completeLIs.Count == 2
- // && completeLIs[0].Find(".item-isdone").Selected;
- // });
- //}
+ [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ public void CanUseJsInteropForRefElementsDuringOnAfterRender()
+ {
+ //var cut = RenderComponent();
+ //Assert.Equal("Value set after render", () => Browser.Find("input").GetAttribute("value"));
+ }
+
+ [Fact]
+ public void CanRenderMarkupBlocks()
+ {
+ var cut = RenderComponent();
+
+ // Static markup
+ Assert.Equal("attributes", cut.Find("p span#attribute-example").TextContent);
+
+ // Dynamic markup (from a custom RenderFragment)
+ Assert.Equal("[Here is an example. We support multiple-top-level nodes.]", cut.Find("#dynamic-markup-block").TextContent.Trim());
+ Assert.Equal("example", cut.Find("#dynamic-markup-block strong#dynamic-element em").TextContent);
+
+ // Dynamic markup (from a MarkupString)
+ Assert.Equal("This is a markup string.", cut.Find(".markup-string-value").TextContent);
+ Assert.Equal("markup string", cut.Find(".markup-string-value em").TextContent);
+
+ // Updating markup blocks
+ cut.Find("button").Click();
+ Assert.Equal("[The output was changed completely.]", cut.Find("#dynamic-markup-block").TextContent.Trim());
+ Assert.Equal("changed", cut.Find("#dynamic-markup-block span em").TextContent);
+ }
+
+ [Fact]
+ public void CanRenderRazorTemplates()
+ {
+ var cut = RenderComponent();
+
+ // code block template (component parameter)
+ var element = cut.Find("div#codeblocktemplate ol");
+ Assert.Collection(element.QuerySelectorAll("li"),
+ e => Assert.Equal("#1 - a", e.TextContent),
+ e => Assert.Equal("#2 - b", e.TextContent),
+ e => Assert.Equal("#3 - c", e.TextContent));
+ }
+
+ [Fact]
+ public void CanRenderMultipleChildContent()
+ {
+ var cut = RenderComponent();
+
+ var table = cut.Find("table");
+
+ var thead = table.QuerySelector("thead");
+ Assert.Collection(
+ thead.QuerySelectorAll("th"),
+ e => Assert.Equal("Col1", e.TextContent),
+ e => Assert.Equal("Col2", e.TextContent),
+ e => Assert.Equal("Col3", e.TextContent));
+
+ var tfootElements = cut.FindAll("table tfoot td", enableAutoRefresh: true);
+ Assert.Empty(tfootElements);
+ var toggle = cut.Find("#toggle");
+ toggle.Change(true);
+
+ Assert.Collection(tfootElements,
+ e => Assert.Equal("The", e.TextContent),
+ e => Assert.Equal("", e.TextContent),
+ e => Assert.Equal("End", e.TextContent)
+ );
+ }
+
+ [Fact]
+ public void CanAcceptSimultaneousRenderRequests()
+ {
+ var expectedOutput = string.Join(
+ string.Empty,
+ Enumerable.Range(0, 100).Select(_ => "😊"));
+
+ var cut = RenderComponent();
+
+ // It's supposed to pause the rendering for this long. The WaitAssert below
+ // allows it to take up extra time if needed.
+ //await Task.Delay(1000);
+
+ var outputElement = cut.Find("#concurrent-render-output");
+
+ cut.WaitForAssertion(
+ () => Assert.Equal(expectedOutput, outputElement.TextContent.Trim()),
+ timeout: TimeSpan.FromMilliseconds(2000)
+ );
+ }
+
+ [Fact]
+ public void CanDispatchRenderToSyncContext()
+ {
+ var cut = RenderComponent();
+ var result = cut.Find("#result");
+
+ cut.Find("#run-with-dispatch").Click();
+
+ cut.WaitForAssertion(() => Assert.Equal("Success (completed synchronously)", result.TextContent.Trim()));
+ }
+
+ [Fact]
+ public void CanDoubleDispatchRenderToSyncContext()
+ {
+ var cut = RenderComponent();
+ var result = cut.Find("#result");
+
+ cut.Find("#run-with-double-dispatch").Click();
+
+ cut.WaitForAssertion(() => Assert.Equal("Success (completed synchronously)", result.TextContent.Trim()));
+ }
+
+ [Fact]
+ public void CanDispatchAsyncWorkToSyncContext()
+ {
+ var cut = RenderComponent();
+ var result = cut.Find("#result");
+
+ cut.Find("#run-async-with-dispatch").Click();
+
+ cut.WaitForAssertion(() => Assert.Equal("First Second Third Fourth Fifth", result.TextContent.Trim()), timeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ public void CanPerformInteropImmediatelyOnComponentInsertion()
+ {
+ //var cut = RenderComponent();
+ //Assert.Equal("Hello from interop call", () => cut.Find("#val-get-by-interop").TextContent);
+ //Assert.Equal("Hello from interop call", () => cut.Find("#val-set-by-interop").GetAttribute("value"));
+ }
+
+ [Fact]
+ public void CanUseAddMultipleAttributes()
+ {
+ var cut = RenderComponent();
+
+ var element = cut.Find("#duplicate-on-element > div");
+ Assert.True(element.HasAttribute("bool")); // attribute is present
+ Assert.Equal("middle-value", element.GetAttribute("string"));
+ Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
+
+ element = cut.Find("#duplicate-on-element-override > div");
+ Assert.False(element.HasAttribute("bool")); // attribute is not present
+ Assert.Equal("other-text", element.GetAttribute("string"));
+ Assert.Equal("unmatched-value", element.GetAttribute("unmatched"));
+ }
+
+ [Fact]
+ public void CanPatchRenderTreeToMatchLatestDOMState()
+ {
+ var cut = RenderComponent();
+ var incompleteItemsSelector = ".incomplete-items li";
+ var completeItemsSelector = ".complete-items li";
+
+ // Mark first item as done; observe the remaining incomplete item appears unchecked
+ // because the diff algorithm explicitly unchecks it
+ cut.Find(".incomplete-items .item-isdone").Change(true);
+ var incompleteLIs = cut.FindAll(incompleteItemsSelector);
+ Assert.Equal(1, incompleteLIs.Count);
+ Assert.False(incompleteLIs[0].QuerySelector(".item-isdone").HasAttribute("checked"));
+
+ // Mark first done item as not done; observe the remaining complete item appears checked
+ // because the diff algorithm explicitly re-checks it
+ cut.Find(".complete-items .item-isdone").Change(false);
+ var completeLIs = cut.FindAll(completeItemsSelector);
+ Assert.Equal(2, completeLIs.Count);
+ Assert.True(completeLIs[0].QuerySelector(".item-isdone").HasAttribute("checked"));
+ }
}
}
diff --git a/tests/ComponentTestFixtureTest.cs b/tests/ComponentTestFixtureTest.cs
index 8229cd99b..42c7ec84e 100644
--- a/tests/ComponentTestFixtureTest.cs
+++ b/tests/ComponentTestFixtureTest.cs
@@ -1,8 +1,6 @@
using System;
-using Microsoft.AspNetCore.Components;
using Xunit;
using Shouldly;
-using System.Threading.Tasks;
using Bunit.SampleComponents;
using System.Diagnostics.CodeAnalysis;
using Bunit.Mocking.JSInterop;
diff --git a/tests/EventDispatchExtensions/ClipboardEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/ClipboardEventDispatchExtensionsTest.cs
index 7f746180d..26f718ec3 100644
--- a/tests/EventDispatchExtensions/ClipboardEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/ClipboardEventDispatchExtensionsTest.cs
@@ -1,13 +1,6 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Threading;
+using System.Reflection;
using System.Threading.Tasks;
-using Bunit.SampleComponents;
-using Bunit.SampleComponents.Data;
using Microsoft.AspNetCore.Components.Web;
-using Moq;
-using Shouldly;
using Xunit;
namespace Bunit
diff --git a/tests/EventDispatchExtensions/EventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/EventDispatchExtensionsTest.cs
index b23b09523..371fb7ad6 100644
--- a/tests/EventDispatchExtensions/EventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/EventDispatchExtensionsTest.cs
@@ -7,7 +7,6 @@
using DeepEqual.Syntax;
using Bunit.SampleComponents;
using Shouldly;
-using Xunit;
using System.Diagnostics.CodeAnalysis;
namespace Bunit
diff --git a/tests/EventDispatchExtensions/FocusEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/FocusEventDispatchExtensionsTest.cs
index 9839295a0..4182a0972 100644
--- a/tests/EventDispatchExtensions/FocusEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/FocusEventDispatchExtensionsTest.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Xunit;
diff --git a/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
index abd6bf4fc..287b245cb 100644
--- a/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/GeneralEventDispatchExtensionsTest.cs
@@ -1,8 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Dom;
diff --git a/tests/EventDispatchExtensions/MediaEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/MediaEventDispatchExtensionsTest.cs
index 43d54e21f..4a35d8a23 100644
--- a/tests/EventDispatchExtensions/MediaEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/MediaEventDispatchExtensionsTest.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
diff --git a/tests/EventDispatchExtensions/PointerEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/PointerEventDispatchExtensionsTest.cs
index 176769dff..e8037aaea 100644
--- a/tests/EventDispatchExtensions/PointerEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/PointerEventDispatchExtensionsTest.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
+using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Xunit;
diff --git a/tests/EventDispatchExtensions/ProgressEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/ProgressEventDispatchExtensionsTest.cs
index 40f49a2ce..966770f64 100644
--- a/tests/EventDispatchExtensions/ProgressEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/ProgressEventDispatchExtensionsTest.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
+using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Xunit;
diff --git a/tests/EventDispatchExtensions/TouchEventDispatchExtensionsTest.cs b/tests/EventDispatchExtensions/TouchEventDispatchExtensionsTest.cs
index 07a9d60af..28bae3f56 100644
--- a/tests/EventDispatchExtensions/TouchEventDispatchExtensionsTest.cs
+++ b/tests/EventDispatchExtensions/TouchEventDispatchExtensionsTest.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
+using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Xunit;
diff --git a/tests/EventDispatchExtensions/TriggerEventSpy.cs b/tests/EventDispatchExtensions/TriggerEventSpy.cs
index abe8bed58..02956e551 100644
--- a/tests/EventDispatchExtensions/TriggerEventSpy.cs
+++ b/tests/EventDispatchExtensions/TriggerEventSpy.cs
@@ -1,5 +1,4 @@
using System;
-using System.Reflection;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Bunit.SampleComponents;
diff --git a/tests/Mocking/JSInterop/JsRuntimeAssertExtensionsTest.cs b/tests/Mocking/JSInterop/JsRuntimeAssertExtensionsTest.cs
index dff880423..59171f599 100644
--- a/tests/Mocking/JSInterop/JsRuntimeAssertExtensionsTest.cs
+++ b/tests/Mocking/JSInterop/JsRuntimeAssertExtensionsTest.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Bunit.Diffing;
diff --git a/tests/Mocking/JSInterop/JsRuntimeInvocationTest.cs b/tests/Mocking/JSInterop/JsRuntimeInvocationTest.cs
index 2778e5478..4a6471c57 100644
--- a/tests/Mocking/JSInterop/JsRuntimeInvocationTest.cs
+++ b/tests/Mocking/JSInterop/JsRuntimeInvocationTest.cs
@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading;
-using System.Threading.Tasks;
using Shouldly;
using Xunit;
diff --git a/tests/Mocking/JSInterop/MockJsRuntimeInvokeHandlerTest.cs b/tests/Mocking/JSInterop/MockJsRuntimeInvokeHandlerTest.cs
index c6f7c46b5..f08b98234 100644
--- a/tests/Mocking/JSInterop/MockJsRuntimeInvokeHandlerTest.cs
+++ b/tests/Mocking/JSInterop/MockJsRuntimeInvokeHandlerTest.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.JSInterop;
diff --git a/tests/RefreshableQueryCollectionTest.cs b/tests/RefreshableQueryCollectionTest.cs
index f96c249da..53b54c1ec 100644
--- a/tests/RefreshableQueryCollectionTest.cs
+++ b/tests/RefreshableQueryCollectionTest.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Bunit.SampleComponents;
+using Bunit.SampleComponents;
using Shouldly;
using Xunit;
diff --git a/tests/Rendering/ComponentParameterTest.cs b/tests/Rendering/ComponentParameterTest.cs
index 2fadf0500..9cf78db23 100644
--- a/tests/Rendering/ComponentParameterTest.cs
+++ b/tests/Rendering/ComponentParameterTest.cs
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Shouldly;
using Xunit;
diff --git a/tests/Rendering/RenderEventPubSubTest.cs b/tests/Rendering/RenderEventPubSubTest.cs
index 6e6d64f22..07bb4c229 100644
--- a/tests/Rendering/RenderEventPubSubTest.cs
+++ b/tests/Rendering/RenderEventPubSubTest.cs
@@ -1,10 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.RenderTree;
-using Moq;
using Shouldly;
using Xunit;
diff --git a/tests/Rendering/RenderWaitingHelperExtensionsTest.cs b/tests/Rendering/RenderWaitingHelperExtensionsTest.cs
index 3dee4eb74..495f12e90 100644
--- a/tests/Rendering/RenderWaitingHelperExtensionsTest.cs
+++ b/tests/Rendering/RenderWaitingHelperExtensionsTest.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using AngleSharp.Dom;
using Bunit.Mocking.JSInterop;
using Bunit.SampleComponents;
diff --git a/tests/Rendering/RenderedFragmentTest.cs b/tests/Rendering/RenderedFragmentTest.cs
index 0ce41f1cd..b1fa96751 100644
--- a/tests/Rendering/RenderedFragmentTest.cs
+++ b/tests/Rendering/RenderedFragmentTest.cs
@@ -1,16 +1,7 @@
-using AngleSharp.Dom;
-using Bunit.Extensions.Xunit;
-using Bunit.Mocking.JSInterop;
-using Bunit.SampleComponents;
-using Bunit.SampleComponents.Data;
+using Bunit.SampleComponents;
using Microsoft.AspNetCore.Components;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Shouldly;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
diff --git a/tests/SampleComponents/Data/AsyncNameDep.cs b/tests/SampleComponents/Data/AsyncNameDep.cs
index 1aab24318..af01c9524 100644
--- a/tests/SampleComponents/Data/AsyncNameDep.cs
+++ b/tests/SampleComponents/Data/AsyncNameDep.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
namespace Bunit.SampleComponents.Data
{
diff --git a/tests/SampleComponents/Data/IAsyncTestDep.cs b/tests/SampleComponents/Data/IAsyncTestDep.cs
index 1e648a4be..09ea254eb 100644
--- a/tests/SampleComponents/Data/IAsyncTestDep.cs
+++ b/tests/SampleComponents/Data/IAsyncTestDep.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
namespace Bunit.SampleComponents.Data
{
diff --git a/tests/SampleComponents/Data/ITestDep.cs b/tests/SampleComponents/Data/ITestDep.cs
index 1e3a569fe..7d34fd951 100644
--- a/tests/SampleComponents/Data/ITestDep.cs
+++ b/tests/SampleComponents/Data/ITestDep.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Bunit.SampleComponents.Data
+namespace Bunit.SampleComponents.Data
{
public interface ITestDep
{
diff --git a/tests/SampleComponents/TriggerTester.cs b/tests/SampleComponents/TriggerTester.cs
index 9e7110dbd..34ec63314 100644
--- a/tests/SampleComponents/TriggerTester.cs
+++ b/tests/SampleComponents/TriggerTester.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
diff --git a/tests/TestRendererTest.cs b/tests/TestRendererTest.cs
index f82998372..fa9c128a5 100644
--- a/tests/TestRendererTest.cs
+++ b/tests/TestRendererTest.cs
@@ -1,11 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Bunit.SampleComponents;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Rendering;
+using Bunit.SampleComponents;
using Shouldly;
using Xunit;
diff --git a/tests/TestServiceProviderTest.cs b/tests/TestServiceProviderTest.cs
index 6e17223f8..3d4329bc5 100644
--- a/tests/TestServiceProviderTest.cs
+++ b/tests/TestServiceProviderTest.cs
@@ -1,14 +1,10 @@
-using Bunit;
-using Bunit.Mocking.JSInterop;
+using Bunit.Mocking.JSInterop;
using Bunit.SampleComponents;
-using Bunit.SampleComponents.Data;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
using Xunit;
using Xunit.Sdk;
From 9aa109f061e334c6d3d91bf4b4b48f5535d1dbc7 Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 15:49:20 +0000
Subject: [PATCH 23/27] Tweaks to test
---
CHANGELOG.md | 77 ++++++++-
src/Extensions/ElementNotFoundException.cs | 2 +-
src/Extensions/Internal/ElementFactory.cs | 2 +-
.../RenderedFragmentQueryExtensions.cs | 2 +-
.../JSInterop/JsRuntimeAssertExtensions.cs | 6 +-
src/Rendering/Internal/Htmlizer.cs | 22 +--
src/bunit.csproj | 2 +-
tests/BlazorE2E/ComponentRenderingTest.cs | 156 ++++++++++++------
tests/Rendering/RenderedFragmentTest.cs | 4 +-
tests/TestRendererTest.cs | 53 +-----
10 files changed, 200 insertions(+), 126 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f118c899..c61dbe6a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,22 +1,85 @@
# Changelog
-All notable changes to this project will be documented in this file. The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+All notable changes to **bUnit** will be documented in this file. The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
-This release includes a name change from Blazor Components Testing Library to **bUnit**. It also brings along two extra helper methods for working with asynchronously rendering components during testing, and a bunch of internal optimizations and tweaks to the code.
+This release includes a **name change from Blazor Components Testing Library to bUnit**. It also brings along two extra helper methods for working with asynchronously rendering components during testing, and a bunch of internal optimizations and tweaks to the code.
*Why change the name?* Naming is hard, and I initial chose a very product-namy name, that quite clearly stated what the library was for. However, the name isn't very searchable, since it just contains generic keywords, plus, bUnit is just much cooler. It also gave me the opportunity to remove my name from all the namespaces and simplify those.
+### NuGet
+The latest version of the library is availble on NuGet:
+
+| | Type | Link |
+| ------------- | ----- | ---- |
+| [](https://www.nuget.org/packages/bunit/) | Library | [https://www.nuget.org/packages/bunit/](https://www.nuget.org/packages/bunit/) |
+| [](https://www.nuget.org/packages/bunit.template/) | Template | [https://www.nuget.org/packages/bunit.template/](https://www.nuget.org/packages/bunit.template/) |
+
### Added
- **`WaitForState(Func statePredicate, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.**
This method will wait (block) until the provided statePredicate returns true, or the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the statePredicate is evaluated.
You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue.
+ The following example tests the `DelayedRenderOnClick.razor` component:
+
+ ```cshtml
+ // DelayedRenderOnClick.razor
+
Times Clicked: @TimesClicked
+
+ @code
+ {
+ public int TimesClicked { get; private set; }
+
+ async Task ClickCounter()
+ {
+ await Task.Delay(1); // wait 1 millisecond
+ TimesClicked += 1;
+ }
+ }
+ ```
+
+ This is a test that uses `WaitForState` to wait until the component under test has a desired state, before the test continues:
+
+ ```csharp
+ [Fact]
+ public void WaitForStateExample()
+ {
+ // Arrange
+ var cut = RenderComponent();
+
+ // Act
+ cut.Find("button").Click();
+ cut.WaitForState(() => cut.Instance.TimesClicked == 1);
+
+ // Assert
+ cut.Find("p").TextContent.ShouldBe("Times Clicked: 1");
+ }
+ ```
+
- **`WaitForAssertion(Action assertion, TimeSpan? timeout = 1 second)` has been added to `ITestContext` and `IRenderedFragment`.**
This method will wait (block) until the provided assertion method passes, i.e. runs without throwing an assert exception, or until the timeout is reached (during debugging the timeout is disabled). Each time the renderer in the test context renders, or the rendered fragment renders, the assertion is attempted.
You use this method, if you have a component under test, that requires _one or more asynchronous triggered renders_, to get to a desired state, before the test can continue.
+ This is a test that tests the `DelayedRenderOnClick.razor` listed above, and that uses `WaitForAssertion` to attempt the assertion each time the component under test renders:
+
+ ```csharp
+ [Fact]
+ public void WaitForAssertionExample()
+ {
+ // Arrange
+ var cut = RenderComponent();
+
+ // Act
+ cut.Find("button").Click();
+
+ // Assert
+ cut.WaitForAssertion(
+ () => cut.Find("p").TextContent.ShouldBe("Times Clicked: 1")
+ );
+ }
+ ```
+
- **Added support for capturing log statements from the renderer and components under test into the test output.**
To enable this, add a constructor to your test classes that takes the `ITestOutputHelper` as input, then in the constructor call `Services.AddXunitLogger` and pass the `ITestOutputHelper` to it, e.g.:
@@ -96,6 +159,11 @@ This release includes a name change from Blazor Components Testing Library to **
- **Added logging to TestRenderer.** To make it easier to understand the rendering life-cycle during a test, the `TestRenderer` will now log when ever it dispatches an event or renders a component (the log statements can be access by capturing debug logs in the test results, as mentioned above).
+- **Added some of the Blazor frameworks end-2-end tests.** To get better test coverage of the many rendering scenarios supported by Blazor, the [ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs) tests from the Blazor frameworks test suite has been converted from a Selenium to a bUnit. The testing style is very similar, so few changes was necessary to port the tests. The two test classes are here, if you want to compare:
+
+ - [bUnit's ComponentRenderingTest.cs](/master/tests/BlazorE2E/ComponentRenderingTest.cs)
+ - [Blazor's ComponentRenderingTest.cs](https://github.com/dotnet/aspnetcore/blob/master/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs)
+
### Changed
- **Namespaces is now `Bunit`**
The namespaces have changed from `Egil.RazorComponents.Testing.Library.*` to simply `Bunit` for the library, and `Bunit.Mocking.JSInterop` for the JSInterop mocking support.
@@ -169,7 +237,4 @@ This release includes a name change from Blazor Components Testing Library to **
### Fixed
- **Wrong casing on keyboard event dispatch helpers.**
- The helper methods for the keyboard events was not probably cased, so that has been updated. E.g. from `Keypress(...)` to `KeyPress(...)`.
-
-
-### Security
\ No newline at end of file
+ The helper methods for the keyboard events was not probably cased, so that has been updated. E.g. from `Keypress(...)` to `KeyPress(...)`.
\ No newline at end of file
diff --git a/src/Extensions/ElementNotFoundException.cs b/src/Extensions/ElementNotFoundException.cs
index 3886349ac..585b32163 100644
--- a/src/Extensions/ElementNotFoundException.cs
+++ b/src/Extensions/ElementNotFoundException.cs
@@ -1,6 +1,6 @@
using System;
-namespace Xunit.Sdk
+namespace Bunit
{
///
/// Represents a failure to find an element in the searched target
diff --git a/src/Extensions/Internal/ElementFactory.cs b/src/Extensions/Internal/ElementFactory.cs
index d97f10ab9..ea637e93d 100644
--- a/src/Extensions/Internal/ElementFactory.cs
+++ b/src/Extensions/Internal/ElementFactory.cs
@@ -1,7 +1,7 @@
using System;
using AngleSharp.Dom;
using AngleSharpWrappers;
-using Xunit.Sdk;
+using Bunit.Extensions;
namespace Bunit
{
diff --git a/src/Extensions/RenderedFragmentQueryExtensions.cs b/src/Extensions/RenderedFragmentQueryExtensions.cs
index aac22efdc..b0da5b5e2 100644
--- a/src/Extensions/RenderedFragmentQueryExtensions.cs
+++ b/src/Extensions/RenderedFragmentQueryExtensions.cs
@@ -1,7 +1,7 @@
using System;
using AngleSharp.Dom;
using AngleSharpWrappers;
-using Xunit.Sdk;
+using Bunit.Extensions;
namespace Bunit
{
diff --git a/src/Mocking/JSInterop/JsRuntimeAssertExtensions.cs b/src/Mocking/JSInterop/JsRuntimeAssertExtensions.cs
index c00c9b08d..260e77e7e 100644
--- a/src/Mocking/JSInterop/JsRuntimeAssertExtensions.cs
+++ b/src/Mocking/JSInterop/JsRuntimeAssertExtensions.cs
@@ -48,10 +48,14 @@ public static JsRuntimeInvocation VerifyInvoke(this MockJsRuntimeInvokeHandler h
public static IReadOnlyList VerifyInvoke(this MockJsRuntimeInvokeHandler handler, string identifier, int calledTimes, string? userMessage = null)
{
if (handler is null) throw new ArgumentNullException(nameof(handler));
+
if (calledTimes < 1)
throw new ArgumentException($"Use {nameof(VerifyNotInvoke)} to verify an identifier has not been invoked.", nameof(calledTimes));
- var invocations = handler.Invocations[identifier];
+ if (!handler.Invocations.TryGetValue(identifier, out var invocations) )
+ {
+ throw new JsInvokeCountExpectedException(identifier, calledTimes, 0, nameof(VerifyInvoke), userMessage);
+ }
if (invocations.Count != calledTimes)
{
diff --git a/src/Rendering/Internal/Htmlizer.cs b/src/Rendering/Internal/Htmlizer.cs
index 71b6057d9..8ad0cf9ab 100644
--- a/src/Rendering/Internal/Htmlizer.cs
+++ b/src/Rendering/Internal/Htmlizer.cs
@@ -1,8 +1,10 @@
-using Microsoft.AspNetCore.Components.RenderTree;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.RenderTree;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Text.Encodings.Web;
namespace Bunit
@@ -188,26 +190,16 @@ private static int RenderAttributes(
var candidateIndex = position + i;
ref var frame = ref frames.Array[candidateIndex];
- // NOTE: The following is the original from HtmlRenderer.cs
- //
- // if (frame.FrameType != RenderTreeFrameType.Attribute)
- // {
- // return candidateIndex;
- // }
- //
- // The next two if block have been added instead.
- // This will enable verification of element ref capture in unit tests.
- if (frame.FrameType != RenderTreeFrameType.Attribute && frame.FrameType != RenderTreeFrameType.ElementReferenceCapture)
+ // Added to write ElementReferenceCaptureId to DOM
+ if (frame.FrameType == RenderTreeFrameType.ElementReferenceCapture)
{
- return candidateIndex;
+ result.Add($" {ELEMENT_REFERENCE_ATTR_NAME}=\"{frame.ElementReferenceCaptureId}\"");
}
- if (frame.FrameType == RenderTreeFrameType.ElementReferenceCapture)
+ if (frame.FrameType != RenderTreeFrameType.Attribute)
{
- result.Add($" {ELEMENT_REFERENCE_ATTR_NAME}=\"{frame.AttributeName}\"");
return candidateIndex;
}
- // End of addition
if (frame.AttributeName.Equals("value", StringComparison.OrdinalIgnoreCase))
{
diff --git a/src/bunit.csproj b/src/bunit.csproj
index dc0c32470..55649adb3 100644
--- a/src/bunit.csproj
+++ b/src/bunit.csproj
@@ -49,7 +49,7 @@
-
+
diff --git a/tests/BlazorE2E/ComponentRenderingTest.cs b/tests/BlazorE2E/ComponentRenderingTest.cs
index ffa757af4..37e302093 100644
--- a/tests/BlazorE2E/ComponentRenderingTest.cs
+++ b/tests/BlazorE2E/ComponentRenderingTest.cs
@@ -4,6 +4,8 @@
using System.Numerics;
using Bunit.BlazorE2E.BasicTestApp;
using Bunit.BlazorE2E.BasicTestApp.HierarchicalImportsTest.Subdir;
+using Bunit.Mocking.JSInterop;
+using Microsoft.AspNetCore.Components;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
@@ -299,36 +301,37 @@ public void CanUseViewImportsHierarchically()
elem => Assert.Equal(typeof(AssemblyHashAlgorithm).FullName, elem.TextContent));
}
- [Fact(Skip = "Test doesn't make sense in this context")]
- public void CanUseComponentAndStaticContentFromExternalNuGetPackage()
- {
- //var appElement = Browser.MountTestComponent();
-
- //// NuGet packages can use JS interop features to provide
- //// .NET code access to browser APIs
- //var showPromptButton = appElement.FindElements(By.TagName("button")).First();
- //showPromptButton.Click();
-
- //var modal = new WebDriverWait(Browser, TimeSpan.FromSeconds(3))
- // .Until(SwitchToAlert);
- //modal.SendKeys("Some value from test");
- //modal.Accept();
- //var promptResult = appElement.FindElement(By.TagName("strong"));
- //Browser.Equal("Some value from test", () => promptResult.Text);
-
- //// NuGet packages can also embed entire components (themselves
- //// authored as Razor files), including static content. The CSS value
- //// here is in a .css file, so if it's correct we know that static content
- //// file was loaded.
- //var specialStyleDiv = appElement.FindElement(By.ClassName("special-style"));
- //Assert.Equal("50px", specialStyleDiv.GetCssValue("padding"));
-
- //// The external components are fully functional, not just static HTML
- //var externalComponentButton = specialStyleDiv.FindElement(By.TagName("button"));
- //Assert.Equal("Click me", externalComponentButton.Text);
- //externalComponentButton.Click();
- //Browser.Equal("It works", () => externalComponentButton.Text);
- }
+ // Test removed since doesn't make sense in this context.
+ //[Fact]
+ //public void CanUseComponentAndStaticContentFromExternalNuGetPackage()
+ //{
+ // var appElement = Browser.MountTestComponent();
+
+ // // NuGet packages can use JS interop features to provide
+ // // .NET code access to browser APIs
+ // var showPromptButton = appElement.FindElements(By.TagName("button")).First();
+ // showPromptButton.Click();
+
+ // var modal = new WebDriverWait(Browser, TimeSpan.FromSeconds(3))
+ // .Until(SwitchToAlert);
+ // modal.SendKeys("Some value from test");
+ // modal.Accept();
+ // var promptResult = appElement.FindElement(By.TagName("strong"));
+ // Browser.Equal("Some value from test", () => promptResult.Text);
+
+ // // NuGet packages can also embed entire components (themselves
+ // // authored as Razor files), including static content. The CSS value
+ // // here is in a .css file, so if it's correct we know that static content
+ // // file was loaded.
+ // var specialStyleDiv = appElement.FindElement(By.ClassName("special-style"));
+ // Assert.Equal("50px", specialStyleDiv.GetCssValue("padding"));
+
+ // // The external components are fully functional, not just static HTML
+ // var externalComponentButton = specialStyleDiv.FindElement(By.TagName("button"));
+ // Assert.Equal("Click me", externalComponentButton.Text);
+ // externalComponentButton.Click();
+ // Browser.Equal("It works", () => externalComponentButton.Text);
+ //}
[Fact]
public void CanRenderSvgWithCorrectNamespace()
@@ -365,24 +368,40 @@ public void LogicalElementInsertionWorksHierarchically()
cut.MarkupMatches("First Second Third");
}
- [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ [Fact]
public void CanUseJsInteropToReferenceElements()
{
- //var cut = RenderComponent();
- //var inputElement = cut.Find("#capturedElement");
- //var buttonElement = cut.Find("button");
+ // NOTE: This test required JS to modify the DOM. Test rewritten to use MockJsRuntime
+ // The original test code is here:
+ // var cut = RenderComponent();
+ // var inputElement = cut.Find("#capturedElement");
+ // var buttonElement = cut.Find("button");
- //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
+ // Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
- //buttonElement.Click();
- //Assert.Equal("Clicks: 1", inputElement.GetAttribute("value"));
- //buttonElement.Click();
- //Assert.Equal("Clicks: 2", inputElement.GetAttribute("value"));
+ // buttonElement.Click();
+ // Assert.Equal("Clicks: 1", inputElement.GetAttribute("value"));
+ // buttonElement.Click();
+ // Assert.Equal("Clicks: 2", inputElement.GetAttribute("value"));
+
+ var mockJs = Services.AddMockJsRuntime();
+ var cut = RenderComponent();
+ var inputElement = cut.Find("#capturedElement");
+ var refId = inputElement.GetAttribute(Htmlizer.ELEMENT_REFERENCE_ATTR_NAME);
+ var buttonElement = cut.Find("button");
+
+ buttonElement.Click();
+ mockJs.VerifyInvoke("setElementValue")
+ .Arguments[0]
+ .ShouldBeOfType()
+ .Id.ShouldBe(refId);
}
- [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
+ [Fact]
public void CanCaptureReferencesToDynamicallyAddedElements()
{
+ // NOTE: This test required JS to modify the DOM. Test rewritten to use MockJsRuntime
+ // The original test code is here:
//var cut = RenderComponent();
//var buttonElement = cut.Find("button");
//var checkbox = cut.Find("input[type=checkbox]");
@@ -403,6 +422,33 @@ public void CanCaptureReferencesToDynamicallyAddedElements()
//// See that the capture variable was automatically updated to reference the new instance
//buttonElement.Click();
//Assert.Equal("Clicks: 1", () => inputElement.GetAttribute("value"));
+
+ var mockJs = Services.AddMockJsRuntime();
+
+ var cut = RenderComponent();
+ var buttonElement = cut.Find("button");
+ var checkbox = cut.Find("input[type=checkbox]");
+
+ // We're going to remove the input. But first, put in some contents
+ // so we can observe it's not the same instance later
+ cut.Find("#capturedElement");
+
+ // Remove the captured element
+ checkbox.Change(false);
+ Should.Throw(() => cut.Find("#capturedElement"));
+
+ // Re-add it; observe it starts empty again
+ checkbox.Change(true);
+ var inputElement = cut.Find("#capturedElement");
+ var refId = inputElement.GetAttribute(Htmlizer.ELEMENT_REFERENCE_ATTR_NAME);
+
+ // See that the capture variable was automatically updated to reference the new instance
+ buttonElement.Click();
+
+ mockJs.VerifyInvoke("setElementValue")
+ .Arguments[0]
+ .ShouldBeOfType()
+ .Id.ShouldBe(refId);
}
[Fact]
@@ -436,12 +482,13 @@ public void CanCaptureReferencesToDynamicallyAddedComponents()
Assert.Equal("Current count: 0", currentCountText());
}
- [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
- public void CanUseJsInteropForRefElementsDuringOnAfterRender()
- {
- //var cut = RenderComponent();
- //Assert.Equal("Value set after render", () => Browser.Find("input").GetAttribute("value"));
- }
+ // Test depends on javascript changing the DOM, thus doesnt make sense in this context.
+ //[Fact]
+ //public void CanUseJsInteropForRefElementsDuringOnAfterRender()
+ //{
+ // var cut = RenderComponent();
+ // Assert.Equal("Value set after render", () => Browser.Find("input").GetAttribute("value"));
+ //}
[Fact]
public void CanRenderMarkupBlocks()
@@ -521,7 +568,7 @@ public void CanAcceptSimultaneousRenderRequests()
cut.WaitForAssertion(
() => Assert.Equal(expectedOutput, outputElement.TextContent.Trim()),
- timeout: TimeSpan.FromMilliseconds(2000)
+ timeout: TimeSpan.FromSeconds(2000)
);
}
@@ -558,13 +605,14 @@ public void CanDispatchAsyncWorkToSyncContext()
cut.WaitForAssertion(() => Assert.Equal("First Second Third Fourth Fifth", result.TextContent.Trim()), timeout: TimeSpan.FromSeconds(2));
}
- [Fact(Skip = "Test depends on javascript changing the DOM, thus doesnt make sense in this context. Test recreated in TestRendererTest.")]
- public void CanPerformInteropImmediatelyOnComponentInsertion()
- {
- //var cut = RenderComponent();
- //Assert.Equal("Hello from interop call", () => cut.Find("#val-get-by-interop").TextContent);
- //Assert.Equal("Hello from interop call", () => cut.Find("#val-set-by-interop").GetAttribute("value"));
- }
+ // Test removed since it does not have any value in this context.
+ //[Fact]
+ //public void CanPerformInteropImmediatelyOnComponentInsertion()
+ //{
+ // var cut = RenderComponent();
+ // Assert.Equal("Hello from interop call", () => cut.Find("#val-get-by-interop").TextContent);
+ // Assert.Equal("Hello from interop call", () => cut.Find("#val-set-by-interop").GetAttribute("value"));
+ //}
[Fact]
public void CanUseAddMultipleAttributes()
diff --git a/tests/Rendering/RenderedFragmentTest.cs b/tests/Rendering/RenderedFragmentTest.cs
index b1fa96751..84cdf03a6 100644
--- a/tests/Rendering/RenderedFragmentTest.cs
+++ b/tests/Rendering/RenderedFragmentTest.cs
@@ -1,10 +1,10 @@
-using Bunit.SampleComponents;
+using Bunit.Extensions;
+using Bunit.SampleComponents;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
using Shouldly;
using Xunit;
using Xunit.Abstractions;
-using Xunit.Sdk;
namespace Bunit
{
diff --git a/tests/TestRendererTest.cs b/tests/TestRendererTest.cs
index fa9c128a5..ced3d8bee 100644
--- a/tests/TestRendererTest.cs
+++ b/tests/TestRendererTest.cs
@@ -1,4 +1,10 @@
-using Bunit.SampleComponents;
+using System;
+using System.Linq;
+using Bunit.BlazorE2E.BasicTestApp;
+using Bunit.Extensions;
+using Bunit.Mocking.JSInterop;
+using Bunit.SampleComponents;
+using Microsoft.AspNetCore.Components;
using Shouldly;
using Xunit;
@@ -11,53 +17,12 @@ public void Test001()
{
var res = new ConcurrentRenderEventSubscriber(Renderer.RenderEvents);
var sut = RenderComponent();
-
+
res.RenderCount.ShouldBe(1);
sut.Find("button").Click();
-
- res.RenderCount.ShouldBe(2);
- }
-
- [Fact(DisplayName = "Can pass reference to elements to JsInterop")]
- public void Test010()
- {
- //var cut = RenderComponent();
- //var inputElement = cut.Find("#capturedElement");
- //var buttonElement = cut.Find("button");
-
- //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
-
- //buttonElement.Click();
- //Assert.Equal("Clicks: 1", inputElement.GetAttribute("value"));
- //buttonElement.Click();
- //Assert.Equal("Clicks: 2", inputElement.GetAttribute("value"));
- }
- [Fact(DisplayName = "Can capture reference to dynamically added elements and pass to JsInterop")]
- public void Test011()
- {
- //var cut = RenderComponent();
- //var buttonElement = cut.Find("button");
- //var checkbox = cut.Find("input[type=checkbox]");
-
- //// We're going to remove the input. But first, put in some contents
- //// so we can observe it's not the same instance later
- //cut.Find("#capturedElement").SendKeys("some text");
-
- //// Remove the captured element
- //checkbox.Click();
- //Browser.Empty(() => cut.FindAll("#capturedElement"));
-
- //// Re-add it; observe it starts empty again
- //checkbox.Click();
- //var inputElement = cut.Find("#capturedElement");
- //Assert.Equal(string.Empty, inputElement.GetAttribute("value"));
-
- //// See that the capture variable was automatically updated to reference the new instance
- //buttonElement.Click();
- //Assert.Equal("Clicks: 1", () => inputElement.GetAttribute("value"));
+ res.RenderCount.ShouldBe(2);
}
-
}
}
From 0e679ea748e7e4a22ee243a8011c2df2158622d4 Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 15:53:39 +0000
Subject: [PATCH 24/27] Changed anglesharp.wrappers version dep
---
src/bunit.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/bunit.csproj b/src/bunit.csproj
index 55649adb3..dc0c32470 100644
--- a/src/bunit.csproj
+++ b/src/bunit.csproj
@@ -49,7 +49,7 @@
-
+
From 561495278613ce2ebbfbf0d8e187885dca1561c5 Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 16:36:37 +0000
Subject: [PATCH 25/27] Tweaks to template
---
src/bunit.csproj | 11 ++++++-----
template/template/.template.config/template.json | 6 +++---
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/bunit.csproj b/src/bunit.csproj
index dc0c32470..d50e65f64 100644
--- a/src/bunit.csproj
+++ b/src/bunit.csproj
@@ -14,6 +14,7 @@
LICENSEhttps://github.com/egil/razor-components-testing-librarygit
+ 1.0.0-beta-6#{BRANCH}##{COMMIT}#https://github.com/egil/razor-components-testing-library
@@ -41,11 +42,10 @@
-
-
-
-
-
+
+
+
+
@@ -53,6 +53,7 @@
+
diff --git a/template/template/.template.config/template.json b/template/template/.template.config/template.json
index 4647c838c..e86ec4c11 100644
--- a/template/template/.template.config/template.json
+++ b/template/template/.template.config/template.json
@@ -2,14 +2,14 @@
"$schema": "http://json.schemastore.org/template",
"author": "Egil Hansen",
"classifications": [
- "Test", "Razor", "Blazor", "Library"
+ "Test", "bUnit", "Blazor"
],
- "name": "bUnit Testing Project",
+ "name": "bUnit Test Project",
"description": "A project for a testing Blazor/Razor components using the bUnit library.",
"generatorVersions": "[1.0.0.0-*)",
"identity": "BunitProject",
"groupIdentity": "Bunit",
- "shortName": "razortest",
+ "shortName": "bunit",
"tags": {
"language": "C#",
"type": "project"
From f7fe4495ea07b803c3e9922ff8e9704f895164e7 Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 16:42:22 +0000
Subject: [PATCH 26/27] Update CI.yml
---
.github/workflows/CI.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 53eb1ccc6..42def49ff 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -38,6 +38,6 @@ jobs:
- name: Verifying template
run: |
dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg
- dotnet new razortest -o ${GITHUB_WORKSPACE}/Test
+ dotnet new bunit -o ${GITHUB_WORKSPACE}/Test
dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib
dotnet test ${GITHUB_WORKSPACE}/Test
From 94eb85721cc5252090d8180141c516e56a750eac Mon Sep 17 00:00:00 2001
From: Egil Hansen
Date: Fri, 28 Feb 2020 16:42:44 +0000
Subject: [PATCH 27/27] Update nuget-pack-push.yml
---
.github/workflows/nuget-pack-push.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/nuget-pack-push.yml b/.github/workflows/nuget-pack-push.yml
index b875e8601..1ad5e15e8 100644
--- a/.github/workflows/nuget-pack-push.yml
+++ b/.github/workflows/nuget-pack-push.yml
@@ -41,7 +41,7 @@ jobs:
- name: Verifying template
run: |
dotnet new --install ${GITHUB_WORKSPACE}/template/bunit.template.$VERSION.nupkg
- dotnet new razortest -o ${GITHUB_WORKSPACE}/Test
+ dotnet new bunit -o ${GITHUB_WORKSPACE}/Test
dotnet restore ${GITHUB_WORKSPACE}/Test/Test.csproj --source ${GITHUB_WORKSPACE}/lib
dotnet test ${GITHUB_WORKSPACE}/Test
- name: Push packages to NuGet.org