From 07665c99e8cf3f2580df441a70712c9d9fbfed4c Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Tue, 25 Apr 2017 12:46:16 -0700 Subject: [PATCH] Add JavaScriptSnippetTagHelperComponent (#27) Addresses #25 --- AzureIntegration.sln | 9 +- build/dependencies.props | 1 + ...icationInsightsHostingStartupSample.csproj | 3 + .../Controllers/HomeController.cs | 15 ++ .../Properties/launchSettings.json | 3 +- .../Startup.cs | 14 ++ .../Views/Home/ScriptCheck.cshtml | 3 + .../Views/Shared/_Layout.cshtml | 19 ++ .../Views/_ViewImports.cshtml | 2 + .../Views/_ViewStart.cshtml | 3 + .../appsettings.Development.json | 10 + .../appsettings.json | 11 + .../ApplicationInsightsStartupLoader.cs | 14 +- .../JavaScriptSnippetTagHelperComponent.cs | 42 ++++ ....ApplicationInsights.HostingStartup.csproj | 1 + .../AfterScript.html | 13 ++ ...pplicationInsightsJavaScriptSnippetTest.cs | 132 ++++++++++++ ...cationInsightsJavaScriptSnippetTest.csproj | 39 ++++ .../BeforeScript.html | 9 + .../Rendered.html | 26 +++ .../Validator.cs | 191 ++++++++++++++++++ ...AppServicesWebHostBuilderExtensionsTest.cs | 1 - ...e.AzureAppServicesIntegration.Tests.csproj | 4 + 23 files changed, 561 insertions(+), 4 deletions(-) create mode 100644 sample/ApplicationInsightsHostingStartupSample/Controllers/HomeController.cs create mode 100644 sample/ApplicationInsightsHostingStartupSample/Views/Home/ScriptCheck.cshtml create mode 100644 sample/ApplicationInsightsHostingStartupSample/Views/Shared/_Layout.cshtml create mode 100644 sample/ApplicationInsightsHostingStartupSample/Views/_ViewImports.cshtml create mode 100644 sample/ApplicationInsightsHostingStartupSample/Views/_ViewStart.cshtml create mode 100644 sample/ApplicationInsightsHostingStartupSample/appsettings.Development.json create mode 100644 sample/ApplicationInsightsHostingStartupSample/appsettings.json create mode 100644 src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/JavaScriptSnippetTagHelperComponent.cs create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/AfterScript.html create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.cs create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.csproj create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/BeforeScript.html create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/Rendered.html create mode 100644 test/ApplicationInsightsJavaScriptSnippetTest/Validator.cs diff --git a/AzureIntegration.sln b/AzureIntegration.sln index 620c224223..3f02a3104a 100644 --- a/AzureIntegration.sln +++ b/AzureIntegration.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26406.2 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServicesIntegration", "src\Microsoft.AspNetCore.AzureAppServicesIntegration\Microsoft.AspNetCore.AzureAppServicesIntegration.csproj", "{5916BEB5-0969-469B-976C-A392E015DFAC}" EndProject @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Web.Xdt.Extension EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServices.SiteExtension", "src\Microsoft.AspNetCore.AzureAppServices.SiteExtension\Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj", "{1CE2D76B-39E6-46C0-8F6F-C63E370955A9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsightsJavaScriptSnippetTest", "test\ApplicationInsightsJavaScriptSnippetTest\ApplicationInsightsJavaScriptSnippetTest.csproj", "{0899A101-E451-40A4-81B0-7AA18202C25D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +77,10 @@ Global {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Release|Any CPU.Build.0 = Release|Any CPU + {0899A101-E451-40A4-81B0-7AA18202C25D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0899A101-E451-40A4-81B0-7AA18202C25D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0899A101-E451-40A4-81B0-7AA18202C25D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0899A101-E451-40A4-81B0-7AA18202C25D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -89,5 +95,6 @@ Global {33E245F0-2566-4B5B-BA7C-8895B7A697AE} = {37237C93-6855-40D9-9E60-418B093EF49A} {9B22E525-FEC9-4C7C-9F9C-598C15BD0250} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435} {1CE2D76B-39E6-46C0-8F6F-C63E370955A9} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435} + {0899A101-E451-40A4-81B0-7AA18202C25D} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0} EndGlobalSection EndGlobal diff --git a/build/dependencies.props b/build/dependencies.props index 8f5d72c7dc..7179a79818 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -1,6 +1,7 @@ 2.0.0-* + 0.4.0-* 2.0.0 4.3.0 2.0.0-* diff --git a/sample/ApplicationInsightsHostingStartupSample/ApplicationInsightsHostingStartupSample.csproj b/sample/ApplicationInsightsHostingStartupSample/ApplicationInsightsHostingStartupSample.csproj index 901e0691d6..f2469be01a 100644 --- a/sample/ApplicationInsightsHostingStartupSample/ApplicationInsightsHostingStartupSample.csproj +++ b/sample/ApplicationInsightsHostingStartupSample/ApplicationInsightsHostingStartupSample.csproj @@ -4,12 +4,15 @@ netcoreapp2.0 + win7-x86;win7-x64;linux-x64;osx.10.12-x64 $(PackageTargetFallback);portable-net40+sl5+win8+wp8+wpa81;portable-net45+win8+wp8+wpa81 + + diff --git a/sample/ApplicationInsightsHostingStartupSample/Controllers/HomeController.cs b/sample/ApplicationInsightsHostingStartupSample/Controllers/HomeController.cs new file mode 100644 index 0000000000..f81a169048 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/Controllers/HomeController.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Mvc; + +namespace ApplicationInsightsJavaScriptSnippetSample.Controllers +{ + public class HomeController : Controller + { + public IActionResult ScriptCheck() + { + return View(); + } + } +} diff --git a/sample/ApplicationInsightsHostingStartupSample/Properties/launchSettings.json b/sample/ApplicationInsightsHostingStartupSample/Properties/launchSettings.json index 1c070ade88..beabdcaf51 100644 --- a/sample/ApplicationInsightsHostingStartupSample/Properties/launchSettings.json +++ b/sample/ApplicationInsightsHostingStartupSample/Properties/launchSettings.json @@ -16,9 +16,10 @@ "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.ApplicationInsights.HostingStartup;Microsoft.AspNetCore.Server.IISIntegration" } }, - "AzureAppServicesHostingStartupSample": { + "ApplicationInsightsHostingStartupSample": { "commandName": "project", "launchBrowser": true, + "launchUrl": "http://localhost:5000/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.ApplicationInsights.HostingStartup" diff --git a/sample/ApplicationInsightsHostingStartupSample/Startup.cs b/sample/ApplicationInsightsHostingStartupSample/Startup.cs index cbb697e1e9..9ef8e2d060 100644 --- a/sample/ApplicationInsightsHostingStartupSample/Startup.cs +++ b/sample/ApplicationInsightsHostingStartupSample/Startup.cs @@ -1,8 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; +using System.IO; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -12,6 +17,7 @@ namespace IISSample { public void ConfigureServices(IServiceCollection services) { + services.AddMvc(); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) @@ -20,6 +26,7 @@ namespace IISSample var logger = loggerfactory.CreateLogger("Requests"); + app.UseMvcWithDefaultRoute(); app.Run(async (context) => { logger.LogDebug("Received request: " + context.Request.Method + " " + context.Request.Path); @@ -67,9 +74,16 @@ namespace IISSample public static void Main(string[] args) { + var config = new ConfigurationBuilder() + .AddCommandLine(args) + .AddEnvironmentVariables(prefix: "ASPNETCORE_") + .Build(); + var host = new WebHostBuilder() .UseKestrel() .UseStartup() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(config) .Build(); host.Run(); diff --git a/sample/ApplicationInsightsHostingStartupSample/Views/Home/ScriptCheck.cshtml b/sample/ApplicationInsightsHostingStartupSample/Views/Home/ScriptCheck.cshtml new file mode 100644 index 0000000000..681b705c37 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/Views/Home/ScriptCheck.cshtml @@ -0,0 +1,3 @@ +@{ + ViewData["Title"] = "Home Page"; +} \ No newline at end of file diff --git a/sample/ApplicationInsightsHostingStartupSample/Views/Shared/_Layout.cshtml b/sample/ApplicationInsightsHostingStartupSample/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..cd79e22702 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/Views/Shared/_Layout.cshtml @@ -0,0 +1,19 @@ + + + + + + @ViewData["Title"] - ApplicationInsightsJavaScriptSnippetSample + + + +
+ @RenderBody() +
+
+

© 2017 - ApplicationInsightsJavaScriptSnippetSample

+
+
+ @RenderSection("Scripts", required: false) + + \ No newline at end of file diff --git a/sample/ApplicationInsightsHostingStartupSample/Views/_ViewImports.cshtml b/sample/ApplicationInsightsHostingStartupSample/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..28b49c14b5 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using ApplicationInsightsJavaScriptSnippetSample +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/sample/ApplicationInsightsHostingStartupSample/Views/_ViewStart.cshtml b/sample/ApplicationInsightsHostingStartupSample/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..a5f10045db --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/sample/ApplicationInsightsHostingStartupSample/appsettings.Development.json b/sample/ApplicationInsightsHostingStartupSample/appsettings.Development.json new file mode 100644 index 0000000000..fa8ce71a97 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/sample/ApplicationInsightsHostingStartupSample/appsettings.json b/sample/ApplicationInsightsHostingStartupSample/appsettings.json new file mode 100644 index 0000000000..cc2304feb2 --- /dev/null +++ b/sample/ApplicationInsightsHostingStartupSample/appsettings.json @@ -0,0 +1,11 @@ +{ + "ApplicationInsights": { + "InstrumentationKey": "11111111-2222-3333-4444-555555555555" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning" + } + } +} diff --git a/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/ApplicationInsightsStartupLoader.cs b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/ApplicationInsightsStartupLoader.cs index 8b1c9c38c4..59906ee5cb 100644 --- a/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/ApplicationInsightsStartupLoader.cs +++ b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/ApplicationInsightsStartupLoader.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.DependencyInjection; [assembly: HostingStartup(typeof(Microsoft.AspNetCore.ApplicationInsights.HostingStartup.ApplicationInsightsHostingStartup))] @@ -11,7 +13,7 @@ internal class Program { public static void Main() { } } namespace Microsoft.AspNetCore.ApplicationInsights.HostingStartup { /// - /// A dynamic Application Insights lightup experiance + /// A dynamic Application Insights lightup experience /// public class ApplicationInsightsHostingStartup : IHostingStartup { @@ -22,6 +24,16 @@ namespace Microsoft.AspNetCore.ApplicationInsights.HostingStartup public void Configure(IWebHostBuilder builder) { builder.UseApplicationInsights(); + builder.ConfigureServices(InitializeServices); + } + + /// + /// Adds the Javascript to the . + /// + /// The associated with the application. + private void InitializeServices(IServiceCollection services) + { + services.AddSingleton(); } } } diff --git a/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/JavaScriptSnippetTagHelperComponent.cs b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/JavaScriptSnippetTagHelperComponent.cs new file mode 100644 index 0000000000..a17c4dad04 --- /dev/null +++ b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/JavaScriptSnippetTagHelperComponent.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.ApplicationInsights.AspNetCore; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Microsoft.AspNetCore.ApplicationInsights.HostingStartup +{ + /// + /// The that injects the at the end of the head tag. + /// + public class JavaScriptSnippetTagHelperComponent : TagHelperComponent + { + private readonly string _javaScriptSnippet; + + /// + /// Initializes the . + /// + /// The to inject in the head tag. + public JavaScriptSnippetTagHelperComponent(JavaScriptSnippet javaScriptSnippet) + { + _javaScriptSnippet = javaScriptSnippet.FullScript; + } + + /// + public override int Order => 100; + + /// + /// Appends the to the head tag. + /// + /// The associated with the head tag. + /// The of the head tag. + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (string.Equals(context.TagName, "head", StringComparison.OrdinalIgnoreCase)) + { + output.PostContent.AppendHtml(_javaScriptSnippet); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/Microsoft.AspNetCore.ApplicationInsights.HostingStartup.csproj b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/Microsoft.AspNetCore.ApplicationInsights.HostingStartup.csproj index d22e6fc4da..c664103e9a 100644 --- a/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/Microsoft.AspNetCore.ApplicationInsights.HostingStartup.csproj +++ b/src/Microsoft.AspNetCore.ApplicationInsights.HostingStartup/Microsoft.AspNetCore.ApplicationInsights.HostingStartup.csproj @@ -14,6 +14,7 @@ +
\ No newline at end of file diff --git a/test/ApplicationInsightsJavaScriptSnippetTest/AfterScript.html b/test/ApplicationInsightsJavaScriptSnippetTest/AfterScript.html new file mode 100644 index 0000000000..db50104624 --- /dev/null +++ b/test/ApplicationInsightsJavaScriptSnippetTest/AfterScript.html @@ -0,0 +1,13 @@ + + + +
+ +
+
+

© 2017 - ApplicationInsightsJavaScriptSnippetSample

+
+
+ + + \ No newline at end of file diff --git a/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.cs b/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.cs new file mode 100644 index 0000000000..2625bb1ad5 --- /dev/null +++ b/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.cs @@ -0,0 +1,132 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.xunit; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace ApplicationInsightsJavaScriptSnippetTest +{ + public class ApplicationInsightsJavaScriptSnippetTest : LoggedTest + { + public ApplicationInsightsJavaScriptSnippetTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(ApplicationType.Portable)] + [InlineData(ApplicationType.Standalone)] + public async Task ScriptInjected(ApplicationType applicationType) + { + await JavaScriptSnippetInjectionTestSuite(applicationType); + } + + private async Task JavaScriptSnippetInjectionTestSuite(ApplicationType applicationType) + { + var testName = $"ApplicationInsightsJavaScriptSnippetTest_{applicationType}"; + using (StartLog(out var loggerFactory, testName)) + { + var logger = loggerFactory.CreateLogger(nameof(ApplicationInsightsJavaScriptSnippetTest)); + var deploymentParameters = new DeploymentParameters(GetApplicationPath(applicationType), ServerType.Kestrel, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64) + { + PublishApplicationBeforeDeployment = true, + PreservePublishedApplicationForDebugging = PreservePublishedApplicationForDebugging, + TargetFramework = "netcoreapp2.0", + Configuration = GetCurrentBuildConfiguration(), + ApplicationType = applicationType, + EnvironmentVariables = + { + new KeyValuePair( + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", + "Microsoft.AspNetCore.ApplicationInsights.HostingStartup"), + }, + }; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + var httpClientHandler = new HttpClientHandler(); + var httpClient = deploymentResult.CreateHttpClient(httpClientHandler); + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(async () => + { + return await httpClient.GetAsync("\\Home\\ScriptCheck"); + }, logger: logger, cancellationToken: deploymentResult.HostShutdownToken); + + Assert.False(response == null, "Response object is null because the client could not " + + "connect to the server after multiple retries"); + + var validator = new Validator(httpClient, httpClientHandler, logger, deploymentResult); + + logger.LogInformation("Verifying layout page"); + await validator.VerifyLayoutPage(response); + + logger.LogInformation("Verifying layout page before script"); + await validator.VerifyLayoutPageBeforeScript(response); + + logger.LogInformation("Verifying layout page after script"); + await validator.VerifyLayoutPageAfterScript(response); + + logger.LogInformation("Variation completed successfully."); + } + } + } + + private static string GetApplicationPath(ApplicationType applicationType) + { + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current != null) + { + if (File.Exists(Path.Combine(current.FullName, "AzureIntegration.sln"))) + { + break; + } + current = current.Parent; + } + + if (current == null) + { + throw new InvalidOperationException("Could not find the solution directory"); + } + + return Path.GetFullPath(Path.Combine(current.FullName, "sample", "ApplicationInsightsHostingStartupSample")); + } + + private static bool PreservePublishedApplicationForDebugging + { + get + { + var deletePublishedFolder = Environment.GetEnvironmentVariable("ASPNETCORE_DELETEPUBLISHEDFOLDER"); + + if (string.Equals("false", deletePublishedFolder, StringComparison.OrdinalIgnoreCase) + || string.Equals("0", deletePublishedFolder, StringComparison.OrdinalIgnoreCase)) + { + // preserve the published folder and do not delete it + return true; + } + + // do not preserve the published folder and delete it + return false; + } + } + + private static string GetCurrentBuildConfiguration() + { + var configuration = "Debug"; + if (string.Equals(Environment.GetEnvironmentVariable("Configuration"), "Release", StringComparison.OrdinalIgnoreCase)) + { + configuration = "Release"; + } + + return configuration; + } + } +} diff --git a/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.csproj b/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.csproj new file mode 100644 index 0000000000..2cf5f1795e --- /dev/null +++ b/test/ApplicationInsightsJavaScriptSnippetTest/ApplicationInsightsJavaScriptSnippetTest.csproj @@ -0,0 +1,39 @@ + + + + + + netcoreapp2.0 + netcoreapp2.0 + $(PackageTargetFallback);portable-net40+sl5+win8+wp8+wpa81;portable-net45+win8+wp8+wpa81 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/ApplicationInsightsJavaScriptSnippetTest/BeforeScript.html b/test/ApplicationInsightsJavaScriptSnippetTest/BeforeScript.html new file mode 100644 index 0000000000..af8b08cb33 --- /dev/null +++ b/test/ApplicationInsightsJavaScriptSnippetTest/BeforeScript.html @@ -0,0 +1,9 @@ + + + + + + Home Page - ApplicationInsightsJavaScriptSnippetSample + + + + + +
+ +
+
+

© 2017 - ApplicationInsightsJavaScriptSnippetSample

+
+
+ + + \ No newline at end of file diff --git a/test/ApplicationInsightsJavaScriptSnippetTest/Validator.cs b/test/ApplicationInsightsJavaScriptSnippetTest/Validator.cs new file mode 100644 index 0000000000..52f1bcb7d6 --- /dev/null +++ b/test/ApplicationInsightsJavaScriptSnippetTest/Validator.cs @@ -0,0 +1,191 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace ApplicationInsightsJavaScriptSnippetTest +{ + public class Validator + { + private HttpClient _httpClient; + + private HttpClientHandler _httpClientHandler; + + private readonly ILogger _logger; + + private readonly DeploymentResult _deploymentResult; + + private static readonly Assembly _resourcesAssembly = typeof(ApplicationInsightsJavaScriptSnippetTest).GetTypeInfo().Assembly; + + public Validator( + HttpClient httpClient, + HttpClientHandler httpClientHandler, + ILogger logger, + DeploymentResult deploymentResult) + { + _httpClient = httpClient; + _httpClientHandler = httpClientHandler; + _logger = logger; + _deploymentResult = deploymentResult; + } + + public async Task VerifyLayoutPage(HttpResponseMessage response) + { + var responseContent = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + { + _logger.LogInformation("Layout page : {0}", responseContent); + } + + await ValidateLayoutPage(responseContent); + } + + public async Task VerifyLayoutPageBeforeScript(HttpResponseMessage response) + { + var responseContent = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + { + _logger.LogInformation("Before app insights script : {0}", responseContent); + } + + await ValidateLayoutPageBeforeScript(responseContent); + } + + public async Task VerifyLayoutPageAfterScript(HttpResponseMessage response) + { + var responseContent = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + { + _logger.LogInformation("After app insights script : {0}", responseContent); + } + + await ValidateLayoutPageAfterScript(responseContent); + } + + // Does not check the contents of the JavaScriptSnippet as it might change. Only checks the instrumentation key. + private async Task ValidateLayoutPage(string responseContent) + { + var outputFile = "Rendered.html"; + var expectedContent = await ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false); + foreach (var substring in expectedContent) + { +#if GENERATE_BASELINES + ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent); +#else + Assert.Contains(substring, responseContent); +#endif + } + } + + private async Task ValidateLayoutPageBeforeScript(string responseContent) + { + var outputFile = "BeforeScript.html"; + var expectedContent = await ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false); + + foreach (var substring in expectedContent) + { +#if GENERATE_BASELINES + ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent); +#else + Assert.Contains(substring, responseContent); +#endif + } + } + + private async Task ValidateLayoutPageAfterScript(string responseContent) + { + var outputFile = "AfterScript.html"; + var expectedContent = await ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false); + + foreach (var substring in expectedContent) + { +#if GENERATE_BASELINES + ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent); +#else + Assert.Contains(substring, responseContent); +#endif + } + } + + private static async Task ReadResourceAsync(Assembly assembly, string resourceName, bool sourceFile) + { + using (var stream = GetResourceStream(assembly, resourceName, sourceFile)) + { + if (stream == null) + { + return null; + } + + using (var streamReader = new StreamReader(stream)) + { + return await streamReader.ReadToEndAsync(); + } + } + } + + private static Stream GetResourceStream(Assembly assembly, string resourceName, bool sourceFile) + { + var fullName = $"{ assembly.GetName().Name }.{ resourceName.Replace('/', '.') }"; + if (!Exists(assembly, fullName)) + { +#if GENERATE_BASELINES + if (sourceFile) + { + // Even when generating baselines, a missing source file is a serious problem. + Assert.True(false, $"Manifest resource: { fullName } not found."); + } +#else + // When not generating baselines, a missing source or output file is always an error. + Assert.True(false, $"Manifest resource '{ fullName }' not found."); +#endif + + return null; + } + + var stream = assembly.GetManifestResourceStream(fullName); + if (sourceFile) + { + // Normalize line endings to '\r\n' (CRLF). This removes core.autocrlf, core.eol, core.safecrlf, and + // .gitattributes from the equation and treats "\r\n" and "\n" as equivalent. Does not handle + // some line endings like "\r" but otherwise ensures checksums and line mappings are consistent. + string text; + using (var streamReader = new StreamReader(stream)) + { + text = streamReader.ReadToEnd().Replace("\r", "").Replace("\n", "\r\n"); + } + + var bytes = Encoding.UTF8.GetBytes(text); + stream = new MemoryStream(bytes); + } + + return stream; + } + + private static bool Exists(Assembly assembly, string fullName) + { + var resourceNames = assembly.GetManifestResourceNames(); + foreach (var resourceName in resourceNames) + { + // Resource names are case-sensitive. + if (string.Equals(fullName, resourceName, StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + } +} diff --git a/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs index 5c386f95f5..ed6e49ed15 100644 --- a/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs @@ -3,7 +3,6 @@ using System; using Microsoft.Extensions.Logging; -using Microsoft.AspNetCore.Hosting; using Moq; using Xunit; diff --git a/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj b/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj index 8eae2583c0..e7ee18e134 100644 --- a/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj +++ b/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj @@ -20,4 +20,8 @@ + + + +