Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Engine/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>8.2.0</VersionPrefix>
<VersionPrefix>8.3.0</VersionPrefix>
</PropertyGroup>
</Project>
10 changes: 9 additions & 1 deletion Engine/Mindbox.Quokka.Abstractions/Html/Reference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// // limitations under the License.

using System;
using System.Collections.Generic;

namespace Mindbox.Quokka.Html
{
Expand All @@ -22,13 +23,20 @@ public class Reference
public string RedirectUrl { get; }
public string Name { get; }
public bool IsConstant { get; }
public IReadOnlyDictionary<string, string> Attributes { get; }

public Reference(string redirectUrl, string name, Guid uniqueKey, bool isConstant)
public Reference(
string redirectUrl,
string name,
Guid uniqueKey,
bool isConstant,
IReadOnlyDictionary<string, string> attributes = null)
{
RedirectUrl = redirectUrl;
Name = name;
UniqueKey = uniqueKey;
IsConstant = isConstant;
Attributes = attributes ?? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
}
}
}
7 changes: 5 additions & 2 deletions Engine/Quokka.Core/Html/Nodes/LinkBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ internal class LinkBlock : TemplateNodeBase, IStaticBlockPart

private readonly AttributeValue hrefValue;
private readonly AttributeValue nameValue;
private readonly IReadOnlyDictionary<string, string> attributes;

private readonly Guid uniqueKey;

public LinkBlock(AttributeValue hrefValue, AttributeValue nameValue)
public LinkBlock(AttributeValue hrefValue, AttributeValue nameValue, IReadOnlyDictionary<string, string> attributes)
{
this.hrefValue = hrefValue;
this.nameValue = nameValue;
this.attributes = attributes;
uniqueKey = Guid.NewGuid();
}

Expand Down Expand Up @@ -85,7 +87,8 @@ public override void CompileGrammarSpecificData(GrammarSpecificDataAnalysisConte
hrefValue.Text,
nameValue?.Text,
uniqueKey,
isConstant: !hrefValue.TextComponents.OfType<OutputInstructionBlock>().Any()));
isConstant: !hrefValue.TextComponents.OfType<OutputInstructionBlock>().Any(),
attributes: attributes));
}

public override void Accept(ITemplateVisitor treeVisitor)
Expand Down
13 changes: 9 additions & 4 deletions Engine/Quokka.Core/Html/Visitors/Html/HtmlBlockVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,27 @@ public override IStaticBlockPart VisitClosingTag(QuokkaHtml.ClosingTagContext co

private IStaticBlockPart TryGetLinkNodeFromTagAttributes(IEnumerable<QuokkaHtml.AttributeContext> attributes)
{
var hrefAttributeValueVisitor = new AttributeValueVisitor(ParsingContext);
var attributeValueVisitor = new AttributeValueVisitor(ParsingContext);

AttributeValue hrefValue = null;
AttributeValue nameValue = null;
var allAttributes = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

foreach (var attribute in attributes)
{
var attributeName = attribute.TAG_NAME().GetText();
var attributeValue = attribute.attributeValue()?.Accept(attributeValueVisitor);

if (attributeName.Equals("href", StringComparison.InvariantCultureIgnoreCase))
hrefValue = attribute.attributeValue()?.Accept(hrefAttributeValueVisitor);
hrefValue = attributeValue;
if (attributeName.Equals("data-name", StringComparison.InvariantCultureIgnoreCase))
nameValue = attribute.attributeValue()?.Accept(hrefAttributeValueVisitor);
nameValue = attributeValue;

allAttributes[attributeName] = attributeValue?.Text ?? string.Empty;
}

return hrefValue != null
? new LinkBlock(hrefValue, nameValue)
? new LinkBlock(hrefValue, nameValue, allAttributes)
: null;
}
}
Expand Down
18 changes: 18 additions & 0 deletions Engine/Quokka.Tests/Helpers/ReferencesAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,23 @@ public static void AreCollectionsEquivalent(IEnumerable<Reference> expected, IRe
Assert.AreEqual(expectedElement.IsConstant, actualElement.IsConstant);
}
}

public static void ContainsAttribute(Reference reference, string attributeName, string expectedValue)
{
Assert.IsTrue(
reference.Attributes.ContainsKey(attributeName),
$"Reference does not contain attribute '{attributeName}'");
Assert.AreEqual(
expectedValue,
reference.Attributes[attributeName],
$"Attribute '{attributeName}' has unexpected value");
}

public static void DoesNotContainAttribute(Reference reference, string attributeName)
{
Assert.IsFalse(
reference.Attributes.ContainsKey(attributeName),
$"Reference unexpectedly contains attribute '{attributeName}'");
}
}
}
54 changes: 54 additions & 0 deletions Engine/Quokka.Tests/Html/HtmlReferenceDiscoveryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,5 +383,59 @@ public void Html_ReferenceDiscovery_AHref_HtmlEncoded()
},
references);
}

[TestMethod]
public void Html_ReferenceDiscovery_AHref_SingleDataAttribute_PopulatesAttributes()
{
var template = new HtmlTemplate(
"<a href=\"http://example.com\" data-custom=\"value-42\">Test</a>");
var references = template.GetReferences();

Assert.AreEqual(1, references.Count);
ReferencesAssert.ContainsAttribute(references[0], "data-custom", "value-42");
}

[TestMethod]
public void Html_ReferenceDiscovery_AHref_MultipleDataAttributes_AllPopulated()
{
var template = new HtmlTemplate(
"<a href=\"http://example.com\" data-foo=\"bar\" data-name=\"Link name\">Test</a>");
var references = template.GetReferences();

Assert.AreEqual(1, references.Count);
ReferencesAssert.ContainsAttribute(references[0], "data-foo", "bar");
ReferencesAssert.ContainsAttribute(references[0], "data-name", "Link name");
}

[TestMethod]
public void Html_ReferenceDiscovery_AHref_AttributeWithoutValue_EmptyString()
{
var template = new HtmlTemplate(
"<a href=\"http://example.com\" data-custom>Test</a>");
var references = template.GetReferences();

Assert.AreEqual(1, references.Count);
ReferencesAssert.ContainsAttribute(references[0], "data-custom", string.Empty);
}

[TestMethod]
public void Html_ReferenceDiscovery_AHref_NoExtraAttributes_AttributesEmpty()
{
var template = new HtmlTemplate("<a href=\"http://example.com\">Test</a>");
var references = template.GetReferences();

Assert.AreEqual(1, references.Count);
ReferencesAssert.DoesNotContainAttribute(references[0], "data-custom");
}

[TestMethod]
public void Html_ReferenceDiscovery_AHref_HrefIsIncludedInAttributes()
{
var template = new HtmlTemplate("<a href=\"http://example.com\">Test</a>");
var references = template.GetReferences();

Assert.AreEqual(1, references.Count);
ReferencesAssert.ContainsAttribute(references[0], "href", "http://example.com");
}
}
}
Loading