@@ -22,83 +22,131 @@ string createTitle()
2222 var titleAttribute = methodInfo . GetCustomAttribute < StepTitleAttribute > ( true ) ;
2323 var executableAttribute = methodInfo . GetCustomAttribute < ExecutableAttribute > ( true ) ;
2424
25- var callerSuppliedTemplate = stepTextTemplate != null ;
25+ var callerSuppliedTemplate = stepTextTemplate is not null ;
2626 includeInputsInStepTitle ??= titleAttribute ? . IncludeInputsInStepTitle ;
27- stepTextTemplate ??= titleAttribute != null
27+
28+ stepTextTemplate ??= titleAttribute is not null
2829 ? ( NullIfEmpty ( titleAttribute . StepTitle ) ?? "" )
2930 : NullIfEmpty ( executableAttribute ? . StepTitle ) ;
30- var stepTextTemplateWasNotSupplied = string . IsNullOrWhiteSpace ( stepTextTemplate ) ;
3131
32+ var hasTemplate = ! string . IsNullOrWhiteSpace ( stepTextTemplate ) ;
3233 stepTextTemplate ??= methodInfo . Name ;
3334
34- var formattedStepTitle = string . Format ( Configurator . CultureInfo , stepTextTemplate , flatInputArray ) ;
35- var stepTitle = stepTextTemplateWasNotSupplied ? Configurator . Humanizer . Humanize ( formattedStepTitle ) : formattedStepTitle ;
36-
37- var shouldAddPrefix = stepTextTemplateWasNotSupplied
38- || IsPrimaryPrefix ( stepPrefix )
39- || ( ! callerSuppliedTemplate && AddGherkinPrefixToSecondarySteps ) ;
40-
41- if ( shouldAddPrefix )
42- stepTitle = AppendPrefix ( stepTitle , stepPrefix ) ;
35+ var formattedTitle = string . Format ( Configurator . CultureInfo , stepTextTemplate , flatInputArray ) ;
36+ var stepTitle = ( hasTemplate ? formattedTitle : Configurator . Humanizer . Humanize ( formattedTitle ) )
37+ ?? throw new InvalidOperationException ( $ "Failed to create a step title for method '{ methodInfo . Name } '.") ;
4338
44- if ( stepTextTemplate != formattedStepTitle && titleAttribute ? . IncludeInputsInStepTitle is null )
45- includeInputsInStepTitle ??= false ;
39+ if ( ShouldAddPrefix ( hasTemplate , callerSuppliedTemplate , stepPrefix ) )
40+ stepTitle = PrependPrefix ( stepTitle , stepPrefix ) ;
4641
47- if ( stepTitle ! . Contains ( '<' ) && stepTitle . Contains ( '>' ) && titleAttribute ? . IncludeInputsInStepTitle is null )
48- includeInputsInStepTitle ??= false ;
42+ includeInputsInStepTitle ??= ShouldIncludeInputs ( stepTitle , stepTextTemplate , formattedTitle , titleAttribute , methodInfo ) ;
4943
5044 if ( includeInputsInStepTitle ?? IncludeInputsInStepTitle )
51- {
52- var parameters = methodInfo . GetParameters ( ) ;
53- var stringFlatInputs =
54- inputArguments
55- . Select ( ( a , i ) => new { ParameterName = parameters [ i ] . Name , Value = a } )
56- . Select ( i =>
57- {
58- if ( testContext . Examples != null )
59- {
60- var matchingHeaders = testContext . Examples . Headers
61- . Where ( header => ExampleTable . HeaderMatches ( header , i . ParameterName ) || ExampleTable . HeaderMatches ( header , i . Value . Name ) )
62- . ToList ( ) ;
63-
64- if ( matchingHeaders . Count > 1 )
65- throw new AmbiguousMatchException ( $ "More than one headers for examples, match the parameter '{ i . ParameterName } ' provided for '{ methodInfo . Name } '") ;
66-
67- var matchingHeader = matchingHeaders . SingleOrDefault ( ) ;
68- if ( matchingHeader != null )
69- return string . Format ( "<{0}>" , matchingHeader ) ;
70- }
71- return i . Value . Value ? . FlattenArray ( ) ?? Array . Empty < string > ( ) ;
72- } )
73- . ToArray ( ) ;
74-
75- stepTitle = stepTitle + " " + string . Join ( ", " , stringFlatInputs . Select ( o => o . ToTextRepresentation ( ) ) ) ;
76- }
45+ stepTitle = AppendInputSuffix ( stepTitle , methodInfo , inputArguments , testContext ) ;
7746
7847 return stepTitle . Trim ( ) ;
7948 }
8049
8150 return new StepTitle ( createTitle ) ;
8251 }
8352
84- public StepTitle Create ( string title , string stepPrefix , ITestContext testContext ) => new ( AppendPrefix ( title , stepPrefix ) ) ;
53+ public StepTitle Create ( string title , string stepPrefix , ITestContext testContext ) =>
54+ new ( PrependPrefix ( title , stepPrefix ) ) ;
55+
56+ private bool ShouldAddPrefix ( bool hasTemplate , bool callerSuppliedTemplate , string stepPrefix ) =>
57+ ! hasTemplate
58+ || IsPrimaryPrefix ( stepPrefix )
59+ || ( ! callerSuppliedTemplate && AddGherkinPrefixToSecondarySteps ) ;
60+
61+ private static bool ? ShouldIncludeInputs (
62+ string stepTitle ,
63+ string stepTextTemplate ,
64+ string formattedTitle ,
65+ StepTitleAttribute ? titleAttribute ,
66+ MethodInfo methodInfo )
67+ {
68+ if ( titleAttribute ? . IncludeInputsInStepTitle is not null )
69+ return null ;
70+
71+ if ( stepTextTemplate != formattedTitle )
72+ return false ;
73+
74+ if ( stepTitle . Contains ( '<' ) && stepTitle . Contains ( '>' ) )
75+ return methodInfo . GetCustomAttributes < RunStepWithArgsAttribute > ( true ) . Any ( ) ;
76+
77+ return null ;
78+ }
79+
80+ private static string AppendInputSuffix (
81+ string stepTitle ,
82+ MethodInfo methodInfo ,
83+ StepArgument [ ] inputArguments ,
84+ ITestContext testContext )
85+ {
86+ var parameters = methodInfo . GetParameters ( ) ;
87+
88+ var inputRepresentations = inputArguments
89+ . Select ( ( arg , i ) => FormatInput ( arg , parameters [ i ] . Name , methodInfo . Name , testContext ) )
90+ . Where ( x => IsNotAlreadyInTitle ( stepTitle , x ) )
91+ . ToArray ( ) ;
92+
93+ if ( inputRepresentations . Length == 0 )
94+ return stepTitle ;
95+
96+ return stepTitle . Trim ( ) + " " + string . Join ( ", " , inputRepresentations . Select ( o => o . ToTextRepresentation ( ) ) ) ;
97+ }
98+
99+ private static object FormatInput ( StepArgument arg , string ? parameterName , string methodName , ITestContext testContext )
100+ {
101+ if ( testContext . Examples is null )
102+ return arg . Value ? . FlattenArray ( ) ?? Array . Empty < string > ( ) ;
103+
104+ var matchingHeader = FindMatchingExampleHeader ( testContext . Examples , parameterName , arg . Name , methodName ) ;
105+ return matchingHeader is not null
106+ ? $ "<{ matchingHeader } >"
107+ : arg . Value ? . FlattenArray ( ) ?? Array . Empty < string > ( ) ;
108+ }
85109
86- private static string AppendPrefix ( string ? title , string stepPrefix )
110+ private static string ? FindMatchingExampleHeader ( ExampleTable examples , string ? parameterName , string ? argName , string methodName )
111+ {
112+ var matchingHeaders = examples . Headers
113+ . Where ( header => ExampleTable . HeaderMatches ( header , parameterName ) || ExampleTable . HeaderMatches ( header , argName ) )
114+ . ToList ( ) ;
115+
116+ if ( matchingHeaders . Count > 1 )
117+ throw new AmbiguousMatchException (
118+ $ "More than one headers for examples, match the parameter '{ parameterName } ' provided for '{ methodName } '") ;
119+
120+ return matchingHeaders . SingleOrDefault ( ) ;
121+ }
122+
123+ private static bool IsNotAlreadyInTitle ( string stepTitle , object arg )
124+ {
125+ if ( arg is not string token )
126+ return true ;
127+
128+ if ( ! token . StartsWith ( '<' ) || ! token . EndsWith ( '>' ) )
129+ return true ;
130+
131+ return ! stepTitle . Replace ( " " , "" ) . Contains ( token , StringComparison . OrdinalIgnoreCase ) ;
132+ }
133+
134+ private static string PrependPrefix ( string ? title , string stepPrefix )
87135 {
88136 var stepTitle = ( title ?? string . Empty ) . Trim ( ) ;
89137
90- if ( ! stepTitle . StartsWith ( stepPrefix , ignoreCase : true , Configurator . CultureInfo ) )
91- {
92- if ( stepTitle . Length == 0 ) return string . Format ( Configurator . CultureInfo , "{0} " , stepPrefix ) ;
138+ if ( stepTitle . StartsWith ( stepPrefix , ignoreCase : true , Configurator . CultureInfo ) )
139+ return stepTitle ;
93140
94- return string . Format ( Configurator . CultureInfo , "{0} {1}{2}" , stepPrefix , stepTitle [ .. 1 ] . ToLower ( Configurator . CultureInfo ) , stepTitle [ 1 .. ] ) ;
95- }
141+ if ( stepTitle . Length == 0 )
142+ return $ " { stepPrefix } " ;
96143
97- return stepTitle ;
144+ return $ " { stepPrefix } { stepTitle [ .. 1 ] . ToLower ( Configurator . CultureInfo ) } { stepTitle [ 1 .. ] } " ;
98145 }
99146
100- private static string ? NullIfEmpty ( string ? value ) => string . IsNullOrWhiteSpace ( value ) ? null : value ;
147+ private static string ? NullIfEmpty ( string ? value ) =>
148+ string . IsNullOrWhiteSpace ( value ) ? null : value ;
101149
102150 private static bool IsPrimaryPrefix ( string prefix ) =>
103151 prefix is "Given" or "When" or "Then" ;
104- }
152+ }
0 commit comments