Add file scoped multiple occurring extensible directives.
- Updated existing singly occurring tests that verified that the directives must occur prior to any content to be theories that tested the joint "file scoped" behavior of the usage. - Added tests that validate `FileScopedMultipleOccurring` can have duplicate directives without erroring. #1377
This commit is contained in:
parent
d73ecf8b36
commit
c2c95ec175
|
|
@ -18,5 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
/// <see cref="DirectiveKind.SingleLine"/>, the last occurrence of the directive is imported.
|
/// <see cref="DirectiveKind.SingleLine"/>, the last occurrence of the directive is imported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
FileScopedSinglyOccurring,
|
FileScopedSinglyOccurring,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Directive must exist prior to any HTML or code.
|
||||||
|
/// </summary>
|
||||||
|
FileScopedMultipleOccurring,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1785,7 +1785,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
UsageError(Resources.FormatDuplicateDirective(descriptor.Directive));
|
UsageError(Resources.FormatDuplicateDirective(descriptor.Directive));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.Usage == DirectiveUsage.FileScopedSinglyOccurring ||
|
||||||
|
descriptor.Usage == DirectiveUsage.FileScopedMultipleOccurring)
|
||||||
|
{
|
||||||
var root = Context.Builder.ActiveBlocks.Last();
|
var root = Context.Builder.ActiveBlocks.Last();
|
||||||
for (var i = 0; i < root.Children.Count; i++)
|
for (var i = 0; i < root.Children.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DirectiveDescriptor_FileScopedSinglyOccurring_ErrorsIfNestedInCode()
|
public void DirectiveDescriptor_FileScopedMultipleOccurring_CanHaveDuplicates()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor = DirectiveDescriptor.CreateDirective(
|
var descriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -20,7 +20,106 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = DirectiveUsage.FileScopedMultipleOccurring;
|
||||||
|
builder.AddTypeToken();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseDocumentTest(
|
||||||
|
@"@custom System.Text.Encoding.ASCIIEncoding
|
||||||
|
@custom System.Text.Encoding.UTF8Encoding",
|
||||||
|
new[] { descriptor },
|
||||||
|
new MarkupBlock(
|
||||||
|
Factory.EmptyHtml(),
|
||||||
|
new DirectiveBlock(new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
|
||||||
|
Factory.Span(SpanKindInternal.Code, "System.Text.Encoding.ASCIIEncoding", markup: false).AsDirectiveToken(descriptor.Tokens[0]),
|
||||||
|
Factory.MetaCode(Environment.NewLine).Accepts(AcceptedCharactersInternal.WhiteSpace)),
|
||||||
|
Factory.EmptyHtml(),
|
||||||
|
new DirectiveBlock(new DirectiveChunkGenerator(descriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
|
||||||
|
Factory.Span(SpanKindInternal.Code, "System.Text.Encoding.UTF8Encoding", markup: false).AsDirectiveToken(descriptor.Tokens[0])),
|
||||||
|
Factory.EmptyHtml()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DirectiveDescriptor_FileScopedMultipleOccurring_MixedContentErrors()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
"custom",
|
||||||
|
DirectiveKind.SingleLine,
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.Usage = DirectiveUsage.FileScopedMultipleOccurring;
|
||||||
|
builder.AddTypeToken();
|
||||||
|
});
|
||||||
|
var chunkGenerator = new DirectiveChunkGenerator(customDescriptor);
|
||||||
|
chunkGenerator.Diagnostics.Add(
|
||||||
|
RazorDiagnostic.Create(
|
||||||
|
new RazorError(
|
||||||
|
Resources.FormatDirectiveMustExistBeforeMarkupOrCode("custom"),
|
||||||
|
151 + Environment.NewLine.Length * 4, 4, 0, 7)));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ParseDocumentTest(
|
||||||
|
@"@custom System.Text.Encoding.ASCIIEncoding
|
||||||
|
@* There is invalid content beneath this *@
|
||||||
|
<p>Should cause error</p>
|
||||||
|
@* There is invalid content above this *@
|
||||||
|
@custom Else",
|
||||||
|
new[] { customDescriptor },
|
||||||
|
new MarkupBlock(
|
||||||
|
Factory.EmptyHtml(),
|
||||||
|
new DirectiveBlock(new DirectiveChunkGenerator(customDescriptor),
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
|
||||||
|
Factory.Span(SpanKindInternal.Code, "System.Text.Encoding.ASCIIEncoding", markup: false).AsDirectiveToken(customDescriptor.Tokens[0]),
|
||||||
|
Factory.MetaCode(Environment.NewLine).Accepts(AcceptedCharactersInternal.WhiteSpace)),
|
||||||
|
Factory.EmptyHtml(),
|
||||||
|
new CommentBlock(
|
||||||
|
Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Comment, new HtmlSymbol(" There is invalid content beneath this ", HtmlSymbolType.RazorComment)).Accepts(AcceptedCharactersInternal.Any),
|
||||||
|
Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None)),
|
||||||
|
Factory.Markup(Environment.NewLine),
|
||||||
|
BlockFactory.MarkupTagBlock("<p>"),
|
||||||
|
Factory.Markup("Should cause error"),
|
||||||
|
BlockFactory.MarkupTagBlock("</p>"),
|
||||||
|
Factory.Markup(Environment.NewLine),
|
||||||
|
new CommentBlock(
|
||||||
|
Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Comment, new HtmlSymbol(" There is invalid content above this ", HtmlSymbolType.RazorComment)).Accepts(AcceptedCharactersInternal.Any),
|
||||||
|
Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None)),
|
||||||
|
Factory.Markup(Environment.NewLine).With(SpanChunkGenerator.Null),
|
||||||
|
new DirectiveBlock(chunkGenerator,
|
||||||
|
Factory.CodeTransition(),
|
||||||
|
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
|
||||||
|
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
|
||||||
|
Factory.Span(SpanKindInternal.Code, "Else", markup: false).AsDirectiveToken(customDescriptor.Tokens[0])),
|
||||||
|
Factory.EmptyHtml()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(DirectiveUsage.FileScopedSinglyOccurring)]
|
||||||
|
[InlineData(DirectiveUsage.FileScopedMultipleOccurring)]
|
||||||
|
public void DirectiveDescriptor_FileScopedSinglyOccurring_ErrorsIfNestedInCode(DirectiveUsage directiveUsage)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
"custom",
|
||||||
|
DirectiveKind.SingleLine,
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.Usage = directiveUsage;
|
||||||
builder.AddTypeToken();
|
builder.AddTypeToken();
|
||||||
});
|
});
|
||||||
var chunkGenerator = new DirectiveChunkGenerator(descriptor);
|
var chunkGenerator = new DirectiveChunkGenerator(descriptor);
|
||||||
|
|
@ -51,8 +150,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)));
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void DirectiveDescriptor_FileScopedSinglyOccurring_ErrorsIfNestedInHtml()
|
[InlineData(DirectiveUsage.FileScopedSinglyOccurring)]
|
||||||
|
[InlineData(DirectiveUsage.FileScopedMultipleOccurring)]
|
||||||
|
public void DirectiveDescriptor_FileScoped_ErrorsIfNestedInHtml(DirectiveUsage directiveUsage)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor = DirectiveDescriptor.CreateDirective(
|
var descriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -60,7 +161,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = directiveUsage;
|
||||||
builder.AddTypeToken();
|
builder.AddTypeToken();
|
||||||
});
|
});
|
||||||
var chunkGenerator = new DirectiveChunkGenerator(descriptor);
|
var chunkGenerator = new DirectiveChunkGenerator(descriptor);
|
||||||
|
|
@ -129,8 +230,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Factory.EmptyHtml()));
|
Factory.EmptyHtml()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void DirectiveDescriptor_FileScopedSinglyOccurring_CanBeBeneathOtherDirectives()
|
[InlineData(DirectiveUsage.FileScopedSinglyOccurring)]
|
||||||
|
[InlineData(DirectiveUsage.FileScopedMultipleOccurring)]
|
||||||
|
public void DirectiveDescriptor_FileScoped_CanBeBeneathOtherDirectives(DirectiveUsage directiveUsage)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var customDescriptor = DirectiveDescriptor.CreateDirective(
|
var customDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -138,7 +241,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = directiveUsage;
|
||||||
builder.AddTypeToken();
|
builder.AddTypeToken();
|
||||||
});
|
});
|
||||||
var somethingDescriptor = DirectiveDescriptor.CreateDirective(
|
var somethingDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -146,7 +249,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = directiveUsage;
|
||||||
builder.AddMemberToken();
|
builder.AddMemberToken();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -172,8 +275,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Factory.EmptyHtml()));
|
Factory.EmptyHtml()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void DirectiveDescriptor_FileScopedSinglyOccurring_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives()
|
[InlineData(DirectiveUsage.FileScopedSinglyOccurring)]
|
||||||
|
[InlineData(DirectiveUsage.FileScopedMultipleOccurring)]
|
||||||
|
public void DirectiveDescriptor_FileScoped_CanBeBeneathOtherWhiteSpaceCommentsAndDirectives(DirectiveUsage directiveUsage)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var customDescriptor = DirectiveDescriptor.CreateDirective(
|
var customDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -181,7 +286,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = directiveUsage;
|
||||||
builder.AddTypeToken();
|
builder.AddTypeToken();
|
||||||
});
|
});
|
||||||
var somethingDescriptor = DirectiveDescriptor.CreateDirective(
|
var somethingDescriptor = DirectiveDescriptor.CreateDirective(
|
||||||
|
|
@ -189,7 +294,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
DirectiveKind.SingleLine,
|
DirectiveKind.SingleLine,
|
||||||
builder =>
|
builder =>
|
||||||
{
|
{
|
||||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
builder.Usage = directiveUsage;
|
||||||
builder.AddMemberToken();
|
builder.AddMemberToken();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue