예제 #1
0
        public void InstrumentationPass_SkipsTagHelper_WithoutLocation()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Push(new TagHelperIntermediateNode());

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n => Assert.IsType <TagHelperIntermediateNode>(n));
        }
        public void InstrumentationPass_SkipsCSharpExpression_WithoutLocation()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Push(new CSharpExpressionIntermediateNode());
            builder.Add(new IntermediateToken()
            {
                Content = "Hi",
                Kind    = TokenKind.CSharp,
            });

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n => CSharpExpression("Hi", n));
        }
예제 #3
0
    public void Execute_Match_AddsGlobalTargetExtensions()
    {
        // Arrange
        var documentNode = new DocumentIntermediateNode()
        {
            Options = RazorCodeGenerationOptions.CreateDefault(),
        };

        var expected = new ICodeTargetExtension[]
        {
            new MyExtension1(),
            new MyExtension2(),
        };

        var pass = new TestDocumentClassifierPass();

        pass.Engine = RazorProjectEngine.CreateEmpty(b =>
        {
            for (var i = 0; i < expected.Length; i++)
            {
                b.AddTargetExtension(expected[i]);
            }
        }).Engine;

        ICodeTargetExtension[] extensions = null;

        pass.CodeTargetCallback = (builder) => extensions = builder.TargetExtensions.ToArray();

        // Act
        pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);

        // Assert
        Assert.Equal(expected, extensions);
    }
    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 InstrumentationPass_InstrumentsTagHelper()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Add(new TagHelperIntermediateNode()
            {
                Source = CreateSource(3),
            });

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n => BeginInstrumentation("3, 3, false", n),
                n => Assert.IsType <TagHelperIntermediateNode>(n),
                n => EndInstrumentation(n));
        }
    public void Execute_ParsesImports()
    {
        // Arrange
        var phase  = new DefaultRazorParsingPhase();
        var engine = RazorProjectEngine.CreateEmpty((builder) =>
        {
            builder.Phases.Add(phase);
            builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
            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 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 InstrumentationPass_NoOps_ForDesignTime()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDesignTimeDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Push(new HtmlContentIntermediateNode());
            builder.Add(new IntermediateToken()
            {
                Content = "Hi",
                Kind    = TokenKind.Html,
            });
            builder.Pop();

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n => IntermediateNodeAssert.Html("Hi", n));
        }
    public void DescriptorProvider_FindsVCTH()
    {
        // Arrange
        var code = @"
        public class StringParameterViewComponent
        {
            public string Invoke(string foo, string bar) => null;
        }
";

        var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));

        var context = TagHelperDescriptorProviderContext.Create();

        context.SetCompilation(compilation);

        var provider = new ViewComponentTagHelperDescriptorProvider()
        {
            Engine = RazorProjectEngine.CreateEmpty().Engine,
        };

        var expectedDescriptor = TagHelperDescriptorBuilder.Create(
            ViewComponentTagHelperConventions.Kind,
            "__Generated__StringParameterViewComponentTagHelper",
            TestCompilation.AssemblyName)
                                 .TypeName("__Generated__StringParameterViewComponentTagHelper")
                                 .DisplayName("StringParameterViewComponentTagHelper")
                                 .TagMatchingRuleDescriptor(rule =>
                                                            rule
                                                            .RequireTagName("vc:string-parameter")
                                                            .RequireAttributeDescriptor(attribute => attribute.Name("foo"))
                                                            .RequireAttributeDescriptor(attribute => attribute.Name("bar")))
                                 .BoundAttributeDescriptor(attribute =>
                                                           attribute
                                                           .Name("foo")
                                                           .PropertyName("foo")
                                                           .TypeName(typeof(string).FullName)
                                                           .DisplayName("string StringParameterViewComponentTagHelper.foo"))
                                 .BoundAttributeDescriptor(attribute =>
                                                           attribute
                                                           .Name("bar")
                                                           .PropertyName("bar")
                                                           .TypeName(typeof(string).FullName)
                                                           .DisplayName("string StringParameterViewComponentTagHelper.bar"))
                                 .AddMetadata(ViewComponentTagHelperMetadata.Name, "StringParameter")
                                 .Build();

        // Act
        provider.Execute(context);

        // Assert
        Assert.Single(context.Results, d => TagHelperDescriptorComparer.Default.Equals(d, expectedDescriptor));
    }
예제 #10
0
        public void InstrumentationPass_SkipsCSharpExpression_InsideTagHelperProperty()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Push(new TagHelperIntermediateNode());

            builder.Push(new TagHelperPropertyIntermediateNode());

            builder.Push(new CSharpExpressionIntermediateNode()
            {
                Source = CreateSource(5)
            });

            builder.Add(new IntermediateToken()
            {
                Content = "Hi",
                Kind    = TokenKind.CSharp,
            });

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n =>
            {
                Assert.IsType <TagHelperIntermediateNode>(n);
                Children(
                    n,
                    c =>
                {
                    Assert.IsType <TagHelperPropertyIntermediateNode>(c);
                    Children(
                        c,
                        s => CSharpExpression("Hi", s));
                });
            });
        }
    public void Execute_ThrowsForMissingDependency()
    {
        // Arrange
        var phase = new DefaultRazorOptimizationPhase();

        var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // Act & Assert
        ExceptionAssert.Throws <InvalidOperationException>(
            () => phase.Execute(codeDocument),
            $"The '{nameof(DefaultRazorOptimizationPhase)}' phase requires a '{nameof(DocumentIntermediateNode)}' " +
            $"provided by the '{nameof(RazorCodeDocument)}'.");
    }
    public void Execute_ExecutesPhasesInOrder()
    {
        // Arrange
        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // We're going to set up mocks to simulate a sequence of passes. We don't care about
        // what's in the nodes, we're just going to look at the identity via strict mocks.
        var originalNode   = new DocumentIntermediateNode();
        var firstPassNode  = new DocumentIntermediateNode();
        var secondPassNode = new DocumentIntermediateNode();

        codeDocument.SetDocumentIntermediateNode(originalNode);

        var firstPass = new Mock <IRazorOptimizationPass>(MockBehavior.Strict);

        firstPass.SetupGet(m => m.Order).Returns(0);
        firstPass.SetupProperty(m => m.Engine);
        firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
        {
            originalNode.Children.Add(firstPassNode);
        });

        var secondPass = new Mock <IRazorOptimizationPass>(MockBehavior.Strict);

        secondPass.SetupGet(m => m.Order).Returns(1);
        secondPass.SetupProperty(m => m.Engine);
        secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
        {
            // Works only when the first pass has run before this.
            originalNode.Children[0].Children.Add(secondPassNode);
        });

        var phase = new DefaultRazorOptimizationPhase();

        var engine = RazorProjectEngine.CreateEmpty(b =>
        {
            b.Phases.Add(phase);

            b.Features.Add(firstPass.Object);
            b.Features.Add(secondPass.Object);
        });

        // Act
        phase.Execute(codeDocument);

        // Assert
        Assert.Same(secondPassNode, codeDocument.GetDocumentIntermediateNode().Children[0].Children[0]);
    }
    public void Execute_AddsSyntaxTree()
    {
        // Arrange
        var phase  = new DefaultRazorParsingPhase();
        var engine = RazorProjectEngine.CreateEmpty(builder =>
        {
            builder.Phases.Add(phase);
            builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
        });

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // Act
        phase.Execute(codeDocument);

        // Assert
        Assert.NotNull(codeDocument.GetSyntaxTree());
    }
    public void Execute_ThrowsForMissingDependency_SyntaxTree()
    {
        // Arrange
        var phase = new DefaultRazorIntermediateNodeLoweringPhase();

        var engine = RazorProjectEngine.CreateEmpty(b =>
        {
            b.Phases.Add(phase);
            b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
        });

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // Act & Assert
        ExceptionAssert.Throws <InvalidOperationException>(
            () => phase.Execute(codeDocument),
            $"The '{nameof(DefaultRazorIntermediateNodeLoweringPhase)}' phase requires a '{nameof(RazorSyntaxTree)}' " +
            $"provided by the '{nameof(RazorCodeDocument)}'.");
    }
예제 #15
0
    public void Execute_ExecutesPhasesInOrder()
    {
        // Arrange
        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // We're going to set up mocks to simulate a sequence of passes. We don't care about
        // what's in the trees, we're just going to look at the identity via strict mocks.
        var originalSyntaxTree   = RazorSyntaxTree.Parse(codeDocument.Source);
        var firstPassSyntaxTree  = RazorSyntaxTree.Parse(codeDocument.Source);
        var secondPassSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);

        codeDocument.SetSyntaxTree(originalSyntaxTree);

        var firstPass = new Mock <IRazorSyntaxTreePass>(MockBehavior.Strict);

        firstPass.SetupGet(m => m.Order).Returns(0);
        firstPass.SetupProperty(m => m.Engine);
        firstPass.Setup(m => m.Execute(codeDocument, originalSyntaxTree)).Returns(firstPassSyntaxTree);

        var secondPass = new Mock <IRazorSyntaxTreePass>(MockBehavior.Strict);

        secondPass.SetupGet(m => m.Order).Returns(1);
        secondPass.SetupProperty(m => m.Engine);
        secondPass.Setup(m => m.Execute(codeDocument, firstPassSyntaxTree)).Returns(secondPassSyntaxTree);

        var phase = new DefaultRazorSyntaxTreePhase();

        var engine = RazorProjectEngine.CreateEmpty(b =>
        {
            b.Phases.Add(phase);

            b.Features.Add(firstPass.Object);
            b.Features.Add(secondPass.Object);
        });

        // Act
        phase.Execute(codeDocument);

        // Assert
        Assert.Same(secondPassSyntaxTree, codeDocument.GetSyntaxTree());
    }
    public void OnInitialized_OrdersPassesInAscendingOrder()
    {
        // Arrange & Act
        var phase = new DefaultRazorOptimizationPhase();

        var first  = Mock.Of <IRazorOptimizationPass>(p => p.Order == 15);
        var second = Mock.Of <IRazorOptimizationPass>(p => p.Order == 17);

        var engine = RazorProjectEngine.CreateEmpty(b =>
        {
            b.Phases.Add(phase);

            b.Features.Add(second);
            b.Features.Add(first);
        });

        // Assert
        Assert.Collection(
            phase.Passes,
            p => Assert.Same(first, p),
            p => Assert.Same(second, p));
    }
    public void Execute_UsesConfigureParserFeatures()
    {
        // Arrange
        var phase  = new DefaultRazorParsingPhase();
        var engine = RazorProjectEngine.CreateEmpty((builder) =>
        {
            builder.Phases.Add(phase);
            builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
            builder.Features.Add(new MyParserOptionsFeature());
        });

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        // Act
        phase.Execute(codeDocument);

        // Assert
        var syntaxTree = codeDocument.GetSyntaxTree();
        var directive  = Assert.Single(syntaxTree.Options.Directives);

        Assert.Equal("test", directive.Directive);
    }
    public void Execute_CollatesSyntaxDiagnosticsFromImportDocuments()
    {
        // Arrange
        var phase  = new DefaultRazorIntermediateNodeLoweringPhase();
        var engine = RazorProjectEngine.CreateEmpty(b =>
        {
            b.Phases.Add(phase);
            b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
        });

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));
        codeDocument.SetImportSyntaxTrees(new[]
        {
            RazorSyntaxTree.Parse(TestRazorSourceDocument.Create("@ ")),
            RazorSyntaxTree.Parse(TestRazorSourceDocument.Create("<p @(")),
        });
        var options = RazorCodeGenerationOptions.CreateDefault();

        // Act
        phase.Execute(codeDocument);

        // Assert
        var documentNode = codeDocument.GetDocumentIntermediateNode();

        Assert.Collection(documentNode.Diagnostics,
                          diagnostic =>
        {
            Assert.Equal(@"A space or line break was encountered after the ""@"" character.  Only valid identifiers, keywords, comments, ""("" and ""{"" are valid at the start of a code block and they must occur immediately following ""@"" with no space in between.",
                         diagnostic.GetMessage(CultureInfo.CurrentCulture));
        },
                          diagnostic =>
        {
            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_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));
    }
예제 #20
0
        public void InstrumentationPass_InstrumentsHtml()
        {
            // Arrange
            var document = new DocumentIntermediateNode()
            {
                Options = RazorCodeGenerationOptions.CreateDefault(),
            };

            var builder = IntermediateNodeBuilder.Create(document);

            builder.Push(new HtmlContentIntermediateNode()
            {
                Source = CreateSource(1),
            });
            builder.Add(new IntermediateToken()
            {
                Content = "Hi",
                Kind    = TokenKind.Html,
                Source  = CreateSource(1)
            });
            builder.Pop();

            var pass = new InstrumentationPass()
            {
                Engine = RazorProjectEngine.CreateEmpty().Engine,
            };

            // Act
            pass.Execute(TestRazorCodeDocument.CreateEmpty(), document);

            // Assert
            Children(
                document,
                n => BeginInstrumentation("1, 1, true", n),
                n => IntermediateNodeAssert.Html("Hi", n),
                n => EndInstrumentation(n));
        }
    public void Execute_ThrowsForMissingDependency_CodeTarget()
    {
        // Arrange
        var phase = new DefaultRazorCSharpLoweringPhase();

        var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));

        var codeDocument = TestRazorCodeDocument.CreateEmpty();

        codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));

        var irDocument = new DocumentIntermediateNode()
        {
            DocumentKind = "test",
        };

        codeDocument.SetDocumentIntermediateNode(irDocument);

        // Act & Assert
        ExceptionAssert.Throws <InvalidOperationException>(
            () => phase.Execute(codeDocument),
            $"The document of kind 'test' does not have a '{nameof(CodeTarget)}'. " +
            $"The document classifier must set a value for '{nameof(DocumentIntermediateNode.Target)}'.");
    }
    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);
    }