Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions SignalRGen.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Shared", "src\Si
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen", "src\SignalRGen\SignalRGen.csproj", "{5075C86D-DBB7-4BFF-80B9-0BD6221CE322}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testing", "testing", "{CAB24F69-24CB-4B1D-BB8B-A616E265E7DA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Testing", "src\testing\SignalRGen.Testing\SignalRGen.Testing.csproj", "{E617AA36-F588-478B-AF49-64FA1E15A91F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Testing.Generator", "src\testing\SignalRGen.Testing.Generator\SignalRGen.Testing.Generator.csproj", "{D9A16ADA-A081-4721-9AF8-697B2E07BEFD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Testing.Abstractions", "src\testing\SignalRGen.Testing.Abstractions\SignalRGen.Testing.Abstractions.csproj", "{07B37834-5257-4FCD-9331-3421D78A6FCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Example.BlazorServer.Tests", "example\SignalRGen.Example.BlazorServer.Tests\SignalRGen.Example.BlazorServer.Tests.csproj", "{7EAEB231-6EA0-4277-A215-F79E6A452307}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRGen.Testing.Generator.Tests", "tests\SignalRGen.Testing.Generator.Tests\SignalRGen.Testing.Generator.Tests.csproj", "{1C28AB28-B383-46F6-883E-26ADD68905CC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -53,6 +65,12 @@ Global
{BAD25B70-315B-4CB0-8E49-52097052AE68} = {156E35FD-D926-4394-99BB-23CE485E357A}
{B85F8F20-B969-4247-9779-9D2F52C9C075} = {05C2839E-9804-4C02-8829-2E825E4D15CA}
{5075C86D-DBB7-4BFF-80B9-0BD6221CE322} = {05C2839E-9804-4C02-8829-2E825E4D15CA}
{CAB24F69-24CB-4B1D-BB8B-A616E265E7DA} = {05C2839E-9804-4C02-8829-2E825E4D15CA}
{E617AA36-F588-478B-AF49-64FA1E15A91F} = {CAB24F69-24CB-4B1D-BB8B-A616E265E7DA}
{D9A16ADA-A081-4721-9AF8-697B2E07BEFD} = {CAB24F69-24CB-4B1D-BB8B-A616E265E7DA}
{07B37834-5257-4FCD-9331-3421D78A6FCC} = {CAB24F69-24CB-4B1D-BB8B-A616E265E7DA}
{7EAEB231-6EA0-4277-A215-F79E6A452307} = {80DBF0E6-9611-4221-B4D5-7DBB90DB033B}
{1C28AB28-B383-46F6-883E-26ADD68905CC} = {156E35FD-D926-4394-99BB-23CE485E357A}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0D84BF69-5C50-40A9-BD7C-9507E609637E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -103,5 +121,25 @@ Global
{5075C86D-DBB7-4BFF-80B9-0BD6221CE322}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5075C86D-DBB7-4BFF-80B9-0BD6221CE322}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5075C86D-DBB7-4BFF-80B9-0BD6221CE322}.Release|Any CPU.Build.0 = Release|Any CPU
{E617AA36-F588-478B-AF49-64FA1E15A91F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E617AA36-F588-478B-AF49-64FA1E15A91F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E617AA36-F588-478B-AF49-64FA1E15A91F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E617AA36-F588-478B-AF49-64FA1E15A91F}.Release|Any CPU.Build.0 = Release|Any CPU
{D9A16ADA-A081-4721-9AF8-697B2E07BEFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9A16ADA-A081-4721-9AF8-697B2E07BEFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9A16ADA-A081-4721-9AF8-697B2E07BEFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9A16ADA-A081-4721-9AF8-697B2E07BEFD}.Release|Any CPU.Build.0 = Release|Any CPU
{07B37834-5257-4FCD-9331-3421D78A6FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07B37834-5257-4FCD-9331-3421D78A6FCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07B37834-5257-4FCD-9331-3421D78A6FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07B37834-5257-4FCD-9331-3421D78A6FCC}.Release|Any CPU.Build.0 = Release|Any CPU
{7EAEB231-6EA0-4277-A215-F79E6A452307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EAEB231-6EA0-4277-A215-F79E6A452307}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EAEB231-6EA0-4277-A215-F79E6A452307}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EAEB231-6EA0-4277-A215-F79E6A452307}.Release|Any CPU.Build.0 = Release|Any CPU
{1C28AB28-B383-46F6-883E-26ADD68905CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C28AB28-B383-46F6-883E-26ADD68905CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C28AB28-B383-46F6-883E-26ADD68905CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C28AB28-B383-46F6-883E-26ADD68905CC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
154 changes: 154 additions & 0 deletions example/SignalRGen.Example.BlazorServer.Tests/Pages/ChatTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using SignalRGen.Example.Client.BlazorServer.Pages;
using SignalRGen.Example.Contracts;
using SignalRGen.Example.Contracts.TestFakes;

namespace SignalRGen.Example.BlazorServer.Tests.Pages;

public class ChatTests : BunitContext, IAsyncLifetime
{
public ChatTests()
{
// We tell the bUnit-Services to use the fake that is generated by SignalRGen instead of the "real" HubClient
// Note: before we have the `FakeChatHubContractClient` in this example, we have to define that we want a fake
// for the HubClient `ChatHubContractClient`.
// This is done in the `Properties/AssemblyInfo.cs` file.
Services.AddSingleton<ChatHubContractClient, FakeChatHubContractClient>();
}

[Fact]
public async Task Join_Chat_Updates_UI_To_Chat_Mode()
{
// Arrange
await using var fakeClient = await GetFake();
var cut = Render<Chat>();

// Act
cut.Find("#username-input").Input("TestUser");
await cut.Find("#join-button").ClickAsync();

// Assert
var userName = cut.Find(".chat-header span strong");
Assert.Equal("TestUser", userName.TextContent);
Assert.NotNull(cut.Find("#message-input"));
}

[Fact]
public async Task Receiving_UserJoined_Event_Shows_Notification()
{
// Arrange
await using var fakeClient = await GetFake();
var cut = Render<Chat>();
cut.Find("#username-input").Input("Me");
await cut.Find("#join-button").ClickAsync();

// Act
// We use the fake HubClient to simulate that another user has joined the chat.
await fakeClient.SimulateOnUserJoinedAsync("NewUser");

// Assert
// To wait until we actually received the message that the new user has joined, we use another method from the
// fake HubClient. This guarantees that our test is not flaky.
await fakeClient.WaitForOnUserJoinedAsync();
await cut.WaitForStateAsync(() =>
cut.FindAll(".msg").Any(m => m.TextContent.Contains("User 'NewUser' joined")));
}



[Fact]
public async Task Receiving_UserLeft_Event_Shows_Notification()
{
// Arrange
await using var fakeClient = await GetFake();
var cut = Render<Chat>();
cut.Find("#username-input").Input("Me");
await cut.Find("#join-button").ClickAsync();

// Act
// We use the fake HubClient to simulate that another user has left the chat.
await fakeClient.SimulateOnUserLeftAsync("OldUser");

// Assert
// To wait until we actually received the message that the other user has left, we use another method from the
// fake HubClient. This guarantees that our test is not flaky.
await fakeClient.WaitForOnUserLeftAsync();
await cut.WaitForStateAsync(() =>
cut.FindAll(".msg").Any(m => m.TextContent.Contains("User 'OldUser' left")));
}

[Fact]
public async Task Receiving_Message_Shows_In_Chat()
{
// Arrange
await using var fakeClient = await GetFake();
var cut = Render<Chat>();
cut.Find("#username-input").Input("Me");
await cut.Find("#join-button").ClickAsync();

// Act
// We use the fake HubClient to simulate that another user has sent a message to the chat.
await fakeClient.SimulateOnMessageReceivedAsync(new ChatMessage("Alice", "Hello World", DateTime.UtcNow));

// Assert
// To wait until we actually received the message that the other user has sent, we use another method from the
// fake HubClient. This guarantees that our test is not flaky.
await fakeClient.WaitForOnMessageReceivedAsync();
await cut.WaitForStateAsync(() =>
{
var readOnlyList = cut.FindAll(".msg");
return readOnlyList.Any(m => m.TextContent.Contains("Alice: Hello World"));
});
}

[Fact]
public async Task Sending_Message_Calls_Hub_Method_And_Updates_UI()
{
// Arrange
await using var fakeClient = await GetFake();
var cut = Render<Chat>();
cut.Find("#username-input").Input("Me");
await cut.Find("#join-button").ClickAsync();

// Act
cut.Find("#message-input").Input("My secret message");
await cut.Find("#send-button").ClickAsync();

// Assert
Assert.Contains("My secret message", fakeClient.SendMessageCalls);

await cut.WaitForStateAsync(() =>
cut.FindAll(".msg").Any(m => m.TextContent.Contains("Me: My secret message")));
}

// This is a little helper to retrieve the fake HubClient
private async Task<FakeChatHubContractClient> GetFake()
{
FakeChatHubContractClient? fakeClient = null;
try
{
fakeClient = Services.GetRequiredService<ChatHubContractClient>() as FakeChatHubContractClient;
ArgumentNullException.ThrowIfNull(fakeClient);
return fakeClient;
}
catch
{
if (fakeClient != null) await fakeClient.DisposeAsync();
throw;
}
}

public Task InitializeAsync()
{
return Task.CompletedTask;
}

// This is required as the bUnit scope is normally disposed synchronously, however, the HubClient needs to be
// disposed asynchronously to properly dispose of everything.
// By implementing this we tell bUnit to dispose in an async fashion.
public new async Task DisposeAsync()
{
await base.DisposeAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using SignalRGen.Example.Contracts;
using SignalRGen.Testing.Abstractions.Attributes;

// With these attributes we tell SignalRGen.Testing for which HubClients we want a fake generated.
[assembly: GenerateFakeForHubClient(typeof(ExampleHubClient))]
[assembly: GenerateFakeForHubClient(typeof(ChatHubContractClient))]
[assembly: GenerateFakeForHubClient(typeof(BasicHubClient))]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="bunit" Version="2.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="xunit" Version="2.9.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"/>
</ItemGroup>

<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\testing\SignalRGen.Testing.Abstractions\SignalRGen.Testing.Abstractions.csproj" />
<ProjectReference Include="..\..\src\testing\SignalRGen.Testing.Generator\SignalRGen.Testing.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\SignalRGen.Example.Client.BlazorServer\SignalRGen.Example.Client.BlazorServer.csproj" />
</ItemGroup>

</Project>
Loading