public async Task Handle_ProcessDiagnostics_HTML_WithCSharpInAttribute_SingleDiagnostic()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p style=\"padding: @System.Math.PI px;\">Hello</p>",
                "var __o = System.Math.PI",
                new[] {
                new SourceMapping(
                    new SourceSpan(20, 14),
                    new SourceSpan(10, 14))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[] { new Diagnostic()
                                      {
                                          Range = new Range(new Position(0, 18), new Position(0, 19))
                                      } },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Empty(response.Diagnostics);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_Unsupported()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now</p>",
                "var __o = DateTime.Now",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 12),
                    new SourceSpan(10, 12))
            });

            codeDocument.SetUnsupported();
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] { new Diagnostic()
                                      {
                                          Range = new Range(new Position(0, 10), new Position(0, 22))
                                      } },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Empty(response.Diagnostics);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_CSharpError_Unmapped_ReturnsUndefinedRangeDiagnostic()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now</p>",
                "var __o = DateTime.Now",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 12),
                    new SourceSpan(10, 12))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] {
                    new Diagnostic()
                    {
                        Severity = DiagnosticSeverity.Error,
                        Range    = new Range(new Position(0, 0), new Position(0, 3))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Equal(RangeExtensions.UndefinedRange, response.Diagnostics[0].Range);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_Html_Razor_IgnoreMissingEndTagDiagnostic()
        {
            // Arrange
            var documentPath        = "C:/path/to/document.razor";
            var codeDocument        = CreateCodeDocument("<p>@DateTime.Now", kind: FileKinds.Component);
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[]
                {
                    new Diagnostic()
                    {
                        Code  = new DiagnosticCode(HtmlErrorCodes.MissingEndTagErrorCode),
                        Range = new Range(new Position(0, 0), new Position(0, 3))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Empty(response.Diagnostics);
        }
        public async Task Handle_ProcessDiagnostics_Html_CSHTML_DoNotIgnoreMissingEndTagDiagnostic()
        {
            // Arrange
            var documentPath        = "C:/path/to/document.cshtml";
            var codeDocument        = CreateCodeDocument("<p>@DateTime.Now");
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[]
                {
                    new Diagnostic()
                    {
                        Code  = new DiagnosticCode(HtmlErrorCodes.MissingEndTagErrorCode),
                        Range = new Range(new Position(0, 0), new Position(0, 3))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            var returnedDiagnostic = Assert.Single(response.Diagnostics);

            Assert.Equal(HtmlErrorCodes.MissingEndTagErrorCode, returnedDiagnostic.Code.Value.String);
        }
        public async Task Handle_ProcessDiagnostics_HTML_WithCSharpInTagHelperAttribute_MultipleDiagnostics()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";

            var descriptor = TagHelperDescriptorBuilder.Create("ButtonTagHelper", "TestAssembly");

            descriptor.SetTypeName("TestNamespace.ButtonTagHelper");
            descriptor.TagMatchingRule(builder => builder.RequireTagName("button"));
            descriptor.BindAttribute(builder =>
                                     builder
                                     .Name("onactivate")
                                     .PropertyName("onactivate")
                                     .TypeName(typeof(string).FullName));

            var addTagHelper = $"@addTagHelper *, TestAssembly{Environment.NewLine}";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                $"{addTagHelper}<button onactivate=\"Send\" disabled=\"@(Something)\">Hi</button>",
                $"var __o = Send;var __o = Something;",
                new[] {
                new SourceMapping(
                    new SourceSpan(addTagHelper.Length + 20, 4),
                    new SourceSpan(addTagHelper.Length + 10, 4)),

                new SourceMapping(
                    new SourceSpan(addTagHelper.Length + 38, 9),
                    new SourceSpan(addTagHelper.Length + 25, 9))
            },
                new[] {
                descriptor.Build()
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[]
                {
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, addTagHelper.Length + 20), new Position(0, addTagHelper.Length + 25))
                    },
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, addTagHelper.Length + 38), new Position(0, addTagHelper.Length + 47))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Empty(response.Diagnostics);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_Html_DisableInvalidNestingWarningInTagHelper()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var descriptor   = GetButtonTagHelperDescriptor();

            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                $@"@addTagHelper *, TestAssembly
<button>
    @* Should NOT show warning *@
    <div>
        <option></option>
    </div>
</button>

@* Should show warning *@
<div>
    <option></option>
</div>",
                projectedCSharpSource: string.Empty,
                sourceMappings: Array.Empty <SourceMapping>(),
                new[] {
                descriptor.Build()
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[]
                {
                    new Diagnostic()
                    {
                        Code  = new DiagnosticCode(HtmlErrorCodes.InvalidNestingErrorCode),
                        Range = new Range(new Position(4, 8), new Position(4, 17))
                    },
                    new Diagnostic()
                    {
                        Code  = new DiagnosticCode(HtmlErrorCodes.InvalidNestingErrorCode),
                        Range = new Range(new Position(10, 4), new Position(10, 13))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            var d = Assert.Single(response.Diagnostics);

            Assert.Equal(HtmlErrorCodes.InvalidNestingErrorCode, d.Code);
            Assert.Equal(10, d.Range.Start.Line);
        }
        public async Task Handle_ProcessDiagnostics_HTML_WithCSharpInAttribute_MultipleDiagnostics()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p style=\"abc;padding: @System.Math.PI px;abc;\">Hello</p>",
                "var __o = System.Math.PI",
                new[] {
                new SourceMapping(
                    new SourceSpan(24, 14),
                    new SourceSpan(10, 14))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Html,
                Diagnostics = new[]
                {
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, 1), new Position(0, 2))
                    },                                                                                  // start of `p` tag
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, 13), new Position(0, 14))
                    },                                                                                  // leading `abc`
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, 25), new Position(0, 26))
                    },                                                                                  // `@`
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, 45), new Position(0, 46))
                    },                                                                                  // trailing `abc`
                    new Diagnostic()
                    {
                        Range = new Range(new Position(0, 55), new Position(0, 57))
                    }                                                                                   // in `Hello`
                },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Collection(response.Diagnostics,
                              d => Assert.Equal(new Range(new Position(0, 1), new Position(0, 2)), d.Range),
                              d => Assert.Equal(new Range(new Position(0, 55), new Position(0, 57)), d.Range));
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_DocumentResolveFailed_ThrowsDebugFail()
        {
            // Arrange
            var documentPath     = "C:/path/to/document.cshtml";
            var documentResolver = new Mock <DocumentResolver>();
            var documentSnapshot = default(DocumentSnapshot);

            documentResolver.Setup(resolver => resolver.TryResolveDocument(documentPath, out documentSnapshot))
            .Returns(false);

            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver.Object, DocumentVersionCache, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind             = RazorLanguageKind.CSharp,
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act & Assert
            await Assert.ThrowsAnyAsync <Exception>(async() => await Task.Run(() => diagnosticsEndpoint.Handle(request, default)));
        }
        public async Task Handle_DocumentVersionFailed_ReturnsNullDiagnostics()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now</p>",
                "var __o = DateTime.Now",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 12),
                    new SourceSpan(10, 12))
            });
            var documentResolver     = CreateDocumentResolver(documentPath, codeDocument);
            var documentVersionCache = new Mock <DocumentVersionCache>();
            int?version = default;

            documentVersionCache.Setup(cache => cache.TryGetDocumentVersion(It.IsAny <DocumentSnapshot>(), out version))
            .Returns(false);

            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, documentVersionCache.Object, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] { new Diagnostic()
                                      {
                                          Range = new Range(new Position(0, 10), new Position(0, 22))
                                      } },
                RazorDocumentUri = new Uri(documentPath),
            };
            var expectedRange = new Range(new Position(0, 4), new Position(0, 16));

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Equal(expectedRange, response.Diagnostics[0].Range);
            Assert.Null(response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_Razor()
        {
            // Arrange
            var documentPath        = "C:/path/to/document.cshtml";
            var codeDocument        = CreateCodeDocument("<p>@DateTime.Now</p>");
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.Razor,
                Diagnostics = new[] { new Diagnostic()
                                      {
                                          Range = new Range(new Position(0, 3), new Position(0, 4))
                                      } },
                RazorDocumentUri = new Uri(documentPath),
            };

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Equal(request.Diagnostics[0].Range, response.Diagnostics[0].Range);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_DoNotFilterErrorDiagnostics_CSharpError()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now</p>",
                "var __o = DateTime.Now",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 12),
                    new SourceSpan(10, 12))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] {
                    new Diagnostic()
                    {
                        Range    = new Range(new Position(0, 10), new Position(0, 22)),
                        Code     = RazorDiagnosticsEndpoint.DiagnosticsToIgnore.First(),
                        Severity = DiagnosticSeverity.Error
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };
            var expectedRange = new Range(new Position(0, 4), new Position(0, 16));

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Equal(expectedRange, response.Diagnostics[0].Range);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        public async Task Handle_ProcessDiagnostics_CSharpError_CS1525_NotInAttribute_ReturnsDiagnostics()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now)</p>",
                "var __o = DateTime.Now)",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 13),
                    new SourceSpan(10, 13))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] {
                    new Diagnostic()
                    {
                        Code     = new DiagnosticCode("CS1525"),
                        Severity = DiagnosticSeverity.Error,
                        Range    = new Range(new Position(0, 12), new Position(0, 13))
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };
            var expectedRange = new Range(new Position(0, 6), new Position(0, 7));

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Equal(expectedRange, response.Diagnostics[0].Range);
            Assert.Equal(1337, response.HostDocumentVersion);
        }