Skip to content

Commit

Permalink
Extend FeatureInfo with FolderPath information (#2090)
Browse files Browse the repository at this point in the history
* Extend FeatureInfo with FolderPath information

* Cleanup + changelog
  • Loading branch information
tzongithub committed Aug 12, 2020
1 parent 0f54260 commit 1a614f2
Show file tree
Hide file tree
Showing 19 changed files with 724 additions and 635 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ private void SetupTestClassInitializeMethod(TestClassGenerationContext generatio
new CodeObjectCreateExpression(typeof(FeatureInfo),
new CodeObjectCreateExpression(typeof(CultureInfo),
new CodePrimitiveExpression(generationContext.Feature.Language)),
new CodePrimitiveExpression(generationContext.Document.DocumentLocation?.FeatureFolderPath),
new CodePrimitiveExpression(generationContext.Feature.Name),
new CodePrimitiveExpression(generationContext.Feature.Description),
new CodeFieldReferenceExpression(
Expand Down
21 changes: 18 additions & 3 deletions TechTalk.SpecFlow.Generator/TestGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private CodeNamespace GenerateTestFileCode(FeatureFileInput featureFileInput)
SpecFlowDocument specFlowDocument;
using (var contentReader = featureFileInput.GetFeatureFileContentReader(projectSettings))
{
specFlowDocument = ParseContent(parser, contentReader, featureFileInput.GetFullPath(projectSettings));
specFlowDocument = ParseContent(parser, contentReader, GetSpecFlowDocumentLocation(featureFileInput));
}

var featureGenerator = featureGeneratorRegistry.CreateGenerator(specFlowDocument);
Expand All @@ -127,9 +127,24 @@ private CodeNamespace GenerateTestFileCode(FeatureFileInput featureFileInput)
return codeNamespace;
}

protected virtual SpecFlowDocument ParseContent(IGherkinParser parser, TextReader contentReader, string sourceFilePath)
private SpecFlowDocumentLocation GetSpecFlowDocumentLocation(FeatureFileInput featureFileInput)
{
return parser.Parse(contentReader, sourceFilePath);
return new SpecFlowDocumentLocation(
featureFileInput.GetFullPath(projectSettings),
GetFeatureFolderPath(featureFileInput.ProjectRelativePath));
}

private string GetFeatureFolderPath(string projectRelativeFilePath)
{
string directoryName = Path.GetDirectoryName(projectRelativeFilePath);
if (string.IsNullOrWhiteSpace(directoryName)) return null;

return string.Join("/", directoryName.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries));
}

protected virtual SpecFlowDocument ParseContent(IGherkinParser parser, TextReader contentReader, SpecFlowDocumentLocation documentLocation)
{
return parser.Parse(contentReader, documentLocation);
}

protected string GetTargetNamespace(FeatureFileInput featureFileInput)
Expand Down
2 changes: 1 addition & 1 deletion TechTalk.SpecFlow.Parser/IGherkinParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace TechTalk.SpecFlow.Parser
{
public interface IGherkinParser
{
SpecFlowDocument Parse(TextReader featureFileReader, string sourceFilePath);
SpecFlowDocument Parse(TextReader featureFileReader, SpecFlowDocumentLocation documentLocation);

IGherkinDialectProvider DialectProvider { get; }
}
Expand Down
8 changes: 4 additions & 4 deletions TechTalk.SpecFlow.Parser/SpecFlowDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ namespace TechTalk.SpecFlow.Parser
{
public class SpecFlowDocument : GherkinDocument
{
public SpecFlowDocument(SpecFlowFeature feature, Comment[] comments, string sourceFilePath) : base(feature, comments)
public SpecFlowDocument(SpecFlowFeature feature, Comment[] comments, SpecFlowDocumentLocation documentLocation) : base(feature, comments)
{
this.SourceFilePath = sourceFilePath;

DocumentLocation = documentLocation;
}

public SpecFlowFeature SpecFlowFeature => (SpecFlowFeature) Feature;

public string SourceFilePath { get; private set; }
public SpecFlowDocumentLocation DocumentLocation { get; private set; }

public string SourceFilePath => DocumentLocation?.SourceFilePath;
}
}
34 changes: 34 additions & 0 deletions TechTalk.SpecFlow.Parser/SpecFlowDocumentLocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace TechTalk.SpecFlow.Parser
{
public class SpecFlowDocumentLocation
{
public SpecFlowDocumentLocation(string sourceFilePath) : this(sourceFilePath, string.Empty)
{}

public SpecFlowDocumentLocation(string sourceFilePath, string featureFolderPath)
{
SourceFilePath = sourceFilePath;
FeatureFolderPath = featureFolderPath ?? string.Empty;
}

/// <summary>
/// Absolute path of the feature file in the file system
/// </summary>
public string SourceFilePath { get; private set; }

/// <summary>
/// Relative path within the project to the folder containing the feature, using '/' as path separator character
/// </summary>
/// <returns>A string representing the relative path or empty string if the feature file is in the root folder of the project</returns>
/// <example>
/// "[ProjectDir]\ARootFeature.feature" -> ""
/// </example>
/// <example>
/// "[ProjectDir]\Features\SomeFeature.feature" -> "Features"
/// </example>\
/// <example>
/// "[ProjectDir]\Features\Configuration\SomeConfigFeature.feature" -> "Features/Configuration"
/// </example>
public string FeatureFolderPath { get; private set; }
}
}
16 changes: 8 additions & 8 deletions TechTalk.SpecFlow.Parser/SpecFlowGherkinParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ private static StepKeyword GetStepKeyword(GherkinDialect dialect, string stepKey

private class SpecFlowAstBuilder : AstBuilder<SpecFlowDocument>
{
private readonly string sourceFilePath;
private readonly SpecFlowDocumentLocation documentLocation;
private ScenarioBlock scenarioBlock = ScenarioBlock.Given;

public SpecFlowAstBuilder(string sourceFilePath)
public SpecFlowAstBuilder(SpecFlowDocumentLocation documentLocation)
{
this.sourceFilePath = sourceFilePath;
this.documentLocation = documentLocation;
}

protected override Feature CreateFeature(Tag[] tags, Location location, string language, string keyword, string name, string description, IHasLocation[] children, AstNode node)
Expand Down Expand Up @@ -94,7 +94,7 @@ private void ResetBlock()

protected override GherkinDocument CreateGherkinDocument(Feature feature, Comment[] gherkinDocumentComments, AstNode node)
{
return new SpecFlowDocument((SpecFlowFeature)feature, gherkinDocumentComments, sourceFilePath);
return new SpecFlowDocument((SpecFlowFeature)feature, gherkinDocumentComments, documentLocation);
}

protected override Background CreateBackground(Location location, string keyword, string name, string description, Step[] steps, AstNode node)
Expand All @@ -104,9 +104,9 @@ protected override Background CreateBackground(Location location, string keyword
}
}

public SpecFlowDocument Parse(TextReader featureFileReader, string sourceFilePath)
public SpecFlowDocument Parse(TextReader featureFileReader, SpecFlowDocumentLocation documentLocation)
{
var parser = new Parser<SpecFlowDocument>(CreateAstBuilder(sourceFilePath));
var parser = new Parser<SpecFlowDocument>(CreateAstBuilder(documentLocation));
SpecFlowDocument specFlowDocument = parser.Parse(CreateTokenScanner(featureFileReader), CreateTokenMatcher());

CheckSemanticErrors(specFlowDocument);
Expand All @@ -124,9 +124,9 @@ protected virtual ITokenMatcher CreateTokenMatcher()
return new TokenMatcher(dialectProvider);
}

protected virtual IAstBuilder<SpecFlowDocument> CreateAstBuilder(string sourceFilePath)
protected virtual IAstBuilder<SpecFlowDocument> CreateAstBuilder(SpecFlowDocumentLocation documentLocation)
{
return new SpecFlowAstBuilder(sourceFilePath);
return new SpecFlowAstBuilder(documentLocation);
}

protected virtual void CheckSemanticErrors(SpecFlowDocument specFlowDocument)
Expand Down
10 changes: 7 additions & 3 deletions TechTalk.SpecFlow/FeatureInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ public class FeatureInfo
{
public string[] Tags { get; private set; }
public ProgrammingLanguage GenerationTargetLanguage { get; private set; }

public string FolderPath { get; private set; }

public string Title { get; private set; }
public string Description { get; private set; }
public CultureInfo Language { get; private set; }

public FeatureInfo(CultureInfo language, string title, string description, params string[] tags)
: this(language, title, description, ProgrammingLanguage.CSharp, tags)
public FeatureInfo(CultureInfo language, string folderPath, string title, string description, params string[] tags)
: this(language, folderPath, title, description, ProgrammingLanguage.CSharp, tags)
{
}

public FeatureInfo(CultureInfo language, string title, string description, ProgrammingLanguage programmingLanguage, params string[] tags)
public FeatureInfo(CultureInfo language, string folderPath, string title, string description, ProgrammingLanguage programmingLanguage, params string[] tags)
{
if (language.IsNeutralCulture)
{
Expand All @@ -27,6 +30,7 @@ public FeatureInfo(CultureInfo language, string title, string description, Progr
}

Language = language;
FolderPath = folderPath;
Title = title;
Description = description;
GenerationTargetLanguage = programmingLanguage;
Expand Down
31 changes: 31 additions & 0 deletions Tests/TechTalk.SpecFlow.GeneratorTests/TestGeneratorBasicsTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using FluentAssertions;
using Moq;
using Xunit;
Expand Down Expand Up @@ -222,5 +224,34 @@ public void Should_detect_outdated_test_file_based_on_context_based_up_to_date_c
});
result.IsUpToDate.Should().Be(false);
}

private static string AssertFolderPathArgument(string outputFile)
{
var match = Regex.Match(outputFile, @"new TechTalk.SpecFlow.FeatureInfo\([^;]*");
match.Success.Should().BeTrue("FeatureInfo ctor should be found in output");
var folderPathArgument = match.Value.Split(',')[1].Trim();
folderPathArgument.Should().StartWith("\"").And.EndWith("\"", "the folderPath argument should be a string");
return folderPathArgument;
}

[Fact]
public void Should_generate_empty_folderpath_when_file_in_project_root()
{
string outputFile = GenerateTestFromSimpleFeature(net35CSProjectSettings);

string folderPathArgument = AssertFolderPathArgument(outputFile);

folderPathArgument.Should().Be("\"\"");
}

[Fact]
public void Should_generate_folderpath_with_slash_separator_when_file_in_subfolder()
{
string outputFile = GenerateTestFromSimpleFeature(net35CSProjectSettings, Path.Combine("Folder1", "Folder2"));

string folderPathArgument = AssertFolderPathArgument(outputFile);

folderPathArgument.Should().Be("\"Folder1/Folder2\"");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public CodeNamespace GenerateCodeNamespaceFromFeature(string feature, bool paral
using (var reader = new StringReader(feature))
{
var parser = new SpecFlowGherkinParser(new CultureInfo("en-US"));
var document = parser.Parse(reader, "test.feature");
var document = parser.Parse(reader, new SpecFlowDocumentLocation("test.feature"));

var featureGenerator = CreateFeatureGenerator(parallelCode,ignoreParallelTags);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void XUnit2TestGeneratorProvider_ShouldSetDisplayNameForTheoryAttribute()
children: null
),
comments: null,
sourceFilePath: null),
documentLocation: null),
ns: null,
testClass: null,
testRunnerField: null,
Expand Down Expand Up @@ -280,7 +280,7 @@ public void XUnit2TestGeneratorProvider_ShouldSetDisplayNameForFactAttribute()
children: null
),
comments: null,
sourceFilePath: null),
documentLocation: null),
ns: null,
testClass: null,
testRunnerField: null,
Expand Down
Loading

0 comments on commit 1a614f2

Please sign in to comment.