public void Execute_AutomaticallyOverridesImportedSingleLineSinglyOccurringDirective_MainDocument() { // Arrange var directive = DirectiveDescriptor.CreateSingleLineDirective( "custom", builder => { builder.AddStringToken(); builder.Usage = DirectiveUsage.FileScopedSinglyOccurring; }); var phase = new DefaultRazorIntermediateNodeLoweringPhase(); var engine = RazorProjectEngine.CreateEmpty(b => { b.Phases.Add(phase); b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false)); b.AddDirective(directive); }); var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive)); var importSource = TestRazorSourceDocument.Create("@custom \"hello\"", filePath: "import.cshtml"); var codeDocument = TestRazorCodeDocument.Create("@custom \"world\""); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options)); codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) }); // Act phase.Execute(codeDocument); // Assert var documentNode = codeDocument.GetDocumentIntermediateNode(); var customDirectives = documentNode.FindDirectiveReferences(directive); var customDirective = (DirectiveIntermediateNode)Assert.Single(customDirectives).Node; var stringToken = Assert.Single(customDirective.Tokens); Assert.Equal("\"world\"", stringToken.Content); }
public void Lower_HtmlWithConditionalAttributes() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@" <html> <body> <span val=""@Hello World"" /> </body> </html>"); // Act var documentNode = Lower(codeDocument); // Assert Children(documentNode, n => Html( @" <html> <body> <span", n), n => ConditionalAttribute( prefix: " val=\"", name: "val", suffix: "\"", node: n, valueValidators: new Action <IntermediateNode>[] { value => CSharpExpressionAttributeValue(string.Empty, "Hello", value), value => LiteralAttributeValue(" ", "World", value) }), n => Html(@" /> </body> </html>", n)); }
public void Execute_ErrorsForRazorBlockFileScopedSinglyOccurringDirectives() { // Arrange var directive = DirectiveDescriptor.CreateRazorBlockDirective("custom", b => b.Usage = DirectiveUsage.FileScopedSinglyOccurring); var phase = new DefaultRazorIntermediateNodeLoweringPhase(); var engine = RazorProjectEngine.CreateEmpty(b => { b.Phases.Add(phase); b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false)); b.AddDirective(directive); }); var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive)); var importSource = TestRazorSourceDocument.Create("@custom { }", filePath: "import.cshtml"); var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>"); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options)); codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) }); var expectedDiagnostic = RazorDiagnosticFactory.CreateDirective_BlockDirectiveCannotBeImported("custom"); // Act phase.Execute(codeDocument); // Assert var documentNode = codeDocument.GetDocumentIntermediateNode(); var directives = documentNode.Children.OfType <DirectiveIntermediateNode>(); Assert.Empty(directives); var diagnostic = Assert.Single(documentNode.GetAllDiagnostics()); Assert.Equal(expectedDiagnostic, diagnostic); }
public void Lower_TagHelpers() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly <span val=""@Hello World""></span>"); var tagHelpers = new[] { CreateTagHelperDescriptor( tagName: "span", typeName: "SpanTagHelper", assemblyName: "TestAssembly") }; // Act var documentNode = Lower(codeDocument, tagHelpers: tagHelpers); // Assert Children(documentNode, n => Directive( SyntaxConstants.CSharp.AddTagHelperKeyword, n, v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)), n => TagHelper( "span", TagMode.StartTagAndEndTag, tagHelpers, n, c => Assert.IsType <TagHelperBodyIntermediateNode>(c), c => TagHelperHtmlAttribute( "val", AttributeStructure.DoubleQuotes, c, v => CSharpExpressionAttributeValue(string.Empty, "Hello", v), v => LiteralAttributeValue(" ", "World", v)))); }
public void TryComputeNamespace_ForNonRelatedFiles_UsesNamespaceVerbatim() { // Arrange var sourceDocument = TestRazorSourceDocument.Create( filePath: "c:\\foo\\bar\\bleh.cshtml", relativePath: "bar\\bleh.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(sourceDocument, RazorParserOptions.Create(options => { options.Directives.Add(NamespaceDirective.Directive); }))); var importSourceDocument = TestRazorSourceDocument.Create( content: "@namespace Base", filePath: "c:\\foo\\baz\\bleh.cshtml", relativePath: "baz\\bleh.cshtml"); codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSourceDocument, RazorParserOptions.Create(options => { options.Directives.Add(NamespaceDirective.Directive); })) }); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Equal("Base", @namespace); }
public void Lower_WithMultipleImports_SingleLineFileScopedSinglyOccurring() { // Arrange var source = TestRazorSourceDocument.Create("<p>Hi!</p>"); var imports = new[] { TestRazorSourceDocument.Create("@test value1"), TestRazorSourceDocument.Create("@test value2"), }; var codeDocument = TestRazorCodeDocument.Create(source, imports); // Act var documentNode = Lower(codeDocument, b => { b.AddDirective(DirectiveDescriptor.CreateDirective( "test", DirectiveKind.SingleLine, builder => { builder.AddMemberToken(); builder.Usage = DirectiveUsage.FileScopedSinglyOccurring; })); }); // Assert Children( documentNode, n => Directive("test", n, c => DirectiveToken(DirectiveTokenKind.Member, "value2", c)), n => Html("<p>Hi!</p>", n)); }
public void TryComputeNamespace_RespectsNamespaceDirective() { // Arrange var sourceDocument = TestRazorSourceDocument.Create( content: "@namespace My.Custom.NS", filePath: "C:\\Hello\\Components\\Test.cshtml", relativePath: "\\Components\\Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); codeDocument.SetFileKind(FileKinds.Component); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(sourceDocument, RazorParserOptions.Create(options => { options.Directives.Add(NamespaceDirective.Directive); }))); var documentNode = new DocumentIntermediateNode() { Options = RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "Hello.World"; }) }; codeDocument.SetDocumentIntermediateNode(documentNode); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Equal("My.Custom.NS", @namespace); }
public void TryComputeNamespace_ComputesNamespaceWithSuffix(string basePath, string relativePath, string expectedNamespace) { // Arrange var sourceDocument = TestRazorSourceDocument.Create( filePath: Path.Combine(basePath, relativePath), relativePath: relativePath); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(sourceDocument, RazorParserOptions.Create(options => { options.Directives.Add(NamespaceDirective.Directive); }))); var importRelativePath = "_ViewImports.cshtml"; var importSourceDocument = TestRazorSourceDocument.Create( content: "@namespace Base", filePath: Path.Combine(basePath, importRelativePath), relativePath: importRelativePath); codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSourceDocument, RazorParserOptions.Create(options => { options.Directives.Add(NamespaceDirective.Directive); })) }); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Equal(expectedNamespace, @namespace); }
public void Lower_HtmlWithDataDashAttributes() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@" <html> <body> <span data-val=""@Hello"" /> </body> </html>"); // Act var documentNode = Lower(codeDocument); // Assert Children(documentNode, n => Html( @" <html> <body> <span data-val=""", n), n => CSharpExpression("Hello", n), n => Html(@""" /> </body> </html>", n)); }
public void TryComputeNamespaceAndClass_PrefersOptionsFromCodeDocument_ComputesNamespaceAndClass() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Components\\Test.cshtml", relativePath: "\\Components\\Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); codeDocument.SetCodeGenerationOptions(RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "World"; })); var documentNode = new DocumentIntermediateNode() { Options = RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "Hello"; }) }; codeDocument.SetDocumentIntermediateNode(documentNode); // Act codeDocument.TryComputeNamespaceAndClass(out var @namespace, out var @class); // Assert Assert.Equal("World.Components", @namespace); Assert.Equal("Test", @class); }
public void Execute_CollatesIRDocumentDiagnosticsFromSourceDocument() { // Arrange var phase = new DefaultRazorCSharpLoweringPhase(); var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase)); var codeDocument = TestRazorCodeDocument.Create("<p class=@("); var options = RazorCodeGenerationOptions.CreateDefault(); var irDocument = new DocumentIntermediateNode() { DocumentKind = "test", Target = CodeTarget.CreateDefault(codeDocument, options), Options = options, }; var expectedDiagnostic = RazorDiagnostic.Create( new RazorDiagnosticDescriptor("1234", () => "I am an error.", RazorDiagnosticSeverity.Error), new SourceSpan("SomeFile.cshtml", 11, 0, 11, 1)); irDocument.Diagnostics.Add(expectedDiagnostic); codeDocument.SetDocumentIntermediateNode(irDocument); // Act phase.Execute(codeDocument); // Assert var csharpDocument = codeDocument.GetCSharpDocument(); var diagnostic = Assert.Single(csharpDocument.Diagnostics); Assert.Same(expectedDiagnostic, diagnostic); }
public void Execute_ParsesImports() { // Arrange var phase = new DefaultRazorParsingPhase(); var engine = RazorEngine.CreateEmpty((builder) => { builder.Phases.Add(phase); builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest)); builder.Features.Add(new MyParserOptionsFeature()); }); var imports = new[] { TestRazorSourceDocument.Create(), TestRazorSourceDocument.Create(), }; var codeDocument = TestRazorCodeDocument.Create(TestRazorSourceDocument.Create(), imports); // Act phase.Execute(codeDocument); // Assert Assert.Collection( codeDocument.GetImportSyntaxTrees(), t => { Assert.Same(t.Source, imports[0]); Assert.Equal("test", Assert.Single(t.Options.Directives).Directive); }, t => { Assert.Same(t.Source, imports[1]); Assert.Equal("test", Assert.Single(t.Options.Directives).Directive); }); }
public void TryComputeNamespace_RelativePathLongerThanFilePath_ReturnsNull() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Test.cshtml", relativePath: "Some\\invalid\\relative\\path\\Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Null(@namespace); }
public void TryComputeNamespace_FilePathNull_ReturnsNull() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: null, relativePath: "Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Null(@namespace); }
public void Lower_HelloWorld() { // Arrange var codeDocument = TestRazorCodeDocument.Create("Hello, World!"); // Act var documentNode = Lower(codeDocument); // Assert Children(documentNode, n => Html("Hello, World!", n)); }
public void TryComputeNamespaceAndClass_RelativePathNull_ReturnsNull() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Test.cshtml", relativePath: null); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); // Act codeDocument.TryComputeNamespaceAndClass(out var @namespace, out var @class); // Assert Assert.Null(@namespace); Assert.Null(@class); }
public void Lower_WithFunctions() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@"@functions { public int Foo { get; set; }}"); // Act var documentNode = Lower(codeDocument); // Assert Children(documentNode, n => Directive( "functions", n, c => Assert.IsType <CSharpCodeIntermediateNode>(c))); }
public void TryComputeNamespace_ComputesNamespace() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Components\\Test.cshtml", relativePath: "\\Components\\Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); codeDocument.SetCodeGenerationOptions(RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "Hello"; })); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Equal("Hello.Components", @namespace); }
public void Lower_WithUsing() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@"@using System"); var expectedSourceLocation = new SourceSpan(codeDocument.Source.FilePath, 1, 0, 1, 12); // Act var documentNode = Lower(codeDocument); // Assert Children(documentNode, n => { Using("System", n); Assert.Equal(expectedSourceLocation, n.Source); }); }
public void Lower_TagHelpersWithBoundAttribute() { // Arrange var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly <input bound='foo' />"); var tagHelpers = new[] { CreateTagHelperDescriptor( tagName: "input", typeName: "InputTagHelper", assemblyName: "TestAssembly", attributes: new Action <BoundAttributeDescriptorBuilder>[] { builder => builder .Name("bound") .PropertyName("FooProp") .TypeName("System.String"), }) }; // Act var documentNode = Lower(codeDocument, tagHelpers: tagHelpers); // Assert Children( documentNode, n => Directive( SyntaxConstants.CSharp.AddTagHelperKeyword, n, v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)), n => TagHelper( "input", TagMode.SelfClosing, tagHelpers, n, c => Assert.IsType <TagHelperBodyIntermediateNode>(c), c => SetTagHelperProperty( "bound", "FooProp", AttributeStructure.SingleQuotes, c, v => Html("foo", v)))); }
public void TryComputeNamespace_NoRootNamespaceFallback_ReturnsNull() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Components\\Test.cshtml", relativePath: "\\Components\\Test.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); var documentNode = new DocumentIntermediateNode() { Options = RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "Hello"; }) }; codeDocument.SetDocumentIntermediateNode(documentNode); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: false, out var @namespace); // Assert Assert.Null(@namespace); }
public void TryComputeNamespace_SanitizesNamespaceName() { // Arrange var sourceDocument = TestRazorSourceDocument.Create(filePath: "C:\\Hello\\Components with space\\Test$name.cshtml", relativePath: "\\Components with space\\Test$name.cshtml"); var codeDocument = TestRazorCodeDocument.Create(sourceDocument, Array.Empty <RazorSourceDocument>()); var documentNode = new DocumentIntermediateNode() { Options = RazorCodeGenerationOptions.Create(c => { c.RootNamespace = "Hel?o.World"; }) }; codeDocument.SetDocumentIntermediateNode(documentNode); // Act codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace); // Assert Assert.Equal("Hel_o.World.Components_with_space", @namespace); }
public void Lower_WithImports_AllowsIdenticalNamespacesInPrimaryDocument() { // Arrange var source = TestRazorSourceDocument.Create(@"@using System.Threading.Tasks @using System.Threading.Tasks"); var imports = new[] { TestRazorSourceDocument.Create("@using System.Threading.Tasks"), }; var codeDocument = TestRazorCodeDocument.Create(source, imports); // Act var documentNode = Lower(codeDocument); // Assert Children( documentNode, n => Using("System.Threading.Tasks", n), n => Using("System.Threading.Tasks", n)); }
public void Lower_WithImports_IgnoresBlockDirective() { // Arrange var source = TestRazorSourceDocument.Create("<p>Hi!</p>"); var imports = new[] { TestRazorSourceDocument.Create("@block token { }"), }; var codeDocument = TestRazorCodeDocument.Create(source, imports); // Act var documentNode = Lower(codeDocument, b => { b.AddDirective(DirectiveDescriptor.CreateDirective("block", DirectiveKind.RazorBlock, d => d.AddMemberToken())); }); // Assert Children( documentNode, n => Html("<p>Hi!</p>", n)); }
public void Execute_CollatesSyntaxDiagnosticsFromSourceDocument() { // Arrange var phase = new DefaultRazorIntermediateNodeLoweringPhase(); var engine = RazorProjectEngine.CreateEmpty(b => { b.Phases.Add(phase); b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false)); }); var codeDocument = TestRazorCodeDocument.Create("<p class=@("); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source)); // Act phase.Execute(codeDocument); // Assert var documentNode = codeDocument.GetDocumentIntermediateNode(); var diagnostic = Assert.Single(documentNode.Diagnostics); Assert.Equal(@"The explicit expression block is missing a closing "")"" character. Make sure you have a matching "")"" character for all the ""("" characters within this block, and that none of the "")"" characters are being interpreted as markup.", diagnostic.GetMessage(CultureInfo.CurrentCulture)); }
public void Execute_DoesNotImportNonFileScopedSinglyOccurringDirectives_Block() { // Arrange var codeBlockDirective = DirectiveDescriptor.CreateCodeBlockDirective("code", b => b.AddStringToken()); var razorBlockDirective = DirectiveDescriptor.CreateRazorBlockDirective("razor", b => b.AddStringToken()); var phase = new DefaultRazorIntermediateNodeLoweringPhase(); var engine = RazorProjectEngine.CreateEmpty(b => { b.Phases.Add(phase); b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false)); b.AddDirective(codeBlockDirective); b.AddDirective(razorBlockDirective); }); var options = RazorParserOptions.Create(builder => { builder.Directives.Add(codeBlockDirective); builder.Directives.Add(razorBlockDirective); }); var importSource = TestRazorSourceDocument.Create( @"@code ""code block"" { } @razor ""razor block"" { }", filePath: "testImports.cshtml"); var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>"); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options)); codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) }); // Act phase.Execute(codeDocument); // Assert var documentNode = codeDocument.GetDocumentIntermediateNode(); var directives = documentNode.Children.OfType <DirectiveIntermediateNode>(); Assert.Empty(directives); }
public void Lower_WithImports_Using() { // Arrange var source = TestRazorSourceDocument.Create(@"@using System.Threading.Tasks <p>Hi!</p>"); var imports = new[] { TestRazorSourceDocument.Create("@using System.Globalization"), TestRazorSourceDocument.Create("@using System.Text"), }; var codeDocument = TestRazorCodeDocument.Create(source, imports); // Act var documentNode = Lower(codeDocument); // Assert Children( documentNode, n => Using("System.Globalization", n), n => Using("System.Text", n), n => Using("System.Threading.Tasks", n), n => Html("<p>Hi!</p>", n)); }