Test all links in templates (#8628)

Improve templating tests
This commit is contained in:
Ryan Brandenburg 2019-04-04 11:31:24 -07:00 committed by GitHub
parent 237b19b2b9
commit fefffd7751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 638 additions and 65 deletions

View File

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Invoke Node.js modules at runtime in ASP.NET Core applications.</Description>
<TargetFramework>netcoreapp3.0</TargetFramework>

View File

@ -6,6 +6,7 @@
<RazorLangVersion Condition="'$(SupportPagesAndViews)' != 'True'">3.0</RazorLangVersion>
<AddRazorSupportForMvc Condition="'$(SupportPagesAndViews)' == 'True'">true</AddRazorSupportForMvc>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">Company.RazorClassLibrary1</RootNamespace>
<AddRazorSupportForMvc Condition="'$(SupportPagesAndViews)' == 'True'">true</AddRazorSupportForMvc>
</PropertyGroup>
<ItemGroup Condition="'$(SupportPagesAndViews)' == 'True'">

View File

@ -22,12 +22,14 @@ namespace Templates.Test
public ITestOutputHelper Output { get; }
[Fact]
public async Task EmptyWebTemplateAsync()
[Theory]
[InlineData(null)]
[InlineData("F#")]
public async Task EmptyWebTemplateAsync(string languageOverride)
{
Project = await ProjectFactory.GetOrCreateProject("empty", Output);
Project = await ProjectFactory.GetOrCreateProject("empty" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
var createResult = await Project.RunDotNetNewAsync("web");
var createResult = await Project.RunDotNetNewAsync("web", language: languageOverride);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();
@ -43,8 +45,8 @@ namespace Templates.Test
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertOk("/");
}

View File

@ -0,0 +1,71 @@
// 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.Threading.Tasks;
using Templates.Test.Helpers;
using Xunit;
using Xunit.Abstractions;
namespace Templates.Test
{
public class GrpcTemplateTest
{
public GrpcTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
{
ProjectFactory = projectFactory;
Output = output;
}
public Project Project { get; set; }
public ProjectFactoryFixture ProjectFactory { get; }
public ITestOutputHelper Output { get; }
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/7973")]
public async Task GrpcTemplate()
{
Project = await ProjectFactory.GetOrCreateProject("grpc", Output);
var createResult = await Project.RunDotNetNewAsync("grpc");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
using (var serverProcess = Project.StartBuiltServerAsync())
{
Assert.False(
serverProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built server", Project, serverProcess.Process));
using (var clientProcess = Project.StartBuiltClientAsync(serverProcess))
{
// Wait for the client to do its thing
await Task.Delay(100);
Assert.False(
clientProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built client", Project, clientProcess.Process));
}
}
using (var aspNetProcess = Project.StartPublishedServerAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published server", Project, aspNetProcess.Process));
using (var clientProcess = Project.StartPublishedClientAsync())
{
// Wait for the client to do its thing
await Task.Delay(100);
Assert.False(
clientProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built client", Project, clientProcess.Process));
}
}
}
}
}

View File

@ -8,6 +8,8 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using Microsoft.AspNetCore.Certificates.Generation;
using Microsoft.Extensions.CommandLineUtils;
using OpenQA.Selenium;
@ -21,17 +23,19 @@ namespace Templates.Test.Helpers
public class AspNetProcess : IDisposable
{
private const string ListeningMessagePrefix = "Now listening on: ";
private readonly Uri _listeningUri;
private readonly HttpClient _httpClient;
private readonly ITestOutputHelper _output;
internal readonly Uri ListeningUri;
internal ProcessEx Process { get; }
public AspNetProcess(
ITestOutputHelper output,
string workingDirectory,
string dllPath,
IDictionary<string, string> environmentVariables)
IDictionary<string, string> environmentVariables,
bool published = true,
bool hasListeningUri = true)
{
_output = output;
_httpClient = new HttpClient(new HttpClientHandler()
@ -48,18 +52,20 @@ namespace Templates.Test.Helpers
var now = DateTimeOffset.Now;
new CertificateManager().EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1));
output.WriteLine("Running ASP.NET application...");
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), $"exec {dllPath}", envVars: environmentVariables);
_listeningUri = GetListeningUri(output);
var arguments = published ? $"exec {dllPath}" : "run";
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables);
if(hasListeningUri)
{
ListeningUri = GetListeningUri(output);
}
}
public void VisitInBrowser(IWebDriver driver)
{
_output.WriteLine($"Opening browser at {_listeningUri}...");
driver.Navigate().GoToUrl(_listeningUri);
_output.WriteLine($"Opening browser at {ListeningUri}...");
driver.Navigate().GoToUrl(ListeningUri);
if (driver is EdgeDriver)
{
@ -75,7 +81,7 @@ namespace Templates.Test.Helpers
{
_output.WriteLine($"Clicking on link '{continueLink.Text}' to skip invalid certificate error page.");
continueLink.Click();
driver.Navigate().GoToUrl(_listeningUri);
driver.Navigate().GoToUrl(ListeningUri);
}
else
{
@ -85,6 +91,58 @@ namespace Templates.Test.Helpers
}
}
public async Task AssertPagesOk(IEnumerable<Page> pages)
{
foreach (var page in pages)
{
await AssertOk(page.Url);
await ContainsLinks(page);
}
}
public async Task ContainsLinks(Page page)
{
var request = new HttpRequestMessage(
HttpMethod.Get,
new Uri(ListeningUri, page.Url));
var response = await _httpClient.SendAsync(request);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var parser = new HtmlParser();
var html = await parser.ParseAsync(await response.Content.ReadAsStreamAsync());
foreach (IHtmlLinkElement styleSheet in html.GetElementsByTagName("link"))
{
Assert.Equal("stylesheet", styleSheet.Relation);
await AssertOk(styleSheet.Href.Replace("about://", string.Empty));
}
foreach (var script in html.Scripts)
{
if (!string.IsNullOrEmpty(script.Source))
{
await AssertOk(script.Source);
}
}
Assert.True(html.Links.Length == page.Links.Count(), $"Expected {page.Url} to have {page.Links.Count()} links but it had {html.Links.Length}");
foreach ((var link, var expectedLink) in html.Links.Zip(page.Links, Tuple.Create))
{
IHtmlAnchorElement anchor = (IHtmlAnchorElement)link;
if (string.Equals(anchor.Protocol, "about:"))
{
Assert.True(anchor.PathName.EndsWith(expectedLink), $"Expected next link on {page.Url} to be {expectedLink} but it was {anchor.PathName}.");
await AssertOk(anchor.PathName);
}
else
{
Assert.True(string.Equals(anchor.Href, expectedLink), $"Expected next link to be {expectedLink} but it was {anchor.Href}.");
var result = await _httpClient.GetAsync(anchor.Href);
Assert.True(IsSuccessStatusCode(result), $"{anchor.Href} is a broken link!");
}
}
}
private Uri GetListeningUri(ITestOutputHelper output)
{
// Wait until the app is accepting HTTP requests
@ -113,6 +171,11 @@ namespace Templates.Test.Helpers
}
}
private bool IsSuccessStatusCode(HttpResponseMessage response)
{
return response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.Redirect;
}
public Task AssertOk(string requestUrl)
=> AssertStatusCode(requestUrl, HttpStatusCode.OK);
@ -121,14 +184,14 @@ namespace Templates.Test.Helpers
internal Task<HttpResponseMessage> SendRequest(string path)
{
return _httpClient.GetAsync(new Uri(_listeningUri, path));
return _httpClient.GetAsync(new Uri(ListeningUri, path));
}
public async Task AssertStatusCode(string requestUrl, HttpStatusCode statusCode, string acceptContentType = null)
{
var request = new HttpRequestMessage(
HttpMethod.Get,
new Uri(_listeningUri, requestUrl));
new Uri(ListeningUri, requestUrl));
if (!string.IsNullOrEmpty(acceptContentType))
{
@ -136,7 +199,7 @@ namespace Templates.Test.Helpers
}
var response = await _httpClient.SendAsync(request);
Assert.Equal(statusCode, response.StatusCode);
Assert.True(statusCode == response.StatusCode, $"Expected {requestUrl} to have status '{statusCode}' but it was '{response.StatusCode}'.");
}
public void Dispose()
@ -153,7 +216,7 @@ namespace Templates.Test.Helpers
{
if (!Process.HasExited)
{
result += $"(Listening on {_listeningUri.OriginalString}) PID: {Process.Id}";
result += $"(Listening on {ListeningUri.OriginalString}) PID: {Process.Id}";
}
else
{
@ -164,4 +227,10 @@ namespace Templates.Test.Helpers
return result;
}
}
public class Page
{
public string Url { get; set; }
public IEnumerable<string> Links { get; set; }
}
}

View File

@ -0,0 +1,17 @@
// 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.
namespace Templates.Test.Helpers
{
public static class PageUrls
{
public const string HomeUrl = "/";
public const string PrivacyUrl = "/Privacy";
public const string PrivacyFullUrl = "/Home/Privacy";
public const string DocsUrl = "https://docs.microsoft.com/aspnet/core";
public const string LoginUrl = "/Identity/Account/Login";
public const string RegisterUrl = "/Identity/Account/Register";
public const string ForgotPassword = "/Identity/Account/ForgotPassword";
public const string ExternalArticle = "https://go.microsoft.com/fwlink/?LinkID=532715";
}
}

View File

@ -19,6 +19,8 @@ namespace Templates.Test.Helpers
[DebuggerDisplay("{ToString(),nq}")]
public class Project
{
private const string _urls = "http://127.0.0.1:0;https://127.0.0.1:0";
public const string DefaultFramework = "netcoreapp3.0";
public SemaphoreSlim DotNetNewLock { get; set; }
@ -30,6 +32,12 @@ namespace Templates.Test.Helpers
public string TemplateBuildDir => Path.Combine(TemplateOutputDir, "bin", "Debug", DefaultFramework);
public string TemplatePublishDir => Path.Combine(TemplateOutputDir, "bin", "Release", DefaultFramework, "publish");
private string TemplateServerDir => Path.Combine(TemplateOutputDir, $"{ProjectName}.Server");
private string TemplateClientDir => Path.Combine(TemplateOutputDir, $"{ProjectName}.Client");
public string TemplateClientDebugDir => Path.Combine(TemplateClientDir, "bin", "Debug", DefaultFramework);
public string TemplateClientReleaseDir => Path.Combine(TemplateClientDir, "bin", "Release", DefaultFramework, "publish");
public string TemplateServerReleaseDir => Path.Combine(TemplateServerDir, "bin", "Release", DefaultFramework, "publish");
public ITestOutputHelper Output { get; set; }
public IMessageSink DiagnosticsMessageSink { get; set; }
@ -127,11 +135,55 @@ namespace Templates.Test.Helpers
}
}
internal AspNetProcess StartBuiltServerAsync()
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_ENVIRONMENT"] = "Development"
};
var projectDll = Path.Combine(TemplateServerDir, $"{ProjectName}.Server.dll");
return new AspNetProcess(Output, TemplateServerDir, projectDll, environment, published: false);
}
internal AspNetProcess StartBuiltClientAsync(AspNetProcess serverProcess)
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_ENVIRONMENT"] = "Development"
};
var projectDll = Path.Combine(TemplateClientDebugDir, $"{ProjectName}.Client.dll {serverProcess.ListeningUri.Port}");
return new AspNetProcess(Output, TemplateOutputDir, projectDll, environment, hasListeningUri: false);
}
internal AspNetProcess StartPublishedServerAsync()
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_URLS"] = _urls,
};
var projectDll = $"{ProjectName}.Server.dll";
return new AspNetProcess(Output, TemplateServerReleaseDir, projectDll, environment);
}
internal AspNetProcess StartPublishedClientAsync()
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_URLS"] = _urls,
};
var projectDll = $"{ProjectName}.Client.dll";
return new AspNetProcess(Output, TemplateClientReleaseDir, projectDll, environment);
}
internal AspNetProcess StartBuiltProjectAsync()
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_URLS"] = $"http://127.0.0.1:0;https://127.0.0.1:0",
["ASPNETCORE_URLS"] = _urls,
["ASPNETCORE_ENVIRONMENT"] = "Development"
};
@ -143,7 +195,7 @@ namespace Templates.Test.Helpers
{
var environment = new Dictionary<string, string>
{
["ASPNETCORE_URLS"] = $"http://127.0.0.1:0;https://127.0.0.1:0",
["ASPNETCORE_URLS"] = _urls,
};
var projectDll = $"{ProjectName}.dll";

View File

@ -1,9 +1,11 @@
// 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.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Templates.Test.Helpers;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
@ -55,14 +57,39 @@ namespace Templates.Test
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
IEnumerable<string> menuLinks = new List<string> {
PageUrls.HomeUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyFullUrl
};
if(languageOverride == null)
{
menuLinks = menuLinks.Append(PageUrls.PrivacyFullUrl);
}
var footerLinks = new string[] { PageUrls.PrivacyFullUrl };
var pages = new List<Page>
{
new Page
{
Url = PageUrls.HomeUrl,
Links = menuLinks.Append(PageUrls.DocsUrl).Concat(footerLinks)
},
new Page
{
Url = PageUrls.PrivacyFullUrl,
Links = menuLinks.Concat(footerLinks)
}
};
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Home/Privacy");
await aspNetProcess.AssertPagesOk(pages);
}
using (var aspNetProcess = Project.StartPublishedProjectAsync())
@ -71,8 +98,7 @@ namespace Templates.Test
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Home/Privacy");
await aspNetProcess.AssertPagesOk(pages);
}
}
@ -110,18 +136,94 @@ namespace Templates.Test
Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult));
Project.AssertEmptyMigration("mvc");
var pages = new List<Page> {
new Page
{
Url = PageUrls.ForgotPassword,
Links = new string [] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.HomeUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.DocsUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.PrivacyFullUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.LoginUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
new Page
{
Url = PageUrls.RegisterUrl,
Links = new string [] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl
}
}
};
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Identity/Account/Login");
await aspNetProcess.AssertOk("/Home/Privacy");
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertPagesOk(pages);
}
using (var aspNetProcess = Project.StartPublishedProjectAsync())
{
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Identity/Account/Login");
await aspNetProcess.AssertOk("/Home/Privacy");
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
await aspNetProcess.AssertPagesOk(pages);
}
}

View File

@ -0,0 +1,43 @@
// 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.Threading.Tasks;
using Templates.Test.Helpers;
using Xunit;
using Xunit.Abstractions;
namespace Templates.Test
{
public class RazorClassLibraryTemplateTest
{
public RazorClassLibraryTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
{
ProjectFactory = projectFactory;
Output = output;
}
public Project Project { get; set; }
public ProjectFactoryFixture ProjectFactory { get; }
public ITestOutputHelper Output { get; }
[Fact]
public async Task RazorClassLibraryTemplateAsync()
{
Project = await ProjectFactory.GetOrCreateProject("razorclasslib", Output);
var createResult = await Project.RunDotNetNewAsync("razorclasslib");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
// The output from publish will go into bin/Release/netcoreapp3.0/publish and won't be affected by calling build
// later, while the opposite is not true.
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
}
}
}

View File

@ -1,6 +1,7 @@
// 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.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Templates.Test.Helpers;
@ -31,8 +32,6 @@ namespace Templates.Test
var createResult = await Project.RunDotNetNewAsync("razor");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("razor", Project, createResult));
AssertFileExists(Project.TemplateOutputDir, "Pages/Shared/_LoginPartial.cshtml", false);
var projectFileContents = ReadFile(Project.TemplateOutputDir, $"{Project.ProjectName}.csproj");
Assert.DoesNotContain(".db", projectFileContents);
Assert.DoesNotContain("Microsoft.EntityFrameworkCore.Tools", projectFileContents);
@ -50,14 +49,39 @@ namespace Templates.Test
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, createResult));
var pages = new List<Page>
{
new Page
{
Url = PageUrls.HomeUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.DocsUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.PrivacyUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl }
}
};
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Privacy");
await aspNetProcess.AssertPagesOk(pages);
}
using (var aspNetProcess = Project.StartPublishedProjectAsync())
@ -66,8 +90,7 @@ namespace Templates.Test
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Privacy");
await aspNetProcess.AssertPagesOk(pages);
}
}
@ -81,8 +104,6 @@ namespace Templates.Test
var createResult = await Project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
AssertFileExists(Project.TemplateOutputDir, "Pages/Shared/_LoginPartial.cshtml", true);
var projectFileContents = ReadFile(Project.TemplateOutputDir, $"{Project.ProjectName}.csproj");
if (!useLocalDB)
{
@ -103,39 +124,104 @@ namespace Templates.Test
Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult));
Project.AssertEmptyMigration("razorpages");
var pages = new List<Page> {
new Page
{
Url = PageUrls.ForgotPassword,
Links = new string [] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.HomeUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.DocsUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.PrivacyUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl
}
},
new Page
{
Url = PageUrls.LoginUrl,
Links = new string[] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
new Page
{
Url = PageUrls.RegisterUrl,
Links = new string [] {
PageUrls.HomeUrl,
PageUrls.RegisterUrl,
PageUrls.LoginUrl,
PageUrls.HomeUrl,
PageUrls.PrivacyUrl,
PageUrls.PrivacyUrl,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl
}
}
};
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Identity/Account/Login");
await aspNetProcess.AssertOk("/Privacy");
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertPagesOk(pages);
}
using (var aspNetProcess = Project.StartPublishedProjectAsync())
{
await aspNetProcess.AssertOk("/");
await aspNetProcess.AssertOk("/Identity/Account/Login");
await aspNetProcess.AssertOk("/Privacy");
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
await aspNetProcess.AssertPagesOk(pages);
}
}
private void AssertFileExists(string basePath, string path, bool shouldExist)
private string ReadFile(string basePath, string path)
{
var fullPath = Path.Combine(basePath, path);
var doesExist = File.Exists(fullPath);
if (shouldExist)
{
Assert.True(doesExist, "Expected file to exist, but it doesn't: " + path);
}
else
{
Assert.False(doesExist, "Expected file not to exist, but it does: " + path);
}
}
private string ReadFile(string basePath, string path)
{
AssertFileExists(basePath, path, shouldExist: true);
Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}");
return File.ReadAllText(Path.Combine(basePath, path));
}
}

View File

@ -22,12 +22,14 @@ namespace Templates.Test
public Project Project { get; set; }
[Fact]
public async Task WebApiTemplateAsync()
[Theory]
[InlineData(null)]
[InlineData("F#")]
public async Task WebApiTemplateAsync(string languageOverride)
{
Project = await FactoryFixture.GetOrCreateProject("webapi", Output);
Project = await FactoryFixture.GetOrCreateProject("webapi" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
var createResult = await Project.RunDotNetNewAsync("webapi");
var createResult = await Project.RunDotNetNewAsync("webapi", language: languageOverride);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();

View File

@ -0,0 +1,56 @@
// 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.Threading.Tasks;
using Templates.Test.Helpers;
using Xunit;
using Xunit.Abstractions;
namespace Templates.Test
{
public class WorkerTemplateTest
{
public WorkerTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
{
ProjectFactory = projectFactory;
Output = output;
}
public Project Project { get; set; }
public ProjectFactoryFixture ProjectFactory { get; }
public ITestOutputHelper Output { get; }
[Fact(Skip = "Microsoft.NET.Sdk.Worker isn't available yet")]
public async Task WorkerTemplateAsync()
{
Project = await ProjectFactory.GetOrCreateProject("worker", Output);
var createResult = await Project.RunDotNetNewAsync("worker");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
// The output from publish will go into bin/Release/netcoreapp3.0/publish and won't be affected by calling build
// later, while the opposite is not true.
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
using (var aspNetProcess = Project.StartBuiltProjectAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
}
using (var aspNetProcess = Project.StartPublishedProjectAsync())
{
Assert.False(
aspNetProcess.Process.HasExited,
ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
}
}
}
}

View File

@ -370,7 +370,7 @@
}
},
"web": {
"None": {
"CSharp": {
"Template": "web",
"Arguments": "new web",
"Files": [
@ -381,6 +381,18 @@
"Properties/launchSettings.json"
],
"AuthOption": "None"
},
"FSharp": {
"Template": "web",
"Arguments": "new web --language F#",
"Files": [
"appsettings.Development.json",
"appsettings.json",
"Program.fs",
"Startup.fs",
"Properties/launchSettings.json"
],
"AuthOption": "None"
}
},
"webapi": {
@ -435,6 +447,18 @@
"Properties/launchSettings.json"
],
"AuthOption": "Windows"
},
"FSharp": {
"Template": "webapi",
"Arguments": "new webapi --language F#",
"Files": [
"appsettings.Development.json",
"appsettings.json",
"Program.fs",
"Startup.fs",
"Controllers/ValuesController.fs",
"Properties/launchSettings.json"
]
}
},
"mvc": {
@ -864,6 +888,55 @@
]
}
},
"razorcomponents": {
"None": {
"Template": "razorcomponents",
"Arguments": "new razorcomponents",
"Files": [
"Pages/_Imports.razor",
"Pages/Counter.razor",
"Pages/FetchData.razor",
"Pages/_Host.cshtml",
"Pages/Index.razor",
"Shared/MainLayout.razor",
"Shared/NavMenu.razor",
"_Imports.razor",
"App.razor",
"Properties/launchSettings.json",
"Data/WeatherForecast.cs",
"Data/WeatherForecastService.cs",
"wwwroot/css/bootstrap/bootstrap.min.css",
"wwwroot/css/bootstrap/bootstrap.min.css.map",
"wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
"wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
"wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
"wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
"wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
"wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
"wwwroot/css/open-iconic/FONT-LICENSE",
"wwwroot/css/open-iconic/ICON-LICENSE",
"wwwroot/css/open-iconic/README.md",
"wwwroot/css/site.css",
"appsettings.Development.json",
"appsettings.json",
"Program.cs",
"Startup.cs"
]
}
},
"worker": {
"None": {
"Template": "worker",
"Arguments": "new worker",
"Files": [
"Properties/launchSettings.json",
"appsettings.Development.json",
"appsettings.json",
"Program.cs",
"Worker.cs"
]
}
},
"angular": {
"None": {
"Template": "angular",