11// Copyright (c) .NET Foundation. All rights reserved.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . Linq . Expressions ;
47using Microsoft . AspNet . Mvc . ModelBinding ;
58using Microsoft . AspNet . Mvc . ModelBinding . Metadata ;
69using Microsoft . AspNet . Mvc . Rendering ;
10+ using Microsoft . AspNet . Testing ;
711using Moq ;
812using Xunit ;
913
@@ -14,6 +18,26 @@ namespace Microsoft.AspNet.Mvc.Core
1418 /// </summary>
1519 public class HtmlHelperNameExtensionsTest
1620 {
21+ private static List < string > _staticCollection = new List < string > ( ) ;
22+ private static int _staticIndex = 6 ;
23+
24+ private List < string > _collection = new List < string > ( ) ;
25+ private int _index = 7 ;
26+ private List < string [ ] > _nestedCollection = new List < string [ ] > ( ) ;
27+ private string _string = string . Empty ;
28+
29+ private static List < string > StaticCollection { get ; }
30+
31+ private static int StaticIndex { get ; } = 8 ;
32+
33+ private List < string > Collection { get ; }
34+
35+ private int Index { get ; } = 9 ;
36+
37+ private List < string [ ] > NestedCollection { get ; }
38+
39+ private string StringProperty { get ; }
40+
1741 [ Fact ]
1842 public void IdAndNameHelpers_ReturnEmptyForModel ( )
1943 {
@@ -140,7 +164,7 @@ public void IdAndNameHelpers_DoNotConsultMetadataOrMetadataProvider()
140164 // Arrange
141165 var provider = new Mock < IModelMetadataProvider > ( MockBehavior . Strict ) ;
142166 var metadata = new Mock < ModelMetadata > (
143- MockBehavior . Loose ,
167+ MockBehavior . Loose ,
144168 ModelMetadataIdentity . ForType ( typeof ( DefaultTemplatesUtilities . ObjectTemplateModel ) ) ) ;
145169 provider
146170 . Setup ( m => m . GetMetadataForType ( typeof ( DefaultTemplatesUtilities . ObjectTemplateModel ) ) )
@@ -223,28 +247,106 @@ public void IdForAndNameFor_ReturnEmpty_IfExpressionUnsupported()
223247 Assert . Empty ( nameResult ) ;
224248 }
225249
226- [ Fact ]
227- public void IdForAndNameFor_ReturnVariableName ( )
250+ // expression, expected name, expected id
251+ public static TheoryData StaticExpressionNamesData
252+ {
253+ get
254+ {
255+ // Give expressions access to non-static fields and properties.
256+ var test = new HtmlHelperNameExtensionsTest ( ) ;
257+ return test . ExpressionNamesData ;
258+ }
259+ }
260+
261+ // expression, expected name, expected id
262+ private TheoryData ExpressionNamesData
263+ {
264+ get
265+ {
266+ var collection = new List < string > ( ) ;
267+ var nestedCollection = new List < string [ ] > ( ) ;
268+ var index1 = 5 ;
269+ var index2 = 23 ;
270+ var unknownKey = "this is a dummy parameter value" ;
271+
272+ return new TheoryData < Expression < Func < List < OuterClass > , string > > , string , string >
273+ {
274+ { m => unknownKey , "unknownKey" , "unknownKey" } ,
275+ { m => collection [ index1 ] , "collection[5]" , "collection_5_" } ,
276+ { m => nestedCollection [ index1 ] [ 23 ] , "nestedCollection[5][23]" , "nestedCollection_5__23_" } ,
277+ { m => nestedCollection [ index1 ] [ index2 ] , "nestedCollection[5][23]" , "nestedCollection_5__23_" } ,
278+ { m => nestedCollection [ _index ] [ Index ] , "nestedCollection[7][9]" , "nestedCollection_7__9_" } ,
279+ { m => nestedCollection [ Index ] [ StaticIndex ] , "nestedCollection[9][8]" , "nestedCollection_9__8_" } ,
280+ { m => _string , "_string" , "zstring" } ,
281+ { m => _collection [ _index ] , "_collection[7]" , "zcollection_7_" } ,
282+ { m => _nestedCollection [ _index ] [ 23 ] , "_nestedCollection[7][23]" , "znestedCollection_7__23_" } ,
283+ { m => _nestedCollection [ _index ] [ index2 ] , "_nestedCollection[7][23]" , "znestedCollection_7__23_" } ,
284+ { m => _nestedCollection [ Index ] [ _staticIndex ] , "_nestedCollection[9][6]" , "znestedCollection_9__6_" } ,
285+ { m => _nestedCollection [ StaticIndex ] [ _index ] , "_nestedCollection[8][7]" , "znestedCollection_8__7_" } ,
286+ { m => StringProperty , "StringProperty" , "StringProperty" } ,
287+ { m => Collection [ Index ] , "Collection[9]" , "Collection_9_" } ,
288+ { m => NestedCollection [ Index ] [ 23 ] , "NestedCollection[9][23]" , "NestedCollection_9__23_" } ,
289+ { m => NestedCollection [ Index ] [ index2 ] , "NestedCollection[9][23]" , "NestedCollection_9__23_" } ,
290+ { m => NestedCollection [ _index ] [ Index ] , "NestedCollection[7][9]" , "NestedCollection_7__9_" } ,
291+ { m => NestedCollection [ Index ] [ StaticIndex ] , "NestedCollection[9][8]" , "NestedCollection_9__8_" } ,
292+ { m => _staticCollection [ _staticIndex ] , "_staticCollection[6]" , "zstaticCollection_6_" } ,
293+ { m => _staticCollection [ Index ] , "_staticCollection[9]" , "zstaticCollection_9_" } ,
294+ { m => _staticCollection [ _index ] , "_staticCollection[7]" , "zstaticCollection_7_" } ,
295+ { m => StaticCollection [ StaticIndex ] , "StaticCollection[8]" , "StaticCollection_8_" } ,
296+ { m => StaticCollection [ _staticIndex ] , "StaticCollection[6]" , "StaticCollection_6_" } ,
297+ { m => StaticCollection [ index1 ] , "StaticCollection[5]" , "StaticCollection_5_" } ,
298+ { m => m [ index1 ] . Inner . Name , "[5].Inner.Name" , "z5__Inner_Name" } ,
299+ { m => m [ _staticIndex ] . Inner . Name , "[6].Inner.Name" , "z6__Inner_Name" } ,
300+ { m => m [ _index ] . Inner . Name , "[7].Inner.Name" , "z7__Inner_Name" } ,
301+ { m => m [ StaticIndex ] . Inner . Name , "[8].Inner.Name" , "z8__Inner_Name" } ,
302+ { m => m [ Index ] . Inner . Name , "[9].Inner.Name" , "z9__Inner_Name" } ,
303+ } ;
304+ }
305+ }
306+
307+ [ Theory ]
308+ [ MemberData ( nameof ( StaticExpressionNamesData ) ) ]
309+ public void IdForAndNameFor_ReturnExpectedValues_WithVariablesInExpression (
310+ Expression < Func < List < OuterClass > , string > > expression ,
311+ string expectedName ,
312+ string expectedId )
228313 {
229314 // Arrange
230- var unknownKey = "this is a dummy parameter value" ;
231- var helper = DefaultTemplatesUtilities . GetHtmlHelper ( ) ;
315+ var model = new List < OuterClass > ( ) ;
316+ var helper = DefaultTemplatesUtilities . GetHtmlHelper ( model ) ;
232317
233318 // Act
234- var idResult = helper . IdFor ( model => unknownKey ) ;
235- var nameResult = helper . NameFor ( model => unknownKey ) ;
319+ var idResult = helper . IdFor ( expression ) ;
320+ var nameResult = helper . NameFor ( expression ) ;
236321
237322 // Assert
238- Assert . Equal ( "unknownKey" , idResult ) ;
239- Assert . Equal ( "unknownKey" , nameResult ) ;
323+ Assert . Equal ( expectedId , idResult ) ;
324+ Assert . Equal ( expectedName , nameResult ) ;
240325 }
241326
242- private sealed class InnerClass
327+ [ Fact ]
328+ public void IdForAndNameFor_Throws_WhenParameterUsedAsIndexer ( )
329+ {
330+ // Arrange
331+ var collection = new List < string > ( ) ;
332+ var index = 24 ;
333+ var helper = DefaultTemplatesUtilities . GetHtmlHelper ( index ) ;
334+ var message = "The expression compiler was unable to evaluate the indexer expression 'm' because it " +
335+ "references the model parameter 'm' which is unavailable." ;
336+
337+ // Act & Assert
338+ ExceptionAssert . Throws < InvalidOperationException > ( ( ) => helper . IdFor ( m => collection [ m ] ) , message ) ;
339+ ExceptionAssert . Throws < InvalidOperationException > ( ( ) => helper . NameFor ( m => collection [ m ] ) , message ) ;
340+ }
341+
342+ public sealed class InnerClass
243343 {
244344 public int Id { get ; set ; }
345+
346+ public string Name { get ; set ; }
245347 }
246348
247- private sealed class OuterClass
349+ public sealed class OuterClass
248350 {
249351 public InnerClass Inner { get ; set ; }
250352 }
0 commit comments