Change duplicate TagHelper bound attributes to flow to output.

- Duplicate TagHelper bound attributes used to be ignored entirely; they now flow to the output as if they were unbound.
- Added code generation test to verify duplicate attributes. Added runtime and design time versions.
- Updated existing tests that happened to have duplicate bound attributes.

#418
This commit is contained in:
N. Taylor Mullen 2015-06-23 16:11:31 -07:00
parent 039062c5eb
commit 0bb9457fd9
7 changed files with 200 additions and 8 deletions

View File

@ -229,15 +229,10 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attributeName)));
// Bound attributes have associated descriptors.
if (associatedDescriptors.Any())
// Bound attributes have associated descriptors. First attribute value wins if there are duplicates;
// later values of duplicate bound attributes are treated as if they were unbound.
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attributeName))
{
if (!renderedBoundAttributeNames.Add(attributeName))
{
// First attribute value wins if there are duplicates.
continue;
}
if (attributeValueChunk == null)
{
// Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already

View File

@ -879,6 +879,36 @@ namespace Microsoft.AspNet.Razor.Test.Generator
contentLength: 2),
}
},
{
"DuplicateAttributeTagHelpers",
"DuplicateAttributeTagHelpers.DesignTime",
DefaultPAndInputTagHelperDescriptors,
new List<LineMapping>
{
BuildLineMapping(
documentAbsoluteIndex: 14,
documentLineIndex: 0,
generatedAbsoluteIndex: 501,
generatedLineIndex: 15,
characterOffsetIndex: 14,
contentLength: 17),
BuildLineMapping(
documentAbsoluteIndex: 146,
documentLineIndex: 4,
generatedAbsoluteIndex: 1567,
generatedLineIndex: 43,
characterOffsetIndex: 34,
contentLength: 4),
BuildLineMapping(
documentAbsoluteIndex: 43,
documentLineIndex: 2,
documentCharacterOffsetIndex: 8,
generatedAbsoluteIndex: 1730,
generatedLineIndex: 49,
generatedCharacterOffsetIndex: 19,
contentLength: 1),
}
},
};
}
}
@ -921,6 +951,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
"PrefixedAttributeTagHelpers.Reversed",
PrefixedAttributeTagHelperDescriptors.Reverse()
},
{ "DuplicateAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
};
}
}

View File

@ -0,0 +1,57 @@
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Threading.Tasks;
public class DuplicateAttributeTagHelpers
{
private static object @__o;
private void @__RazorDesignTimeHelpers__()
{
#pragma warning disable 219
string __tagHelperDirectiveSyntaxHelper = null;
__tagHelperDirectiveSyntaxHelper =
#line 1 "DuplicateAttributeTagHelpers.cshtml"
"something, nice"
#line default
#line hidden
;
#pragma warning restore 219
}
#line hidden
private PTagHelper __PTagHelper = null;
private InputTagHelper __InputTagHelper = null;
private InputTagHelper2 __InputTagHelper2 = null;
#line hidden
public DuplicateAttributeTagHelpers()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper.Type = "button";
__InputTagHelper2.Type = __InputTagHelper.Type;
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper.Type = "button";
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 5 "DuplicateAttributeTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__PTagHelper = CreateTagHelper<PTagHelper>();
#line 3 "DuplicateAttributeTagHelpers.cshtml"
__PTagHelper.Age = 3;
#line default
#line hidden
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,101 @@
#pragma checksum "DuplicateAttributeTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "af64a6adaf73e4143024a1145e70cbd3a24d2c0e"
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Threading.Tasks;
public class DuplicateAttributeTagHelpers
{
#line hidden
#pragma warning disable 0414
private TagHelperContent __tagHelperStringValueBuffer = null;
#pragma warning restore 0414
private TagHelperExecutionContext __tagHelperExecutionContext = null;
private TagHelperRunner __tagHelperRunner = null;
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
private PTagHelper __PTagHelper = null;
private InputTagHelper __InputTagHelper = null;
private InputTagHelper2 __InputTagHelper2 = null;
#line hidden
public DuplicateAttributeTagHelpers()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
Instrumentation.BeginContext(33, 2, true);
WriteLiteral("\r\n");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => {
Instrumentation.BeginContext(65, 6, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper.Type = "button";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2.Type = __InputTagHelper.Type;
__tagHelperExecutionContext.AddHtmlAttribute("TYPE", Html.Raw("checkbox"));
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
Instrumentation.BeginContext(71, 39, false);
await WriteTagHelperAsync(__tagHelperExecutionContext);
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(110, 6, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper.Type = "button";
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 5 "DuplicateAttributeTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("checkbox"));
__tagHelperExecutionContext.AddHtmlAttribute("checked", Html.Raw("false"));
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
Instrumentation.BeginContext(116, 70, false);
await WriteTagHelperAsync(__tagHelperExecutionContext);
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(186, 2, true);
WriteLiteral("\r\n");
Instrumentation.EndContext();
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
#line 3 "DuplicateAttributeTagHelpers.cshtml"
__PTagHelper.Age = 3;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.AddHtmlAttribute("AGE", Html.Raw("40"));
__tagHelperExecutionContext.AddHtmlAttribute("Age", Html.Raw("500"));
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
Instrumentation.BeginContext(35, 157, false);
await WriteTagHelperAsync(__tagHelperExecutionContext);
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.End();
}
#pragma warning restore 1998
}
}

View File

@ -166,6 +166,7 @@ __InputTagHelper2.IntDictionaryProperty["grabber"] = 42;
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper2.IntDictionaryProperty["pepper"]);
__InputTagHelper1.IntDictionaryProperty["pepper"] = __InputTagHelper2.IntDictionaryProperty["pepper"];
__tagHelperExecutionContext.AddHtmlAttribute("int-prefix-salt", Html.Raw("8"));
__InputTagHelper2.StringDictionaryProperty["grabber"] = "string";
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper2.StringDictionaryProperty["grabber"]);
__InputTagHelper1.StringProperty = __InputTagHelper2.StringDictionaryProperty["grabber"];

View File

@ -166,6 +166,7 @@ __InputTagHelper1.IntProperty = 42;
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper1.IntDictionaryProperty["pepper"]);
__InputTagHelper2.IntDictionaryProperty["pepper"] = __InputTagHelper1.IntDictionaryProperty["pepper"];
__tagHelperExecutionContext.AddHtmlAttribute("int-prefix-salt", Html.Raw("8"));
__InputTagHelper1.StringProperty = "string";
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper1.StringProperty);
__InputTagHelper2.StringDictionaryProperty["grabber"] = __InputTagHelper1.StringProperty;

View File

@ -0,0 +1,6 @@
@addTagHelper "something, nice"
<p age="3" AGE="40" Age="500">
<input type="button" TYPE="checkbox" />
<input type="button" checked="true" type="checkbox" checked="false" />
</p>