Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ _dotTrace*

# NuGet tools
.nuget/

**/TestResult.xml
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog
All notable changes to this project will be documented in this file.

## 26.09.04XX
## 26.09.19XX
- "Go to Handler" now also works with the caret anywhere inside a `mediator.Send(...)` /
`Publish(...)` call: the first argument's type is resolved, so there is no need to navigate to the
request declaration first.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ public static class TestSolutionFiles
{
private static readonly string TestSolutionPath = ResolveTestSolutionPath();

/// <summary>
/// Absolute path to the TestSolution folder, used to turn an on-disk file path (e.g. a
/// resolved handler's source file) back into the relative path form used elsewhere in these
/// tests (e.g. "MediatR/Handlers/GetEntityHandler.cs").
/// </summary>
public static string RootPath => TestSolutionPath;

public static string[] MediatR { get; } = LibraryFiles("MediatR");

public static string[] Mediator { get; } = LibraryFiles("Mediator");
Expand Down
55 changes: 54 additions & 1 deletion src/dotnet/MediatorPlugin.Tests/LookupTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
Expand All @@ -9,6 +10,7 @@
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.ReSharper.TestFramework;
using NUnit.Framework;
using Octelys.MediatorPlugin.Tests.Infrastructure;
using ReSharper.MediatorPlugin.Services.Libraries;

namespace Octelys.MediatorPlugin.Tests;
Expand All @@ -23,6 +25,7 @@ public abstract class LookupTestBase : BaseTestWithSingleProject
{
private string _requestTypeName;
private string[] _expectedHandlerNames;
private string[] _expectedHandlerFilePaths;

protected override string RelativeTestDataPath => "TestSolution";

Expand All @@ -42,7 +45,18 @@ ISolution solution
{
IIdentifier requestIdentifier = FindDeclarationIdentifier(solution, _requestTypeName);

IEnumerable<ITypeElement> handlers = new LibraryAdaptor().FindHandlers(requestIdentifier);
ITypeElement[] handlers = new LibraryAdaptor().FindHandlers(requestIdentifier).ToArray();

if (_expectedHandlerFilePaths is not null)
{
string[] handlerFilePaths = handlers
.SelectMany(GetRelativeFilePaths)
.OrderBy(path => path)
.ToArray();

Assert.That(handlerFilePaths, Is.EqualTo(_expectedHandlerFilePaths.OrderBy(path => path).ToArray()));
return;
}

string[] handlerNames = handlers
.Select(handler => handler.ShortName)
Expand All @@ -66,10 +80,49 @@ params string[] fileNames
{
_requestTypeName = requestTypeName;
_expectedHandlerNames = expectedHandlerNames;
_expectedHandlerFilePaths = null;

DoTestSolution(fileNames);
}

/// <summary>
/// Loads the given files as a test solution and asserts that the request or notification
/// resolves to handlers declared in exactly the expected files (relative to
/// test/data/TestSolution, e.g. "MediatR/Handlers/GetEntityHandler.cs") - i.e. the file(s)
/// "Go to Handler" would navigate to when the caret sits on the request. The lookup itself
/// runs in <see cref="DoTest" />.
/// </summary>
protected void AssertHandlerFilesFor
(
string requestTypeName,
string[] expectedHandlerFilePaths,
params string[] fileNames
)
{
_requestTypeName = requestTypeName;
_expectedHandlerNames = null;
_expectedHandlerFilePaths = expectedHandlerFilePaths;

DoTestSolution(fileNames);
}

/// <summary>
/// Resolves the on-disk file(s) declaring the given handler back to their path relative to
/// test/data/TestSolution, matching the relative path form used to load test files (e.g. via
/// <see cref="TestSolutionFiles" />).
/// </summary>
private static IEnumerable<string> GetRelativeFilePaths
(
ITypeElement handler
)
{
return handler
.GetSourceFiles()
.Select(sourceFile => sourceFile.GetLocation().FullPath)
.Select(fullPath => fullPath.Substring(TestSolutionFiles.RootPath.Length + 1))
.Select(relativePath => relativePath.Replace(Path.DirectorySeparatorChar, '/'));
}

/// <summary>
/// Returns the name token of the type declaration, which is the node the plugin receives
/// when the caret sits on a request in the editor. <paramref name="typeName" /> may be a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Octelys.MediatorPlugin.Tests.Infrastructure;

namespace Octelys.MediatorPlugin.Tests;

[MediatRV12TestPackages]
public sealed class MediatrGoToHandlerCaretTestsV12 : MediatrGoToHandlerCaretTestsBase
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Octelys.MediatorPlugin.Tests.Infrastructure;

namespace Octelys.MediatorPlugin.Tests;

[MediatRV13TestPackages]
public sealed class MediatrGoToHandlerCaretTestsV13 : MediatrGoToHandlerCaretTestsBase
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Octelys.MediatorPlugin.Tests.Infrastructure;

namespace Octelys.MediatorPlugin.Tests;

[MediatRV14TestPackages]
public sealed class MediatrGoToHandlerCaretTestsV14 : MediatrGoToHandlerCaretTestsBase
{
}
84 changes: 84 additions & 0 deletions src/dotnet/MediatorPlugin.Tests/MediatR.GoToHandler.CaretTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.IO;
using System.Linq;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.Util;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.ReSharper.TestFramework;
using JetBrains.TextControl;
using NUnit.Framework;
using ReSharper.MediatorPlugin.Services.Libraries;

namespace Octelys.MediatorPlugin.Tests;

/// <summary>
/// Positions a real caret (via the "{caret}" marker in each test's data file) and asserts which
/// handler file the plugin resolves, if any, for the identifier under it. Unlike <see cref="LookupTestBase" />,
/// which locates the request's declaration identifier directly through the PSI, this test goes
/// through the same caret-to-PSI resolution (<see cref="TextControlToPsi" />) that the plugin's
/// own actions use (e.g. GoToHandlerAction.GetSelectedMediatrRequest), so it exercises the literal
/// "caret sits on this token" scenario rather than simulating it by name. <see cref="CaretInNamespace" />
/// and <see cref="CaretOnSendCall" /> place the caret on identifiers that are not a Mediator request
/// declaration and assert that no handler is resolved.
/// </summary>
[TestNetCoreLatest]
[TestReferences("System.Runtime")]
public abstract class MediatrGoToHandlerCaretTestsBase : BaseTestWithTextControl
{
private static readonly string[] TestsExpectingNoHandler =
{
nameof(CaretInNamespace),
nameof(CaretOnSendCall)
};

protected override string RelativeTestDataPath => "GoToHandlerCaretTests";

[Test]
public void Request() => DoNamedTest("Handler.cs");

[Test]
public void RequestCaretAtBeginning() => DoNamedTest("Handler.cs");

[Test]
public void CaretInNamespace() => DoNamedTest("Handler.cs");

[Test]
public void CaretOnSendCall() => DoNamedTest("Handler.cs");

protected override void DoTest
(
Lifetime lifetime,
ISolution solution
)
{
solution.GetPsiServices().Files.CommitAllDocuments();

using (ReadLockCookie.Create())
{
ITextControl textControl = OpenTextControl(lifetime, GetCaretPosition());

IIdentifier requestIdentifier = TextControlToPsi.GetElement<IIdentifier>(solution, textControl);

Assert.That(requestIdentifier, Is.Not.Null, "No identifier was found at the caret.");

ITypeElement[] handlers = new LibraryAdaptor().FindHandlers(requestIdentifier).ToArray();

if (TestsExpectingNoHandler.Contains(TestMethodName))
{
Assert.That(handlers, Is.Empty, "Expected no handler to be resolved from this caret position.");

return;
}

string handlerFileName = handlers
.Single()
.GetSourceFiles()
.Select(sourceFile => Path.GetFileName(sourceFile.GetLocation().FullPath))
.Single();

Assert.That(handlerFileName, Is.EqualTo("Handler.cs"));
}
}
}
12 changes: 12 additions & 0 deletions src/dotnet/MediatorPlugin.Tests/MediatR.Requests.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,16 @@ public void FindHandlers_HandlerImplementsSharedGenericHandlerInterface_HandlerR
TestSolutionFiles.MediatR
);
}

[Test]
public void FindHandlers_CaretOnRequest_HandlerFileReturned()
{
// Act & Assert.
AssertHandlerFilesFor
(
"Domain.GetEntityRequest",
["MediatR/Handlers/GetEntityHandler.cs"],
TestSolutionFiles.MediatR
);
}
}
9 changes: 9 additions & 0 deletions src/dotnet/MediatorPlugin.Tests/MediatorPlugin.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@
<Compile Update="Mediator.Requests.V3.Tests.cs">
<DependentUpon>Mediator.Requests.Tests.cs</DependentUpon>
</Compile>
<Compile Update="MediatR.GoToHandler.CaretTests.V12.cs">
<DependentUpon>MediatR.GoToHandler.CaretTests.cs</DependentUpon>
</Compile>
<Compile Update="MediatR.GoToHandler.CaretTests.V13.cs">
<DependentUpon>MediatR.GoToHandler.CaretTests.cs</DependentUpon>
</Compile>
<Compile Update="MediatR.GoToHandler.CaretTests.V14.cs">
<DependentUpon>MediatR.GoToHandler.CaretTests.cs</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using MediatR;

namespace {caret}Domain;

public class GetEntityRequest : IRequest<string>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;

namespace Domain;

public class GetEntityRequest : IRequest<string>
{
}

public class GetEntityRequestSender
{
private readonly IMediator _mediator;

public GetEntityRequestSender(IMediator mediator)
{
_mediator = mediator;
}

public Task<string> SendAsync(CancellationToken cancellationToken)
{
return _mediator.{caret}Send(new GetEntityRequest(), cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Threading;
using System.Threading.Tasks;
using Domain;
using MediatR;

namespace Application;

internal sealed class GetEntityRequestHandler : IRequestHandler<GetEntityRequest, string>
{
public Task<string> Handle(GetEntityRequest request, CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using MediatR;

namespace Domain;

public class GetEntity{caret}Request : IRequest<string>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using MediatR;

namespace Domain;

public class {caret}GetEntityRequest : IRequest<string>
{
}
19 changes: 19 additions & 0 deletions test-solution/DotNetCore6Project/MediatR/MediatRCallsite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using DotNetCore6_Domain.MediatR.Requests;
using MediatR;

namespace DotNetCore6_Domain.MediatR;

public class MediatRCallsite
{
private readonly IMediator _mediator;

public MediatRCallsite(IMediator mediator)
{
_mediator = mediator;
}

public async Task RunAsync(CancellationToken cancellationToken)
{
await _mediator.Send(new CustomClassRequest(), cancellationToken);
}
}
19 changes: 19 additions & 0 deletions test-solution/DotNetCore6Project/Mediator/MediatorCallsite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using DotNetCore6_Domain.Mediator.Requests;
using MediatR;

namespace DotNetCore6_Domain.Mediator;

public class MediatorCallsite
{
private readonly IMediator _mediator;

public MediatorCallsite(IMediator mediator)
{
_mediator = mediator;
}

public async Task RunAsync(CancellationToken cancellationToken)
{
await _mediator.Send(new NewMediatorRequest(), cancellationToken);
}
}
Loading