Skip to content

Commit

Permalink
feat: Add Using directive for FluentAssertion if missing (#285)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 committed Jan 8, 2024
1 parent f7a5c04 commit 7d1c8b2
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 2 deletions.
142 changes: 142 additions & 0 deletions src/FluentAssertions.Analyzers.Tests/Tips/FluentAssertionsTests.cs
@@ -1,3 +1,4 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FluentAssertions.Analyzers.Tests
Expand Down Expand Up @@ -55,5 +56,146 @@ public class TestClassB
.WithSources(source)
);
}

[TestMethod]
[Implemented]
public void ShouldAddFluentAssertionsUsing_WhenFluentAssertionIsNotInScope_ForXunit()
=> ShouldAddFluentAssertionsUsing_WhenFluentAssertionIsNotInScope<XunitCodeFixProvider>("True", "using Xunit;", PackageReference.XunitAssert_2_5_1);

[TestMethod]
[Implemented]
public void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInGlobalScope_ForXunit()
=> ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInGlobalScope<XunitCodeFixProvider>("True", "using Xunit;", PackageReference.XunitAssert_2_5_1);

[TestMethod]
[Implemented]
public void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInAnyScope_ForXunit()
=> ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInAnyScope<XunitCodeFixProvider>("True", "using Xunit;", PackageReference.XunitAssert_2_5_1);

[TestMethod]
[Implemented]
public void ShouldAddFluentAssertionsUsing_WhenFluentAssertionIsNotInScope_ForMsTest()
=> ShouldAddFluentAssertionsUsing_WhenFluentAssertionIsNotInScope<MsTestCodeFixProvider>("IsTrue", "using Microsoft.VisualStudio.TestTools.UnitTesting;", PackageReference.MSTestTestFramework_3_1_1);

[TestMethod]
[Implemented]
public void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInGlobalScope_ForMsTest()
=> ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInGlobalScope<MsTestCodeFixProvider>("IsTrue", "using Microsoft.VisualStudio.TestTools.UnitTesting;", PackageReference.MSTestTestFramework_3_1_1);

[TestMethod]
[Implemented]
public void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInAnyScope_ForMsTest()
=> ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInAnyScope<MsTestCodeFixProvider>("IsTrue", "using Microsoft.VisualStudio.TestTools.UnitTesting;", PackageReference.MSTestTestFramework_3_1_1);

private void ShouldAddFluentAssertionsUsing_WhenFluentAssertionIsNotInScope<TCodeFixProvider>(string assertTrue, string usingDirective, PackageReference testingLibraryReference) where TCodeFixProvider : CodeFixProvider, new()
{
string source = $@"
{usingDirective}
namespace TestProject
{{
public class TestClass
{{
public void TestMethod(bool subject)
{{
Assert.{assertTrue}(subject);
}}
}}
}}";
string newSource = @$"
using FluentAssertions;
{usingDirective}
namespace TestProject
{{
public class TestClass
{{
public void TestMethod(bool subject)
{{
subject.Should().BeTrue();
}}
}}
}}";
DiagnosticVerifier.VerifyFix(new CodeFixVerifierArguments()
.WithDiagnosticAnalyzer<AssertAnalyzer>()
.WithCodeFixProvider<TCodeFixProvider>()
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, testingLibraryReference)
.WithSources(source)
.WithFixedSources(newSource)
);
}

private void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInGlobalScope<TCodeFixProvider>(string assertTrue, string usingDirective, PackageReference testingLibraryReference) where TCodeFixProvider : CodeFixProvider, new()
{
string source = $@"
{usingDirective}
namespace TestProject
{{
public class TestClass
{{
public void TestMethod(bool subject)
{{
Assert.{assertTrue}(subject);
}}
}}
}}";
const string globalUsings = "global using FluentAssertions;";
string newSource = @$"
{usingDirective}
namespace TestProject
{{
public class TestClass
{{
public void TestMethod(bool subject)
{{
subject.Should().BeTrue();
}}
}}
}}";

DiagnosticVerifier.VerifyFix(new CodeFixVerifierArguments()
.WithDiagnosticAnalyzer<AssertAnalyzer>()
.WithCodeFixProvider<TCodeFixProvider>()
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, testingLibraryReference)
.WithSources(source, globalUsings)
.WithFixedSources(newSource)
);
}

private void ShouldNotAddFluentAssertionsUsing_WhenFluentAssertionIsInAnyScope<TCodeFixProvider>(string assertTrue, string usingDirective, PackageReference testingLibraryReference) where TCodeFixProvider : CodeFixProvider, new()
{
string source = $@"
{usingDirective}
namespace TestProject
{{
using FluentAssertions;
public class TestClass
{{
public void TestMethod(bool subject)
{{
Assert.{assertTrue}(subject);
}}
}}
}}";
string newSource = @$"
{usingDirective}
namespace TestProject
{{
using FluentAssertions;
public class TestClass
{{
public void TestMethod(bool subject)
{{
subject.Should().BeTrue();
}}
}}
}}";

DiagnosticVerifier.VerifyFix(new CodeFixVerifierArguments()
.WithDiagnosticAnalyzer<AssertAnalyzer>()
.WithCodeFixProvider<TCodeFixProvider>()
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, testingLibraryReference)
.WithSources(source)
.WithFixedSources(newSource)
);
}
}
}
Expand Up @@ -12,7 +12,7 @@ public class MsTestCodeFixProvider : TestingFrameworkCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.MSTestsRule.Id);

protected override CreateChangedDocument TryComputeFix(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext testContext, Diagnostic diagnostic)
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext testContext, Diagnostic diagnostic)
{
var assertType = invocation.TargetMethod.ContainingType;
return assertType.Name switch
Expand Down
@@ -1,6 +1,17 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions.Analyzers.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Simplification;

namespace FluentAssertions.Analyzers;

Expand Down Expand Up @@ -71,6 +82,41 @@ protected static bool ArgumentsCount(IInvocationOperation invocation, int argume
return invocation.TargetMethod.Parameters.Length == arguments;
}

protected override Func<CancellationToken, Task<Document>> TryComputeFix(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
{
var fix = TryComputeFixCore(invocation, context, t, diagnostic);
if (fix is null)
{
return null;
}

return async ctx =>
{
const string fluentAssertionNamespace = "FluentAssertions";
var document = await fix(ctx);
var model = await document.GetSemanticModelAsync();
var scopes = model.GetImportScopes(diagnostic.Location.SourceSpan.Start);
var hasFluentAssertionImport = scopes.Any(scope => scope.Imports.Any(import => import.NamespaceOrType.ToString().Equals(fluentAssertionNamespace)));
if (hasFluentAssertionImport)
{
return document;
}
var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync();
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(fluentAssertionNamespace)).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation));
document = document.WithSyntaxRoot(root);
document = await Formatter.OrganizeImportsAsync(document);
return document;
};
}

protected abstract Func<CancellationToken, Task<Document>> TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic);


public sealed class TestingFrameworkCodeFixContext(Compilation compilation)
{
public INamedTypeSymbol Object { get; } = compilation.ObjectType;
Expand Down
Expand Up @@ -13,7 +13,7 @@ public class XunitCodeFixProvider : TestingFrameworkCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.XunitRule.Id);

protected override CreateChangedDocument TryComputeFix(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
{
switch (invocation.TargetMethod.Name)
{
Expand Down

0 comments on commit 7d1c8b2

Please sign in to comment.