public void Visit_GeneratesProperties_ForInjectChunks()
        {
            // Arrange
            var expected =
                @"[ActivateAttribute]
public MyType1 MyPropertyName1 { get; private set; }
[ActivateAttribute]
public MyType2 @MyPropertyName2 { get; private set; }
";
            var writer  = new CSharpCodeWriter();
            var context = CreateContext();

            var visitor = new InjectChunkVisitor(writer, context, "ActivateAttribute");
            var factory = SpanFactory.CreateCsHtml();
            var node    = (Span)factory.Code("Some code")
                          .As(new InjectParameterGenerator("MyType", "MyPropertyName"));

            // Act
            visitor.Accept(new Chunk[]
            {
                new LiteralChunk(),
                new InjectChunk("MyType1", "MyPropertyName1")
                {
                    Association = node
                },
                new InjectChunk("MyType2", "@MyPropertyName2")
                {
                    Association = node
                }
            });
            var code = writer.GenerateCode();

            // Assert
            Assert.Equal(expected, code);
        }
        public void ParseModelKeyword_InfersBaseType_FromModelName(
            string modelName,
            string expectedModel)
        {
            // Arrange
            var documentContent = "@model " + modelName + Environment.NewLine + "Bar";
            var factory         = SpanFactory.CreateCsHtml();
            var errors          = new List <RazorError>();
            var expectedSpans   = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code(modelName + Environment.NewLine)
                .As(new ModelChunkGenerator(expectedModel))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.Markup("Bar")
                .With(new MarkupChunkGenerator())
            };

            // Act
            var spans = ParseDocument(documentContent, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Empty(errors);
        }
        public void ParseModelKeyword_ErrorOnMissingModelType()
        {
            // Arrange + Act
            var errors   = new List <RazorError>();
            var document = "@model   ";
            var spans    = ParseDocument(document, errors);

            // Assert
            var factory       = SpanFactory.CreateCsHtml();
            var expectedSpans = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("  ")
                .As(new ModelChunkGenerator(string.Empty))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml(),
            };
            var expectedErrors = new[]
            {
                new RazorError("The 'model' keyword must be followed by a type name on the same line.",
                               new SourceLocation(1, 0, 1), 5)
            };

            Assert.Equal(expectedSpans, spans);
            Assert.Equal(expectedErrors, errors);
        }
        public void ParseInjectKeyword_ErrorOnMissingPropertyName_WhenTypeNameEndsWithEOF()
        {
            // Arrange
            var errors          = new List <RazorError>();
            var documentContent = "@inject    IMyServi";
            var factory         = SpanFactory.CreateCsHtml();
            var expectedSpans   = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("inject ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("   IMyServi")
                .As(new InjectParameterGenerator("IMyServi", string.Empty))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };
            var expectedErrors = new[]
            {
                new RazorError("A property name must be specified when using the 'inject' statement. " +
                               "Format for a 'inject' statement is '@inject <Type Name> <Property Name>'.",
                               new SourceLocation(1, 0, 1), 21)
            };

            // Act
            var spans = ParseDocument(documentContent, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Equal(expectedErrors, errors);
        }
        public void ParseInjectKeyword_ErrorOnMissingTypeName_WhenTypeNameEndsWithEOF()
        {
            // Arrange
            var errors          = new List <RazorError>();
            var documentContent = "@inject    ";
            var factory         = SpanFactory.CreateCsHtml();
            var expectedSpans   = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("inject ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("   ")
                .As(new InjectParameterGenerator(string.Empty, string.Empty))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };
            var expectedErrors = new[]
            {
                new RazorError("The 'inject' keyword must be followed by a type name on the same line.",
                               new SourceLocation(1, 0, 1), 6)
            };

            // Act
            var spans = ParseDocument(documentContent, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Equal(expectedErrors, errors);
        }
        public void ParseInjectKeyword_ParsesUpToNewLine(
            string injectStatement,
            string expectedService,
            string expectedPropertyName)
        {
            // Arrange
            var documentContent = "@inject " + injectStatement + Environment.NewLine + "Bar";
            var factory         = SpanFactory.CreateCsHtml();
            var errors          = new List <RazorError>();
            var expectedSpans   = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("inject ")
                .Accepts(AcceptedCharacters.None),
                factory.Code(injectStatement + Environment.NewLine)
                .As(new InjectParameterGenerator(expectedService, expectedPropertyName))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.Markup("Bar")
                .With(new MarkupChunkGenerator())
            };

            // Act
            var spans = ParseDocument(documentContent, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Empty(errors);
        }
        public void ParseModelKeyword_HandlesSingleInstance()
        {
            // Arrange
            var document      = "@model    Foo";
            var factory       = SpanFactory.CreateCsHtml();
            var errors        = new List <RazorError>();
            var expectedSpans = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("   Foo")
                .As(new ModelChunkGenerator("Foo"))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };

            // Act
            var spans = ParseDocument(document, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Empty(errors);
        }
        public void ParseInjectKeyword_AllowsOptionalTrailingSemicolon(
            string injectStatement,
            string expectedService,
            string expectedPropertyName)
        {
            // Arrange
            var documentContent = "@inject " + injectStatement;
            var factory         = SpanFactory.CreateCsHtml();
            var errors          = new List <RazorError>();
            var expectedSpans   = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("inject ")
                .Accepts(AcceptedCharacters.None),
                factory.Code(injectStatement)
                .As(new InjectParameterGenerator(expectedService, expectedPropertyName))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };

            // Act
            var spans = ParseDocument(documentContent, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Empty(errors);
        }
        public void Visit_WithDesignTimeHost_GeneratesPropertiesAndLinePragmas_ForInjectChunks()
        {
            // Arrange
            var expected = string.Join(Environment.NewLine,
                                       @"[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]",
                                       @"public",
                                       @"#line 1 """"",
                                       @"MyType1 MyPropertyName1",
                                       "",
                                       @"#line default",
                                       @"#line hidden",
                                       @"{ get; private set; }",
                                       @"[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]",
                                       @"public",
                                       @"#line 1 """"",
                                       @"MyType2 @MyPropertyName2",
                                       "",
                                       @"#line default",
                                       @"#line hidden",
                                       @"{ get; private set; }",
                                       "");
            var writer  = new CSharpCodeWriter();
            var context = CreateContext();

            context.Host.DesignTimeMode = true;

            var visitor = new InjectChunkVisitor(writer, context, "Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute");
            var factory = SpanFactory.CreateCsHtml();
            var node    = (Span)factory.Code("Some code")
                          .As(new InjectParameterGenerator("MyType", "MyPropertyName"));

            // Act
            visitor.Accept(new Chunk[]
            {
                new LiteralChunk(),
                new InjectChunk("MyType1", "MyPropertyName1")
                {
                    Association = node
                },
                new InjectChunk("MyType2", "@MyPropertyName2")
                {
                    Association = node
                }
            });
            var code = writer.GenerateCode();

            // Assert
            Assert.Equal(expected, code);
        }
        public void ParseModelKeyword_ErrorOnInheritsFollowedByModel()
        {
            // Arrange
            var errors   = new List <RazorError>();
            var document =
                "@inherits Bar" + Environment.NewLine
                + "@model Foo";

            var factory       = SpanFactory.CreateCsHtml();
            var expectedSpans = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("inherits ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("Bar" + Environment.NewLine)
                .As(new SetBaseTypeChunkGenerator("Bar"))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("Foo")
                .As(new ModelChunkGenerator("Foo"))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };

            var expectedErrors = new[]
            {
                new RazorError(
                    "The 'inherits' keyword is not allowed when a 'model' keyword is used.",
                    new SourceLocation(9, 0, 9),
                    length: 8)
            };

            // Act
            var spans = ParseDocument(document, errors);

            // Assert
            Assert.Equal(expectedSpans, spans.ToArray());
            Assert.Equal(expectedErrors, errors.ToArray());
        }
        public void ParseModelKeyword_ErrorOnMultipleModelStatements()
        {
            // Arrange + Act
            var errors   = new List <RazorError>();
            var document =
                "@model Foo" + Environment.NewLine
                + "@model Bar";

            var factory       = SpanFactory.CreateCsHtml();
            var expectedSpans = new Span[]
            {
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("Foo" + Environment.NewLine)
                .As(new ModelChunkGenerator("Foo"))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml(),
                factory.CodeTransition(SyntaxConstants.TransitionString)
                .Accepts(AcceptedCharacters.None),
                factory.MetaCode("model ")
                .Accepts(AcceptedCharacters.None),
                factory.Code("Bar")
                .As(new ModelChunkGenerator("Bar"))
                .Accepts(AcceptedCharacters.AnyExceptNewline),
                factory.EmptyHtml()
            };

            var expectedErrors = new[]
            {
                new RazorError(
                    "Only one 'model' statement is allowed in a file.",
                    PlatformNormalizer.NormalizedSourceLocation(13, 1, 1),
                    5)
            };

            // Act
            var spans = ParseDocument(document, errors);

            // Assert
            Assert.Equal(expectedSpans, spans);
            Assert.Equal(expectedErrors, errors);
        }