Add element completion API for TagHelpers.
- Added a new `TagHelperCompletionService` which can be queried for information on what completion information should be provided. - Added tests to validate the completion service's expectations. - Fixed an issue where `TagHelper`s with output hints that did not exist in a passed in completion would never be highlighted as `TagHelper`'s; even when their primary targeting element was already in the element completion list. #1181
This commit is contained in:
parent
1b8a4e704c
commit
a1cfd22a32
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
[Export(typeof(TagHelperCompletionService))]
|
||||
internal class DefaultTagHelperCompletionService : TagHelperCompletionService
|
||||
{
|
||||
private readonly TagHelperFactsService _tagHelperFactsService;
|
||||
private static readonly HashSet<TagHelperDescriptor> _emptyHashSet = new HashSet<TagHelperDescriptor>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultTagHelperCompletionService(TagHelperFactsService tagHelperFactsService)
|
||||
{
|
||||
_tagHelperFactsService = tagHelperFactsService;
|
||||
}
|
||||
|
||||
public override ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext)
|
||||
{
|
||||
if (completionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(completionContext));
|
||||
}
|
||||
|
||||
var elementCompletions = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
AddAllowedChildrenCompletions(completionContext, elementCompletions);
|
||||
|
||||
if (elementCompletions.Count > 0)
|
||||
{
|
||||
// If the containing element is already a TagHelper and only allows certain children.
|
||||
var emptyResult = ElementCompletionResult.Create(elementCompletions);
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
elementCompletions = completionContext.ExistingCompletions.ToDictionary(
|
||||
completion => completion,
|
||||
_ => new HashSet<TagHelperDescriptor>(),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var possibleChildDescriptors = _tagHelperFactsService.GetTagHelpersGivenParent(completionContext.DocumentContext, completionContext.ContainingTagName);
|
||||
foreach (var possibleDescriptor in possibleChildDescriptors)
|
||||
{
|
||||
var addRuleCompletions = false;
|
||||
var outputHint = possibleDescriptor.TagOutputHint;
|
||||
|
||||
// Filter out catch-all rules because TagHelpers that target attributes only would light up every child tag otherwise. Force those TagHelpers
|
||||
// to have additional requirements before showing them in the element completion list.
|
||||
var nonCatchAllRules = possibleDescriptor.TagMatchingRules.Where(rule => rule.TagName != TagHelperMatchingConventions.ElementCatchAllName);
|
||||
foreach (var rule in nonCatchAllRules)
|
||||
{
|
||||
if (elementCompletions.ContainsKey(rule.TagName))
|
||||
{
|
||||
addRuleCompletions = true;
|
||||
}
|
||||
else if (outputHint != null && elementCompletions.ContainsKey(outputHint))
|
||||
{
|
||||
// If the possible descriptors final output tag already exists in our list of completions, we should add every representation
|
||||
// of that descriptor to the possible element completions.
|
||||
addRuleCompletions = true;
|
||||
}
|
||||
else if (!completionContext.InHTMLSchema(rule.TagName))
|
||||
{
|
||||
// If there is an unknown HTML schema tag that doesn't exist in the current completion we should add it. This happens for
|
||||
// TagHelpers that target non-schema oriented tags.
|
||||
addRuleCompletions = true;
|
||||
}
|
||||
|
||||
if (addRuleCompletions)
|
||||
{
|
||||
if (!elementCompletions.TryGetValue(rule.TagName, out var existingRuleDescriptors))
|
||||
{
|
||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
||||
elementCompletions[rule.TagName] = existingRuleDescriptors;
|
||||
}
|
||||
|
||||
existingRuleDescriptors.Add(possibleDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = ElementCompletionResult.Create(elementCompletions);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void AddAllowedChildrenCompletions(
|
||||
ElementCompletionContext completionContext,
|
||||
Dictionary<string, HashSet<TagHelperDescriptor>> elementCompletions)
|
||||
{
|
||||
if (completionContext.ContainingTagName == null)
|
||||
{
|
||||
// If we're at the root then there's no containing TagHelper to specify allowed children.
|
||||
return;
|
||||
}
|
||||
|
||||
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
|
||||
var binding = _tagHelperFactsService.GetTagHelperBinding(
|
||||
completionContext.DocumentContext,
|
||||
completionContext.ContainingTagName,
|
||||
completionContext.Attributes,
|
||||
completionContext.ContainingParentTagName);
|
||||
|
||||
if (binding == null)
|
||||
{
|
||||
// Containing tag is not a TagHelper; therefore, it allows any children.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var descriptor in binding.Descriptors)
|
||||
{
|
||||
if (descriptor.AllowedChildTags == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var childTag in descriptor.AllowedChildTags)
|
||||
{
|
||||
var prefixedName = string.Concat(prefix, childTag);
|
||||
var descriptors = _tagHelperFactsService.GetTagHelpersGivenTag(
|
||||
completionContext.DocumentContext,
|
||||
prefixedName,
|
||||
completionContext.ContainingTagName);
|
||||
|
||||
if (descriptors.Count == 0)
|
||||
{
|
||||
if (!elementCompletions.ContainsKey(prefixedName))
|
||||
{
|
||||
elementCompletions[prefixedName] = _emptyHashSet;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!elementCompletions.TryGetValue(prefixedName, out var existingRuleDescriptors))
|
||||
{
|
||||
existingRuleDescriptors = new HashSet<TagHelperDescriptor>();
|
||||
elementCompletions[prefixedName] = existingRuleDescriptors;
|
||||
}
|
||||
|
||||
existingRuleDescriptors.UnionWith(descriptors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
public sealed class ElementCompletionContext
|
||||
{
|
||||
public ElementCompletionContext(
|
||||
TagHelperDocumentContext documentContext,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string containingTagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||
string containingParentTagName,
|
||||
Func<string, bool> inHTMLSchema)
|
||||
{
|
||||
if (documentContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentContext));
|
||||
}
|
||||
|
||||
if (existingCompletions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(existingCompletions));
|
||||
}
|
||||
|
||||
if (inHTMLSchema == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inHTMLSchema));
|
||||
}
|
||||
|
||||
DocumentContext = documentContext;
|
||||
ExistingCompletions = existingCompletions;
|
||||
ContainingTagName = containingTagName;
|
||||
Attributes = attributes;
|
||||
ContainingParentTagName = containingParentTagName;
|
||||
InHTMLSchema = inHTMLSchema;
|
||||
}
|
||||
|
||||
public TagHelperDocumentContext DocumentContext { get; }
|
||||
|
||||
public IEnumerable<string> ExistingCompletions { get; }
|
||||
|
||||
public string ContainingTagName { get; }
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> Attributes { get; }
|
||||
|
||||
public string ContainingParentTagName { get; }
|
||||
|
||||
public Func<string, bool> InHTMLSchema { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
public abstract class ElementCompletionResult
|
||||
{
|
||||
private ElementCompletionResult()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions { get; }
|
||||
|
||||
internal static ElementCompletionResult Create(Dictionary<string, HashSet<TagHelperDescriptor>> completions)
|
||||
{
|
||||
var readonlyCompletions = completions.ToDictionary(
|
||||
key => key.Key,
|
||||
value => (IEnumerable<TagHelperDescriptor>)value.Value,
|
||||
completions.Comparer);
|
||||
var result = new DefaultElementCompletionResult(readonlyCompletions);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class DefaultElementCompletionResult : ElementCompletionResult
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> _completions;
|
||||
|
||||
public DefaultElementCompletionResult(IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> completions)
|
||||
{
|
||||
_completions = completions;
|
||||
}
|
||||
|
||||
public override IReadOnlyDictionary<string, IEnumerable<TagHelperDescriptor>> Completions => _completions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
public abstract class TagHelperCompletionService
|
||||
{
|
||||
public abstract ElementCompletionResult GetElementCompletions(ElementCompletionContext completionContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
public class DefaultTagHelperCompletionServiceTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetElementCompletions_AllowsMultiTargetingTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldTagHelper1", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("strong"))
|
||||
.TagMatchingRule(rule => rule.RequireTagName("b"))
|
||||
.TagMatchingRule(rule => rule.RequireTagName("bold"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("BoldTagHelper2", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("strong"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "strong", "b", "bold" };
|
||||
var completionContext = BuildCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CombinesDescriptorsOnExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_NewCompletionsForSchemaTagsNotInExistingCompletionsAreIgnored()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("superli"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li"))
|
||||
.TagOutputHint("strong")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
||||
["superli"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_OutputHintIsCrossReferencedWithExistingCompletions()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.TagOutputHint("li")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li"))
|
||||
.TagOutputHint("strong")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_EnsuresDescriptorsHaveSatisfiedParent()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper1", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li"))
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("LiTagHelper2", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("li").RequireParentTag("ol"))
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["li"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "li" };
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_AllowedChildrenAreIgnoredWhenAtRoot()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("CatchAll", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("*"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["p"] = new HashSet<TagHelperDescriptor>(),
|
||||
["em"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "p", "em" };
|
||||
var completionContext = BuildCompletionContext(
|
||||
documentDescriptors,
|
||||
existingCompletions,
|
||||
containingTagName: null,
|
||||
containingParentTagName: null);
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_DoesNotReturnExistingCompletionsWhenAllowedChildren()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
||||
});
|
||||
|
||||
var existingCompletions = new[] { "p", "em" };
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, existingCompletions, containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_NoneTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
});
|
||||
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_SomeTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.AllowChildTag("div")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["b"] = new HashSet<TagHelperDescriptor>(),
|
||||
["bold"] = new HashSet<TagHelperDescriptor>(),
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] }
|
||||
});
|
||||
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetElementCompletions_CapturesAllAllowedChildTagsFromParentTagHelpers_AllTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var documentDescriptors = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("BoldParentCatchAll", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("*"))
|
||||
.AllowChildTag("strong")
|
||||
.AllowChildTag("div")
|
||||
.AllowChildTag("b")
|
||||
.Build(),
|
||||
TagHelperDescriptorBuilder.Create("BoldParent", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.AllowChildTag("b")
|
||||
.AllowChildTag("bold")
|
||||
.Build(),
|
||||
};
|
||||
var expectedCompletions = ElementCompletionResult.Create(new Dictionary<string, HashSet<TagHelperDescriptor>>()
|
||||
{
|
||||
["strong"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["b"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["bold"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0] },
|
||||
["div"] = new HashSet<TagHelperDescriptor> { documentDescriptors[0], documentDescriptors[1] },
|
||||
});
|
||||
|
||||
var completionContext = BuildCompletionContext(documentDescriptors, Enumerable.Empty<string>(), containingTagName: "div");
|
||||
var service = CreateTagHelperCompletionFactsService();
|
||||
|
||||
// Act
|
||||
var completions = service.GetElementCompletions(completionContext);
|
||||
|
||||
// Assert
|
||||
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||
}
|
||||
|
||||
private static DefaultTagHelperCompletionService CreateTagHelperCompletionFactsService()
|
||||
{
|
||||
var tagHelperFactService = new DefaultTagHelperFactsService();
|
||||
var completionFactService = new DefaultTagHelperCompletionService(tagHelperFactService);
|
||||
|
||||
return completionFactService;
|
||||
}
|
||||
|
||||
private static void AssertCompletionsAreEquivalent(ElementCompletionResult expected, ElementCompletionResult actual)
|
||||
{
|
||||
Assert.Equal(expected.Completions.Count, actual.Completions.Count);
|
||||
|
||||
foreach (var expectedCompletion in expected.Completions)
|
||||
{
|
||||
var actualValue = actual.Completions[expectedCompletion.Key];
|
||||
Assert.NotNull(actualValue);
|
||||
Assert.Equal(expectedCompletion.Value, actualValue, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
private static ElementCompletionContext BuildCompletionContext(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
IEnumerable<string> existingCompletions,
|
||||
string containingTagName,
|
||||
string containingParentTagName = "body")
|
||||
{
|
||||
var documentContext = TagHelperDocumentContext.Create(string.Empty, descriptors);
|
||||
var completionContext = new ElementCompletionContext(
|
||||
documentContext,
|
||||
existingCompletions,
|
||||
containingTagName,
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
containingParentTagName: containingParentTagName,
|
||||
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
|
||||
|
||||
return completionContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue