From 02a19aff568ed5925296aa4d75b664de33abe6be Mon Sep 17 00:00:00 2001 From: Piotr Puszkiewicz Date: Fri, 14 Oct 2016 00:06:35 -0700 Subject: [PATCH] dotnet-new csproj templates (#4382) Make csproj templates first-class in dotnet-new. --- .../ProjectJsonConsoleTemplate/Program.cs | 9 + .../ProjectJsonConsoleTemplate/project.json | 19 + .../ProjectJsonWebTemplate/.bowerrc | 3 + .../ProjectJsonWebTemplate/.gitignore | 234 +++++++++ .../Controllers/AccountController.cs | 468 ++++++++++++++++++ .../Controllers/HomeController.cs | 35 ++ .../Controllers/ManageController.cs | 347 +++++++++++++ .../Data/ApplicationDbContext.cs | 26 + ...000000000_CreateIdentitySchema.Designer.cs | 212 ++++++++ .../00000000000000_CreateIdentitySchema.cs | 215 ++++++++ .../ApplicationDbContextModelSnapshot.cs | 211 ++++++++ .../ExternalLoginConfirmationViewModel.cs | 15 + .../ForgotPasswordViewModel.cs | 15 + .../AccountViewModels/LoginViewModel.cs | 22 + .../AccountViewModels/RegisterViewModel.cs | 27 + .../ResetPasswordViewModel.cs | 27 + .../AccountViewModels/SendCodeViewModel.cs | 19 + .../AccountViewModels/VerifyCodeViewModel.cs | 25 + .../Models/ApplicationUser.cs | 13 + .../AddPhoneNumberViewModel.cs | 16 + .../ChangePasswordViewModel.cs | 27 + .../ConfigureTwoFactorViewModel.cs | 15 + .../ManageViewModels/FactorViewModel.cs | 12 + .../Models/ManageViewModels/IndexViewModel.cs | 21 + .../ManageViewModels/ManageLoginsViewModel.cs | 16 + .../ManageViewModels/RemoveLoginViewModel.cs | 14 + .../ManageViewModels/SetPasswordViewModel.cs | 22 + .../VerifyPhoneNumberViewModel.cs | 19 + .../ProjectJsonWebTemplate/Program.cs | 24 + .../ProjectJsonWebTemplate/README.md | 39 ++ .../Services/IEmailSender.cs | 12 + .../Services/ISmsSender.cs | 12 + .../Services/MessageServices.cs | 25 + .../ProjectJsonWebTemplate/Startup.cs | 88 ++++ .../Views/Account/ConfirmEmail.cshtml | 10 + .../Account/ExternalLoginConfirmation.cshtml | 35 ++ .../Views/Account/ExternalLoginFailure.cshtml | 8 + .../Views/Account/ForgotPassword.cshtml | 31 ++ .../Account/ForgotPasswordConfirmation.cshtml | 8 + .../Views/Account/Lockout.cshtml | 8 + .../Views/Account/Login.cshtml | 92 ++++ .../Views/Account/Register.cshtml | 42 ++ .../Views/Account/ResetPassword.cshtml | 43 ++ .../Account/ResetPasswordConfirmation.cshtml | 8 + .../Views/Account/SendCode.cshtml | 21 + .../Views/Account/VerifyCode.cshtml | 38 ++ .../Views/Home/About.cshtml | 7 + .../Views/Home/Contact.cshtml | 17 + .../Views/Home/Index.cshtml | 109 ++++ .../Views/Manage/AddPhoneNumber.cshtml | 27 + .../Views/Manage/ChangePassword.cshtml | 42 ++ .../Views/Manage/Index.cshtml | 71 +++ .../Views/Manage/ManageLogins.cshtml | 54 ++ .../Views/Manage/SetPassword.cshtml | 38 ++ .../Views/Manage/VerifyPhoneNumber.cshtml | 30 ++ .../Views/Shared/Error.cshtml | 14 + .../Views/Shared/_Layout.cshtml | 68 +++ .../Views/Shared/_LoginPartial.cshtml | 26 + .../Shared/_ValidationScriptsPartial.cshtml | 14 + .../Views/_ViewImports.cshtml | 6 + .../Views/_ViewStart.cshtml | 3 + .../ProjectJsonWebTemplate/appsettings.json | 13 + .../ProjectJsonWebTemplate/bower.json | 10 + .../ProjectJsonWebTemplate/gulpfile.js | 45 ++ .../ProjectJsonWebTemplate/package.json | 12 + .../project.json.template | 112 +++++ .../ProjectJsonWebTemplate/web.config | 14 + .../wwwroot/css/site.css | 44 ++ .../wwwroot/css/site.min.css | 1 + .../wwwroot/favicon.ico | Bin 0 -> 32038 bytes .../wwwroot/images/banner1.svg | 1 + .../wwwroot/images/banner2.svg | 1 + .../wwwroot/images/banner3.svg | 1 + .../wwwroot/images/banner4.svg | 1 + .../ProjectJsonWebTemplate/wwwroot/js/site.js | 1 + .../wwwroot/js/site.min.js | 0 build/Microsoft.DotNet.Cli.tasks | 1 + .../Microsoft.DotNet.Cli.LzmaArchive.targets | 4 +- build/test/RunTest.proj | 3 +- .../dotnet-cli-build/DotNetRestore3.cs | 54 ++ .../NuGetCachePrimer.cs | 4 +- src/dotnet/commands/dotnet-migrate/Program.cs | 2 +- .../TemporaryDotnetNewTemplateProject.cs | 2 +- .../$projectName$.csproj | 9 +- .../dotnet-new/CSharp_Console/Program.cs | 2 +- .../CSharp_Console/project.json.template | 19 - .../CSharp_Lib/$projectName$.csproj | 24 + .../commands/dotnet-new/CSharp_Lib/Library.cs | 3 - .../CSharp_Lib/project.json.template | 14 - .../dotnet-new/CSharp_MSBuild/Program.cs | 12 - src/dotnet/commands/dotnet-new/Program.cs | 42 +- test/EndToEnd/EndToEndTest.cs | 266 ---------- test/EndToEnd/GivenDotNetUsesMSBuild.cs | 8 +- .../GivenANuGetCachePrimer.cs | 6 +- test/Performance/HelloWorld.cs | 16 +- .../GivenThatIWantToMigrateTestApps.cs | 98 ++-- .../GivenThatIWantANewCSApp.cs | 66 +-- .../GivenThatIWantANewCSLIbrary.cs | 25 +- test/dotnet-new.Tests/MSBuild.exe | 1 + test/dotnet-new.Tests/MSBuild.exe.config | 1 + test/dotnet-new.Tests/project.json | 2 +- ...atTheUserIsRunningDotNetForTheFirstTime.cs | 1 + 102 files changed, 3670 insertions(+), 445 deletions(-) create mode 100644 TestAssets/TestProjects/ProjectJsonConsoleTemplate/Program.cs create mode 100644 TestAssets/TestProjects/ProjectJsonConsoleTemplate/project.json create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/.bowerrc create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/.gitignore create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/AccountController.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/HomeController.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/ManageController.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Data/ApplicationDbContext.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/ApplicationDbContextModelSnapshot.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ForgotPasswordViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/LoginViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/RegisterViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ResetPasswordViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/SendCodeViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/VerifyCodeViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ApplicationUser.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/AddPhoneNumberViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ChangePasswordViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/FactorViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/IndexViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ManageLoginsViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/RemoveLoginViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/SetPasswordViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Program.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/README.md create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Services/IEmailSender.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Services/ISmsSender.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Services/MessageServices.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Startup.cs create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ConfirmEmail.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginConfirmation.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginFailure.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPassword.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPasswordConfirmation.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Lockout.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Login.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Register.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPassword.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPasswordConfirmation.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/SendCode.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/VerifyCode.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/About.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Contact.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Index.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/AddPhoneNumber.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ChangePassword.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/Index.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ManageLogins.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/SetPassword.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/VerifyPhoneNumber.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/Error.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_Layout.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_LoginPartial.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_ValidationScriptsPartial.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewImports.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewStart.cshtml create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/appsettings.json create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/bower.json create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/gulpfile.js create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/package.json create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/project.json.template create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/web.config create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.css create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.min.css create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/favicon.ico create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner1.svg create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner2.svg create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner3.svg create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner4.svg create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.js create mode 100644 TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.min.js create mode 100644 build_projects/dotnet-cli-build/DotNetRestore3.cs rename src/dotnet/commands/dotnet-new/{CSharp_MSBuild => CSharp_Console}/$projectName$.csproj (77%) mode change 100644 => 100755 delete mode 100644 src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template create mode 100755 src/dotnet/commands/dotnet-new/CSharp_Lib/$projectName$.csproj delete mode 100644 src/dotnet/commands/dotnet-new/CSharp_Lib/project.json.template delete mode 100644 src/dotnet/commands/dotnet-new/CSharp_MSBuild/Program.cs delete mode 100644 test/EndToEnd/EndToEndTest.cs create mode 100644 test/dotnet-new.Tests/MSBuild.exe create mode 100644 test/dotnet-new.Tests/MSBuild.exe.config diff --git a/TestAssets/TestProjects/ProjectJsonConsoleTemplate/Program.cs b/TestAssets/TestProjects/ProjectJsonConsoleTemplate/Program.cs new file mode 100644 index 000000000..5f9be2467 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonConsoleTemplate/Program.cs @@ -0,0 +1,9 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } +} \ No newline at end of file diff --git a/TestAssets/TestProjects/ProjectJsonConsoleTemplate/project.json b/TestAssets/TestProjects/ProjectJsonConsoleTemplate/project.json new file mode 100644 index 000000000..10e7042d6 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonConsoleTemplate/project.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "debugType": "portable", + "emitEntryPoint": true + }, + "dependencies": {}, + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.1" + } + }, + "imports": "dnxcore50" + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/.bowerrc b/TestAssets/TestProjects/ProjectJsonWebTemplate/.bowerrc new file mode 100644 index 000000000..6406626ab --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "wwwroot/lib" +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/.gitignore b/TestAssets/TestProjects/ProjectJsonWebTemplate/.gitignore new file mode 100644 index 000000000..0ca27f04e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/.gitignore @@ -0,0 +1,234 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/AccountController.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/AccountController.cs new file mode 100644 index 000000000..035638af9 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/AccountController.cs @@ -0,0 +1,468 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Logging; +using WebApplication.Models; +using WebApplication.Models.AccountViewModels; +using WebApplication.Services; + +namespace WebApplication.Controllers +{ + [Authorize] + public class AccountController : Controller + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly ISmsSender _smsSender; + private readonly ILogger _logger; + + public AccountController( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender, + ISmsSender smsSender, + ILoggerFactory loggerFactory) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _smsSender = smsSender; + _logger = loggerFactory.CreateLogger(); + } + + // + // GET: /Account/Login + [HttpGet] + [AllowAnonymous] + public IActionResult Login(string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + // + // POST: /Account/Login + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Login(LoginViewModel model, string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); + if (result.Succeeded) + { + _logger.LogInformation(1, "User logged in."); + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning(2, "User account locked out."); + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return View(model); + } + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/Register + [HttpGet] + [AllowAnonymous] + public IActionResult Register(string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + // + // POST: /Account/Register + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Register(RegisterViewModel model, string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + if (ModelState.IsValid) + { + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await _userManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); + //await _emailSender.SendEmailAsync(model.Email, "Confirm your account", + // $"Please confirm your account by clicking this link: link"); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(3, "User created a new account with password."); + return RedirectToLocal(returnUrl); + } + AddErrors(result); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // POST: /Account/LogOff + [HttpPost] + [ValidateAntiForgeryToken] + public async Task LogOff() + { + await _signInManager.SignOutAsync(); + _logger.LogInformation(4, "User logged out."); + return RedirectToAction(nameof(HomeController.Index), "Home"); + } + + // + // POST: /Account/ExternalLogin + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public IActionResult ExternalLogin(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return Challenge(properties, provider); + } + + // + // GET: /Account/ExternalLoginCallback + [HttpGet] + [AllowAnonymous] + public async Task ExternalLoginCallback(string returnUrl = null, string remoteError = null) + { + if (remoteError != null) + { + ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}"); + return View(nameof(Login)); + } + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return RedirectToAction(nameof(Login)); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); + if (result.Succeeded) + { + _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider); + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl }); + } + if (result.IsLockedOut) + { + return View("Lockout"); + } + else + { + // If the user does not have an account, then ask the user to create an account. + ViewData["ReturnUrl"] = returnUrl; + ViewData["LoginProvider"] = info.LoginProvider; + var email = info.Principal.FindFirstValue(ClaimTypes.Email); + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); + } + } + + // + // POST: /Account/ExternalLoginConfirmation + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) + { + if (ModelState.IsValid) + { + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return View("ExternalLoginFailure"); + } + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await _userManager.CreateAsync(user); + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider); + return RedirectToLocal(returnUrl); + } + } + AddErrors(result); + } + + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } + + // GET: /Account/ConfirmEmail + [HttpGet] + [AllowAnonymous] + public async Task ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } + var result = await _userManager.ConfirmEmailAsync(user, code); + return View(result.Succeeded ? "ConfirmEmail" : "Error"); + } + + // + // GET: /Account/ForgotPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPassword() + { + return View(); + } + + // + // POST: /Account/ForgotPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ForgotPassword(ForgotPasswordViewModel model) + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByNameAsync(model.Email); + if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return View("ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + //var code = await _userManager.GeneratePasswordResetTokenAsync(user); + //var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); + //await _emailSender.SendEmailAsync(model.Email, "Reset Password", + // $"Please reset your password by clicking here: link"); + //return View("ForgotPasswordConfirmation"); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/ForgotPasswordConfirmation + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/ResetPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPassword(string code = null) + { + return code == null ? View("Error") : View(); + } + + // + // POST: /Account/ResetPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ResetPassword(ResetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await _userManager.FindByNameAsync(model.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account"); + } + var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password); + if (result.Succeeded) + { + return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account"); + } + AddErrors(result); + return View(); + } + + // + // GET: /Account/ResetPasswordConfirmation + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/SendCode + [HttpGet] + [AllowAnonymous] + public async Task SendCode(string returnUrl = null, bool rememberMe = false) + { + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); + var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); + return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/SendCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task SendCode(SendCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + + // Generate the token and send it + var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); + if (string.IsNullOrWhiteSpace(code)) + { + return View("Error"); + } + + var message = "Your security code is: " + code; + if (model.SelectedProvider == "Email") + { + await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message); + } + else if (model.SelectedProvider == "Phone") + { + await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message); + } + + return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + } + + // + // GET: /Account/VerifyCode + [HttpGet] + [AllowAnonymous] + public async Task VerifyCode(string provider, bool rememberMe, string returnUrl = null) + { + // Require that the user has already logged in via username/password or external login + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/VerifyCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task VerifyCode(VerifyCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + // The following code protects for brute force attacks against the two factor codes. + // If a user enters incorrect codes for a specified amount of time then the user account + // will be locked out for a specified amount of time. + var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); + if (result.Succeeded) + { + return RedirectToLocal(model.ReturnUrl); + } + if (result.IsLockedOut) + { + _logger.LogWarning(7, "User account locked out."); + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid code."); + return View(model); + } + } + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + private Task GetCurrentUserAsync() + { + return _userManager.GetUserAsync(HttpContext.User); + } + + private IActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + else + { + return RedirectToAction(nameof(HomeController.Index), "Home"); + } + } + + #endregion + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/HomeController.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/HomeController.cs new file mode 100644 index 000000000..4b21d0cce --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/HomeController.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace WebApplication.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index() + { + return View(); + } + + public IActionResult About() + { + ViewData["Message"] = "Your application description page."; + + return View(); + } + + public IActionResult Contact() + { + ViewData["Message"] = "Your contact page."; + + return View(); + } + + public IActionResult Error() + { + return View(); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/ManageController.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/ManageController.cs new file mode 100644 index 000000000..8d04fe58b --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Controllers/ManageController.cs @@ -0,0 +1,347 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using WebApplication.Models; +using WebApplication.Models.ManageViewModels; +using WebApplication.Services; + +namespace WebApplication.Controllers +{ + [Authorize] + public class ManageController : Controller + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly ISmsSender _smsSender; + private readonly ILogger _logger; + + public ManageController( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender, + ISmsSender smsSender, + ILoggerFactory loggerFactory) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _smsSender = smsSender; + _logger = loggerFactory.CreateLogger(); + } + + // + // GET: /Manage/Index + [HttpGet] + public async Task Index(ManageMessageId? message = null) + { + ViewData["StatusMessage"] = + message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." + : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." + : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." + : message == ManageMessageId.Error ? "An error has occurred." + : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." + : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." + : ""; + + var user = await GetCurrentUserAsync(); + var model = new IndexViewModel + { + HasPassword = await _userManager.HasPasswordAsync(user), + PhoneNumber = await _userManager.GetPhoneNumberAsync(user), + TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user), + Logins = await _userManager.GetLoginsAsync(user), + BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user) + }; + return View(model); + } + + // + // POST: /Manage/RemoveLogin + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemoveLogin(RemoveLoginViewModel account) + { + ManageMessageId? message = ManageMessageId.Error; + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + message = ManageMessageId.RemoveLoginSuccess; + } + } + return RedirectToAction(nameof(ManageLogins), new { Message = message }); + } + + // + // GET: /Manage/AddPhoneNumber + public IActionResult AddPhoneNumber() + { + return View(); + } + + // + // POST: /Manage/AddPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AddPhoneNumber(AddPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + // Generate the token and send it + var user = await GetCurrentUserAsync(); + var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); + await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code); + return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber }); + } + + // + // POST: /Manage/EnableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EnableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await _userManager.SetTwoFactorEnabledAsync(user, true); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(1, "User enabled two-factor authentication."); + } + return RedirectToAction(nameof(Index), "Manage"); + } + + // + // POST: /Manage/DisableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DisableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(2, "User disabled two-factor authentication."); + } + return RedirectToAction(nameof(Index), "Manage"); + } + + // + // GET: /Manage/VerifyPhoneNumber + [HttpGet] + public async Task VerifyPhoneNumber(string phoneNumber) + { + var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); + // Send an SMS to verify the phone number + return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); + } + + // + // POST: /Manage/VerifyPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess }); + } + } + // If we got this far, something failed, redisplay the form + ModelState.AddModelError(string.Empty, "Failed to verify phone number"); + return View(model); + } + + // + // POST: /Manage/RemovePhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemovePhoneNumber() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.SetPhoneNumberAsync(user, null); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); + } + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/ChangePassword + [HttpGet] + public IActionResult ChangePassword() + { + return View(); + } + + // + // POST: /Manage/ChangePassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(3, "User changed their password successfully."); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/SetPassword + [HttpGet] + public IActionResult SetPassword() + { + return View(); + } + + // + // POST: /Manage/SetPassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SetPassword(SetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.AddPasswordAsync(user, model.NewPassword); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + //GET: /Manage/ManageLogins + [HttpGet] + public async Task ManageLogins(ManageMessageId? message = null) + { + ViewData["StatusMessage"] = + message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." + : message == ManageMessageId.AddLoginSuccess ? "The external login was added." + : message == ManageMessageId.Error ? "An error has occurred." + : ""; + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var userLogins = await _userManager.GetLoginsAsync(user); + var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList(); + ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1; + return View(new ManageLoginsViewModel + { + CurrentLogins = userLogins, + OtherLogins = otherLogins + }); + } + + // + // POST: /Manage/LinkLogin + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult LinkLogin(string provider) + { + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + return Challenge(properties, provider); + } + + // + // GET: /Manage/LinkLoginCallback + [HttpGet] + public async Task LinkLoginCallback() + { + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); + if (info == null) + { + return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error }); + } + var result = await _userManager.AddLoginAsync(user, info); + var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; + return RedirectToAction(nameof(ManageLogins), new { Message = message }); + } + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + public enum ManageMessageId + { + AddPhoneSuccess, + AddLoginSuccess, + ChangePasswordSuccess, + SetTwoFactorSuccess, + SetPasswordSuccess, + RemoveLoginSuccess, + RemovePhoneSuccess, + Error + } + + private Task GetCurrentUserAsync() + { + return _userManager.GetUserAsync(HttpContext.User); + } + + #endregion + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/ApplicationDbContext.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/ApplicationDbContext.cs new file mode 100644 index 000000000..336e6d466 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/ApplicationDbContext.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using WebApplication.Models; + +namespace WebApplication.Data +{ + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + // Customize the ASP.NET Identity model and override the defaults if needed. + // For example, you can rename the ASP.NET Identity table names and more. + // Add your customizations after calling base.OnModelCreating(builder); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 000000000..bb12d2bbb --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,212 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using WebApplication.Data; + +namespace WebApplication.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.0.0-rc2-20901"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => + { + b.Property("Id"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("WebApplication.Models.ApplicationUser", b => + { + b.Property("Id"); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasAnnotation("MaxLength", 256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedUserName") + .HasAnnotation("MaxLength", 256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 000000000..e6f038f8e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace WebApplication.Data.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(nullable: false), + ConcurrencyStamp = table.Column(nullable: true), + Name = table.Column(nullable: true), + NormalizedName = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(nullable: false), + Name = table.Column(nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false), + ConcurrencyStamp = table.Column(nullable: true), + Email = table.Column(nullable: true), + EmailConfirmed = table.Column(nullable: false), + LockoutEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + NormalizedEmail = table.Column(nullable: true), + NormalizedUserName = table.Column(nullable: true), + PasswordHash = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + SecurityStamp = table.Column(nullable: true), + TwoFactorEnabled = table.Column(nullable: false), + UserName = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(nullable: false), + ProviderKey = table.Column(nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_UserId", + table: "AspNetUserRoles", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 000000000..cb459319d --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,211 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using WebApplication.Data; + +namespace WebApplication.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.0.0-rc2-20901"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => + { + b.Property("Id"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("WebApplication.Models.ApplicationUser", b => + { + b.Property("Id"); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasAnnotation("MaxLength", 256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedUserName") + .HasAnnotation("MaxLength", 256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("WebApplication.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs new file mode 100644 index 000000000..a60894ce8 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class ExternalLoginConfirmationViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ForgotPasswordViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ForgotPasswordViewModel.cs new file mode 100644 index 000000000..70fab0c9f --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ForgotPasswordViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class ForgotPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/LoginViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/LoginViewModel.cs new file mode 100644 index 000000000..7dc974b6b --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/LoginViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class LoginViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/RegisterViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/RegisterViewModel.cs new file mode 100644 index 000000000..bc86f2aed --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/RegisterViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class RegisterViewModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ResetPasswordViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ResetPasswordViewModel.cs new file mode 100644 index 000000000..43198b7e5 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/ResetPasswordViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class ResetPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/SendCodeViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/SendCodeViewModel.cs new file mode 100644 index 000000000..b8ed8f1d4 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/SendCodeViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace WebApplication.Models.AccountViewModels +{ + public class SendCodeViewModel + { + public string SelectedProvider { get; set; } + + public ICollection Providers { get; set; } + + public string ReturnUrl { get; set; } + + public bool RememberMe { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/VerifyCodeViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/VerifyCodeViewModel.cs new file mode 100644 index 000000000..394db8c59 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/AccountViewModels/VerifyCodeViewModel.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.AccountViewModels +{ + public class VerifyCodeViewModel + { + [Required] + public string Provider { get; set; } + + [Required] + public string Code { get; set; } + + public string ReturnUrl { get; set; } + + [Display(Name = "Remember this browser?")] + public bool RememberBrowser { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ApplicationUser.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ApplicationUser.cs new file mode 100644 index 000000000..4642ef2c3 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ApplicationUser.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; + +namespace WebApplication.Models +{ + // Add profile data for application users by adding properties to the ApplicationUser class + public class ApplicationUser : IdentityUser + { + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/AddPhoneNumberViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/AddPhoneNumberViewModel.cs new file mode 100644 index 000000000..d2baaf7e0 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/AddPhoneNumberViewModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class AddPhoneNumberViewModel + { + [Required] + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ChangePasswordViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ChangePasswordViewModel.cs new file mode 100644 index 000000000..421b91a03 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ChangePasswordViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class ChangePasswordViewModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs new file mode 100644 index 000000000..beb1fd1a4 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace WebApplication.Models.ManageViewModels +{ + public class ConfigureTwoFactorViewModel + { + public string SelectedProvider { get; set; } + + public ICollection Providers { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/FactorViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/FactorViewModel.cs new file mode 100644 index 000000000..b2d4f9e2f --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/FactorViewModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class FactorViewModel + { + public string Purpose { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/IndexViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/IndexViewModel.cs new file mode 100644 index 000000000..e0b69f2df --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/IndexViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; + +namespace WebApplication.Models.ManageViewModels +{ + public class IndexViewModel + { + public bool HasPassword { get; set; } + + public IList Logins { get; set; } + + public string PhoneNumber { get; set; } + + public bool TwoFactor { get; set; } + + public bool BrowserRemembered { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ManageLoginsViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ManageLoginsViewModel.cs new file mode 100644 index 000000000..fc03a0c27 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/ManageLoginsViewModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Authentication; +using Microsoft.AspNetCore.Identity; + +namespace WebApplication.Models.ManageViewModels +{ + public class ManageLoginsViewModel + { + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/RemoveLoginViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/RemoveLoginViewModel.cs new file mode 100644 index 000000000..394df34a0 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/RemoveLoginViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class RemoveLoginViewModel + { + public string LoginProvider { get; set; } + public string ProviderKey { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/SetPasswordViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/SetPasswordViewModel.cs new file mode 100644 index 000000000..76c1b4bb4 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/SetPasswordViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class SetPasswordViewModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs new file mode 100644 index 000000000..3c8c08c87 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Models.ManageViewModels +{ + public class VerifyPhoneNumberViewModel + { + [Required] + public string Code { get; set; } + + [Required] + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Program.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Program.cs new file mode 100644 index 000000000..74e9753fb --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; + +namespace WebApplication +{ + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/README.md b/TestAssets/TestProjects/ProjectJsonWebTemplate/README.md new file mode 100644 index 000000000..d8ba0b382 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/README.md @@ -0,0 +1,39 @@ +# Welcome to ASP.NET Core + +We've made some big updates in this release, so it’s **important** that you spend a few minutes to learn what’s new. + +You've created a new ASP.NET Core project. [Learn what's new](https://go.microsoft.com/fwlink/?LinkId=518016) + +## This application consists of: + +* Sample pages using ASP.NET Core MVC +* [Gulp](https://go.microsoft.com/fwlink/?LinkId=518007) and [Bower](https://go.microsoft.com/fwlink/?LinkId=518004) for managing client-side libraries +* Theming using [Bootstrap](https://go.microsoft.com/fwlink/?LinkID=398939) + +## How to + +* [Add a Controller and View](https://go.microsoft.com/fwlink/?LinkID=398600) +* [Add an appsetting in config and access it in app.](https://go.microsoft.com/fwlink/?LinkID=699562) +* [Manage User Secrets using Secret Manager.](https://go.microsoft.com/fwlink/?LinkId=699315) +* [Use logging to log a message.](https://go.microsoft.com/fwlink/?LinkId=699316) +* [Add packages using NuGet.](https://go.microsoft.com/fwlink/?LinkId=699317) +* [Add client packages using Bower.](https://go.microsoft.com/fwlink/?LinkId=699318) +* [Target development, staging or production environment.](https://go.microsoft.com/fwlink/?LinkId=699319) + +## Overview + +* [Conceptual overview of what is ASP.NET Core](https://go.microsoft.com/fwlink/?LinkId=518008) +* [Fundamentals of ASP.NET Core such as Startup and middleware.](https://go.microsoft.com/fwlink/?LinkId=699320) +* [Working with Data](https://go.microsoft.com/fwlink/?LinkId=398602) +* [Security](https://go.microsoft.com/fwlink/?LinkId=398603) +* [Client side development](https://go.microsoft.com/fwlink/?LinkID=699321) +* [Develop on different platforms](https://go.microsoft.com/fwlink/?LinkID=699322) +* [Read more on the documentation site](https://go.microsoft.com/fwlink/?LinkID=699323) + +## Run & Deploy + +* [Run your app](https://go.microsoft.com/fwlink/?LinkID=517851) +* [Run tools such as EF migrations and more](https://go.microsoft.com/fwlink/?LinkID=517853) +* [Publish to Microsoft Azure Web Apps](https://go.microsoft.com/fwlink/?LinkID=398609) + +We would love to hear your [feedback](https://go.microsoft.com/fwlink/?LinkId=518015) diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/IEmailSender.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/IEmailSender.cs new file mode 100644 index 000000000..08fb35bad --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/IEmailSender.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Services +{ + public interface IEmailSender + { + Task SendEmailAsync(string email, string subject, string message); + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/ISmsSender.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/ISmsSender.cs new file mode 100644 index 000000000..8e57a2343 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/ISmsSender.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Services +{ + public interface ISmsSender + { + Task SendSmsAsync(string number, string message); + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/MessageServices.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/MessageServices.cs new file mode 100644 index 000000000..de54bfc79 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Services/MessageServices.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication.Services +{ + // This class is used by the application to send Email and SMS + // when you turn on two-factor authentication in ASP.NET Identity. + // For more details see this link https://go.microsoft.com/fwlink/?LinkID=532713 + public class AuthMessageSender : IEmailSender, ISmsSender + { + public Task SendEmailAsync(string email, string subject, string message) + { + // Plug in your email service here to send an email. + return Task.FromResult(0); + } + + public Task SendSmsAsync(string number, string message) + { + // Plug in your SMS service here to send a text message. + return Task.FromResult(0); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Startup.cs b/TestAssets/TestProjects/ProjectJsonWebTemplate/Startup.cs new file mode 100644 index 000000000..2fa7ae51b --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Startup.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using WebApplication.Data; +using WebApplication.Models; +using WebApplication.Services; + +namespace WebApplication +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + + if (env.IsDevelopment()) + { + // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709 + builder.AddUserSecrets(); + } + + builder.AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add framework services. + services.AddDbContext(options => + options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); + + services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + services.AddMvc(); + + // Add application services. + services.AddTransient(); + services.AddTransient(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseDatabaseErrorPage(); + app.UseBrowserLink(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseIdentity(); + + // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715 + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ConfirmEmail.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ConfirmEmail.cshtml new file mode 100644 index 000000000..8e8088d44 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ConfirmEmail.cshtml @@ -0,0 +1,10 @@ +@{ + ViewData["Title"] = "Confirm Email"; +} + +

@ViewData["Title"].

+
+

+ Thank you for confirming your email. Please Click here to Log in. +

+
diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginConfirmation.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginConfirmation.cshtml new file mode 100644 index 000000000..eb3612b55 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginConfirmation.cshtml @@ -0,0 +1,35 @@ +@model ExternalLoginConfirmationViewModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"].

+

Associate your @ViewData["LoginProvider"] account.

+ +
+

Association Form

+
+
+ +

+ You've successfully authenticated with @ViewData["LoginProvider"]. + Please enter an email address for this site below and click the Register button to finish + logging in. +

+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginFailure.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginFailure.cshtml new file mode 100644 index 000000000..2509746be --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ExternalLoginFailure.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Login Failure"; +} + +
+

@ViewData["Title"].

+

Unsuccessful login with service.

+
diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPassword.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPassword.cshtml new file mode 100644 index 000000000..9d748023f --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPassword.cshtml @@ -0,0 +1,31 @@ +@model ForgotPasswordViewModel +@{ + ViewData["Title"] = "Forgot your password?"; +} + +

@ViewData["Title"]

+

+ For more information on how to enable reset password please see this article. +

+ +@*
+

Enter your email.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
*@ + +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPasswordConfirmation.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 000000000..4877fc83a --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Forgot Password Confirmation"; +} + +

@ViewData["Title"].

+

+ Please check your email to reset your password. +

diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Lockout.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Lockout.cshtml new file mode 100644 index 000000000..34ac56ff3 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Lockout.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Locked out"; +} + +
+

Locked out.

+

This account has been locked out, please try again later.

+
diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Login.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Login.cshtml new file mode 100644 index 000000000..95430a12e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Login.cshtml @@ -0,0 +1,92 @@ +@using System.Collections.Generic +@using Microsoft.AspNetCore.Http +@using Microsoft.AspNetCore.Http.Authentication +@model LoginViewModel +@inject SignInManager SignInManager + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"].

+
+
+
+
+

Use a local account to log in.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ Register as a new user? +

+

+ Forgot your password? +

+
+
+
+
+
+

Use another service to log in.

+
+ @{ + var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList(); + if (loginProviders.Count == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in loginProviders) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Register.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Register.cshtml new file mode 100644 index 000000000..2090f900e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/Register.cshtml @@ -0,0 +1,42 @@ +@model RegisterViewModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"].

+ +
+

Create a new account.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPassword.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPassword.cshtml new file mode 100644 index 000000000..dd716d735 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPassword.cshtml @@ -0,0 +1,43 @@ +@model ResetPasswordViewModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"].

+ +
+

Reset your password.

+
+
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPasswordConfirmation.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 000000000..6321d858e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Reset password confirmation"; +} + +

@ViewData["Title"].

+

+ Your password has been reset. Please Click here to log in. +

diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/SendCode.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/SendCode.cshtml new file mode 100644 index 000000000..e85ca3c2b --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/SendCode.cshtml @@ -0,0 +1,21 @@ +@model SendCodeViewModel +@{ + ViewData["Title"] = "Send Verification Code"; +} + +

@ViewData["Title"].

+ +
+ +
+
+ Select Two-Factor Authentication Provider: + + +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/VerifyCode.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/VerifyCode.cshtml new file mode 100644 index 000000000..60afb361d --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Account/VerifyCode.cshtml @@ -0,0 +1,38 @@ +@model VerifyCodeViewModel +@{ + ViewData["Title"] = "Verify"; +} + +

@ViewData["Title"].

+ +
+
+ + +

@ViewData["Status"]

+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/About.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/About.cshtml new file mode 100644 index 000000000..b653a26f1 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/About.cshtml @@ -0,0 +1,7 @@ +@{ + ViewData["Title"] = "About"; +} +

@ViewData["Title"].

+

@ViewData["Message"]

+ +

Use this area to provide additional information.

diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Contact.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Contact.cshtml new file mode 100644 index 000000000..f953aa63d --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Contact.cshtml @@ -0,0 +1,17 @@ +@{ + ViewData["Title"] = "Contact"; +} +

@ViewData["Title"].

+

@ViewData["Message"]

+ +
+ One Microsoft Way
+ Redmond, WA 98052-6399
+ P: + 425.555.0100 +
+ +
+ Support: Support@example.com
+ Marketing: Marketing@example.com +
diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Index.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Index.cshtml new file mode 100644 index 000000000..957b8c1da --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Home/Index.cshtml @@ -0,0 +1,109 @@ +@{ + ViewData["Title"] = "Home Page"; +} + + + + diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/AddPhoneNumber.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/AddPhoneNumber.cshtml new file mode 100644 index 000000000..2feb93b22 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/AddPhoneNumber.cshtml @@ -0,0 +1,27 @@ +@model AddPhoneNumberViewModel +@{ + ViewData["Title"] = "Add Phone Number"; +} + +

@ViewData["Title"].

+
+

Add a phone number.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ChangePassword.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ChangePassword.cshtml new file mode 100644 index 000000000..41c7960c8 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ChangePassword.cshtml @@ -0,0 +1,42 @@ +@model ChangePasswordViewModel +@{ + ViewData["Title"] = "Change Password"; +} + +

@ViewData["Title"].

+ +
+

Change Password Form

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/Index.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/Index.cshtml new file mode 100644 index 000000000..8419b2429 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/Index.cshtml @@ -0,0 +1,71 @@ +@model IndexViewModel +@{ + ViewData["Title"] = "Manage your account"; +} + +

@ViewData["Title"].

+

@ViewData["StatusMessage"]

+ +
+

Change your account settings

+
+
+
Password:
+
+ @if (Model.HasPassword) + { + Change + } + else + { + Create + } +
+
External Logins:
+
+ + @Model.Logins.Count Manage +
+
Phone Number:
+
+

+ Phone Numbers can used as a second factor of verification in two-factor authentication. + See this article + for details on setting up this ASP.NET application to support two-factor authentication using SMS. +

+ @*@(Model.PhoneNumber ?? "None") + @if (Model.PhoneNumber != null) + { +
+ Change +
+ [] +
+ } + else + { + Add + }*@ +
+ +
Two-Factor Authentication:
+
+

+ There are no two-factor authentication providers configured. See this article + for setting up this application to support two-factor authentication. +

+ @*@if (Model.TwoFactor) + { +
+ Enabled +
+ } + else + { +
+ Disabled +
+ }*@ +
+
+
diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ManageLogins.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ManageLogins.cshtml new file mode 100644 index 000000000..35e12da68 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/ManageLogins.cshtml @@ -0,0 +1,54 @@ +@model ManageLoginsViewModel +@using Microsoft.AspNetCore.Http.Authentication +@{ + ViewData["Title"] = "Manage your external logins"; +} + +

@ViewData["Title"].

+ +

@ViewData["StatusMessage"]

+@if (Model.CurrentLogins.Count > 0) +{ +

Registered Logins

+ + + @for (var index = 0; index < Model.CurrentLogins.Count; index++) + { + + + + + } + +
@Model.CurrentLogins[index].LoginProvider + @if ((bool)ViewData["ShowRemoveButton"]) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins.Count > 0) +{ +

Add another service to log in.

+
+
+
+

+ @foreach (var provider in Model.OtherLogins) + { + + } +

+
+
+} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/SetPassword.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/SetPassword.cshtml new file mode 100644 index 000000000..cfa779160 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/SetPassword.cshtml @@ -0,0 +1,38 @@ +@model SetPasswordViewModel +@{ + ViewData["Title"] = "Set Password"; +} + +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+ +
+

Set your password

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/VerifyPhoneNumber.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/VerifyPhoneNumber.cshtml new file mode 100644 index 000000000..af7cd0b1f --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Manage/VerifyPhoneNumber.cshtml @@ -0,0 +1,30 @@ +@model VerifyPhoneNumberViewModel +@{ + ViewData["Title"] = "Verify Phone Number"; +} + +

@ViewData["Title"].

+ +
+ +

Add a phone number.

+
@ViewData["Status"]
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/Error.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/Error.cshtml new file mode 100644 index 000000000..229c2dead --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/Error.cshtml @@ -0,0 +1,14 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_Layout.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_Layout.cshtml new file mode 100644 index 000000000..56827ca52 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_Layout.cshtml @@ -0,0 +1,68 @@ + + + + + + @ViewData["Title"] - WebApplication + + + + + + + + + + + + +
+ @RenderBody() +
+
+

© 2016 - WebApplication

+
+
+ + + + + + + + + + + + + @RenderSection("scripts", required: false) + + diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_LoginPartial.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 000000000..f50d5e89e --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,26 @@ +@using Microsoft.AspNetCore.Identity +@using WebApplication.Models + +@inject SignInManager SignInManager +@inject UserManager UserManager + +@if (SignInManager.IsSignedIn(User)) +{ + +} +else +{ + +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_ValidationScriptsPartial.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_ValidationScriptsPartial.cshtml new file mode 100644 index 000000000..289b22064 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/Shared/_ValidationScriptsPartial.cshtml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewImports.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewImports.cshtml new file mode 100644 index 000000000..dcca16cb0 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewImports.cshtml @@ -0,0 +1,6 @@ +@using WebApplication +@using WebApplication.Models +@using WebApplication.Models.AccountViewModels +@using WebApplication.Models.ManageViewModels +@using Microsoft.AspNetCore.Identity +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewStart.cshtml b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewStart.cshtml new file mode 100644 index 000000000..820a2f6e0 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/appsettings.json b/TestAssets/TestProjects/ProjectJsonWebTemplate/appsettings.json new file mode 100644 index 000000000..53b17ae04 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/appsettings.json @@ -0,0 +1,13 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=WebApplication.db" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/bower.json b/TestAssets/TestProjects/ProjectJsonWebTemplate/bower.json new file mode 100644 index 000000000..3891fce13 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/bower.json @@ -0,0 +1,10 @@ +{ + "name": "webapplication", + "private": true, + "dependencies": { + "bootstrap": "3.3.6", + "jquery": "2.2.3", + "jquery-validation": "1.15.0", + "jquery-validation-unobtrusive": "3.2.6" + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/gulpfile.js b/TestAssets/TestProjects/ProjectJsonWebTemplate/gulpfile.js new file mode 100644 index 000000000..faf295540 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/gulpfile.js @@ -0,0 +1,45 @@ +/// +"use strict"; + +var gulp = require("gulp"), + rimraf = require("rimraf"), + concat = require("gulp-concat"), + cssmin = require("gulp-cssmin"), + uglify = require("gulp-uglify"); + +var webroot = "./wwwroot/"; + +var paths = { + js: webroot + "js/**/*.js", + minJs: webroot + "js/**/*.min.js", + css: webroot + "css/**/*.css", + minCss: webroot + "css/**/*.min.css", + concatJsDest: webroot + "js/site.min.js", + concatCssDest: webroot + "css/site.min.css" +}; + +gulp.task("clean:js", function (cb) { + rimraf(paths.concatJsDest, cb); +}); + +gulp.task("clean:css", function (cb) { + rimraf(paths.concatCssDest, cb); +}); + +gulp.task("clean", ["clean:js", "clean:css"]); + +gulp.task("min:js", function () { + return gulp.src([paths.js, "!" + paths.minJs], { base: "." }) + .pipe(concat(paths.concatJsDest)) + .pipe(uglify()) + .pipe(gulp.dest(".")); +}); + +gulp.task("min:css", function () { + return gulp.src([paths.css, "!" + paths.minCss]) + .pipe(concat(paths.concatCssDest)) + .pipe(cssmin()) + .pipe(gulp.dest(".")); +}); + +gulp.task("min", ["min:js", "min:css"]); diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/package.json b/TestAssets/TestProjects/ProjectJsonWebTemplate/package.json new file mode 100644 index 000000000..4bb6c884c --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/package.json @@ -0,0 +1,12 @@ +{ + "name": "webapplication", + "version": "0.0.0", + "private": true, + "devDependencies": { + "gulp": "3.9.1", + "gulp-concat": "2.6.0", + "gulp-cssmin": "0.1.7", + "gulp-uglify": "1.5.3", + "rimraf": "2.5.2" + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/project.json.template b/TestAssets/TestProjects/ProjectJsonWebTemplate/project.json.template new file mode 100644 index 000000000..7da844f6d --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/project.json.template @@ -0,0 +1,112 @@ +{ + "userSecretsId": "aspnet-WebApplication-0799fe3e-6eaf-4c5f-b40e-7c6bfd5dfa9a", + + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.1", + "type": "platform" + }, + "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", + "Microsoft.AspNetCore.Diagnostics": "1.0.0", + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0", + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0", + "Microsoft.AspNetCore.Mvc": "1.0.1", + "Microsoft.AspNetCore.Razor.Tools": { + "version": "1.0.0-preview2-final", + "type": "build" + }, + "Microsoft.AspNetCore.Routing": "1.0.1", + "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", + "Microsoft.AspNetCore.StaticFiles": "1.0.0", + "Microsoft.EntityFrameworkCore.Sqlite": "1.0.1", + "Microsoft.EntityFrameworkCore.Tools": { + "version": "1.0.0-preview2-final", + "type": "build" + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", + "Microsoft.Extensions.Configuration.Json": "1.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", + "Microsoft.Extensions.Logging": "1.0.0", + "Microsoft.Extensions.Logging.Console": "1.0.0", + "Microsoft.Extensions.Logging.Debug": "1.0.0", + "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { + "version": "1.0.0-preview2-update1", + "type": "build" + }, + "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { + "version": "1.0.0-preview2-update1", + "type": "build" + } + }, + + "tools": { + "Microsoft.AspNetCore.Razor.Tools": { + "version": "1.0.0-preview2-final", + "imports": "portable-net45+win8+dnxcore50" + }, + "Microsoft.AspNetCore.Server.IISIntegration.Tools": { + "version": "1.0.0-preview2-final", + "imports": "portable-net45+win8+dnxcore50" + }, + "Microsoft.EntityFrameworkCore.Tools": { + "version": "1.0.0-preview2-final", + "imports": [ + "portable-net45+win8+dnxcore50", + "portable-net45+win8" + ] + }, + "Microsoft.Extensions.SecretManager.Tools": { + "version": "1.0.0-preview2-final", + "imports": "portable-net45+win8+dnxcore50" + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { + "version": "1.0.0-preview2-final", + "imports": [ + "portable-net45+win8+dnxcore50", + "portable-net45+win8" + ] + } + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dotnet5.6", + "dnxcore50", + "portable-net45+win8" + ] + } + }, + + "buildOptions": { + "debugType": "portable", + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + }, + + "publishOptions": { + "include": [ + "wwwroot", + "**/*.cshtml", + "appsettings.json", + "web.config" + ] + }, + + "scripts": { + "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ], + "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] + }, + + "tooling": { + "defaultNamespace": "WebApplication" + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/web.config b/TestAssets/TestProjects/ProjectJsonWebTemplate/web.config new file mode 100644 index 000000000..a8d667275 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.css b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.css new file mode 100644 index 000000000..6baa84da1 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.css @@ -0,0 +1,44 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Wrapping element */ +/* Set some basic padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Set widths on the form inputs since otherwise they're 100% wide */ +input, +select, +textarea { + max-width: 280px; +} + +/* Carousel */ +.carousel-caption p { + font-size: 20px; + line-height: 1.4; +} + +/* buttons and links extension to use brackets: [ click me ] */ +.btn-bracketed::before { + display:inline-block; + content: "["; + padding-right: 0.5em; +} +.btn-bracketed::after { + display:inline-block; + content: "]"; + padding-left: 0.5em; +} + +/* Hide/rearrange for smaller screens */ +@media screen and (max-width: 767px) { + /* Hide captions */ + .carousel-caption { + display: none + } +} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.min.css b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.min.css new file mode 100644 index 000000000..c8f600ac5 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/css/site.min.css @@ -0,0 +1 @@ +body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.btn-bracketed::before{display:inline-block;content:"[";padding-right:.5em}.btn-bracketed::after{display:inline-block;content:"]";padding-left:.5em}@media screen and (max-width:767px){.carousel-caption{display:none}} diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/favicon.ico b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a3a799985c43bc7309d701b2cad129023377dc71 GIT binary patch literal 32038 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 \ No newline at end of file diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner2.svg b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner2.svg new file mode 100644 index 000000000..9679c604d --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner3.svg b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner3.svg new file mode 100644 index 000000000..9be2c2503 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner3.svg @@ -0,0 +1 @@ +banner3b \ No newline at end of file diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner4.svg b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner4.svg new file mode 100644 index 000000000..38b3d7cd1 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/images/banner4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.js b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.js new file mode 100644 index 000000000..e069226a1 --- /dev/null +++ b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.js @@ -0,0 +1 @@ +// Write your Javascript code. diff --git a/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.min.js b/TestAssets/TestProjects/ProjectJsonWebTemplate/wwwroot/js/site.min.js new file mode 100644 index 000000000..e69de29bb diff --git a/build/Microsoft.DotNet.Cli.tasks b/build/Microsoft.DotNet.Cli.tasks index 7de1a9a70..257a6a96a 100644 --- a/build/Microsoft.DotNet.Cli.tasks +++ b/build/Microsoft.DotNet.Cli.tasks @@ -12,6 +12,7 @@ + diff --git a/build/compile/Microsoft.DotNet.Cli.LzmaArchive.targets b/build/compile/Microsoft.DotNet.Cli.LzmaArchive.targets index cfdd1ffde..056d0e6ee 100644 --- a/build/compile/Microsoft.DotNet.Cli.LzmaArchive.targets +++ b/build/compile/Microsoft.DotNet.Cli.LzmaArchive.targets @@ -15,8 +15,10 @@ - diff --git a/build/test/RunTest.proj b/build/test/RunTest.proj index b3faca530..3c9055f42 100644 --- a/build/test/RunTest.proj +++ b/build/test/RunTest.proj @@ -3,10 +3,9 @@ - $([MSBuild]::Unescape($(CLIBuildDll))) + $([MSBuild]::Unescape($(CLIBuildDll))) - diff --git a/build_projects/dotnet-cli-build/DotNetRestore3.cs b/build_projects/dotnet-cli-build/DotNetRestore3.cs new file mode 100644 index 000000000..571ccc16e --- /dev/null +++ b/build_projects/dotnet-cli-build/DotNetRestore3.cs @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Cli.Build +{ + public class DotNetRestore3 : DotNetTool + { + protected override string Command + { + get { return "restore3"; } + } + + protected override string Args + { + get { return $"{GetSource()} {GetPackages()} {GetSkipInvalidConfigurations()}"; } + } + + public string Source { get; set; } + + public string Packages { get; set; } + + public bool SkipInvalidConfigurations { get; set; } + + private string GetSource() + { + if (!string.IsNullOrEmpty(Source)) + { + return $"--source {Source}"; + } + + return null; + } + + private string GetPackages() + { + if (!string.IsNullOrEmpty(Packages)) + { + return $"--packages {Packages}"; + } + + return null; + } + + private string GetSkipInvalidConfigurations() + { + if (SkipInvalidConfigurations) + { + return "/p:SkipInvalidConfigurations=true"; + } + + return null; + } + } +} diff --git a/src/Microsoft.DotNet.Configurer/NuGetCachePrimer.cs b/src/Microsoft.DotNet.Configurer/NuGetCachePrimer.cs index 20b08d3b8..df2d85931 100644 --- a/src/Microsoft.DotNet.Configurer/NuGetCachePrimer.cs +++ b/src/Microsoft.DotNet.Configurer/NuGetCachePrimer.cs @@ -86,8 +86,8 @@ namespace Microsoft.DotNet.Configurer private bool RestoreTemporaryProject(string extractedPackagesArchiveDirectory, string workingDirectory) { return RunCommand( - "restore", - new[] {"-s", $"{extractedPackagesArchiveDirectory}"}, + "restore3", + new[] {"-s", extractedPackagesArchiveDirectory}, workingDirectory); } diff --git a/src/dotnet/commands/dotnet-migrate/Program.cs b/src/dotnet/commands/dotnet-migrate/Program.cs index 9e5b6b84b..ae7cf43c9 100644 --- a/src/dotnet/commands/dotnet-migrate/Program.cs +++ b/src/dotnet/commands/dotnet-migrate/Program.cs @@ -36,7 +36,7 @@ namespace Microsoft.DotNet.Tools.Migrate " - a directory to migrate, it will recursively search for project.json files to migrate.", "Defaults to current directory if nothing is specified.")); - CommandOption template = app.Option("-t|--template-file", "Base MSBuild template to use for migrated app. The default is the project included in dotnet new -t msbuild", CommandOptionType.SingleValue); + CommandOption template = app.Option("-t|--template-file", "Base MSBuild template to use for migrated app. The default is the project included in dotnet new", CommandOptionType.SingleValue); CommandOption sdkVersion = app.Option("-v|--sdk-package-version", "The version of the sdk package that will be referenced in the migrated app. The default is the version of the sdk in dotnet new -t msbuild", CommandOptionType.SingleValue); CommandOption xprojFile = app.Option("-x|--xproj-file", "The path to the xproj file to use. Required when there is more than one xproj in a project directory.", CommandOptionType.SingleValue); CommandOption skipProjectReferences = app.Option("-s|--skip-project-references", "Skip migrating project references. By default project references are migrated recursively", CommandOptionType.BoolValue); diff --git a/src/dotnet/commands/dotnet-migrate/TemporaryDotnetNewTemplateProject.cs b/src/dotnet/commands/dotnet-migrate/TemporaryDotnetNewTemplateProject.cs index 7e508daad..124e0e81a 100644 --- a/src/dotnet/commands/dotnet-migrate/TemporaryDotnetNewTemplateProject.cs +++ b/src/dotnet/commands/dotnet-migrate/TemporaryDotnetNewTemplateProject.cs @@ -42,7 +42,7 @@ namespace Microsoft.DotNet.Tools.Migrate } Directory.CreateDirectory(tempDir); - RunCommand("new", new string[] { "-t", "msbuild" }, tempDir); + RunCommand("new", new string[] {}, tempDir); return tempDir; } diff --git a/src/dotnet/commands/dotnet-new/CSharp_MSBuild/$projectName$.csproj b/src/dotnet/commands/dotnet-new/CSharp_Console/$projectName$.csproj old mode 100644 new mode 100755 similarity index 77% rename from src/dotnet/commands/dotnet-new/CSharp_MSBuild/$projectName$.csproj rename to src/dotnet/commands/dotnet-new/CSharp_Console/$projectName$.csproj index 6cb47acbf..87ad98fe3 --- a/src/dotnet/commands/dotnet-new/CSharp_MSBuild/$projectName$.csproj +++ b/src/dotnet/commands/dotnet-new/CSharp_Console/$projectName$.csproj @@ -1,10 +1,9 @@ - + - + Exe netcoreapp1.0 - bin\$(Configuration) @@ -17,10 +16,10 @@ 1.0.1 - 1.0.0-alpha-20161010-1 + 1.0.0-alpha-20161012-3 All - + diff --git a/src/dotnet/commands/dotnet-new/CSharp_Console/Program.cs b/src/dotnet/commands/dotnet-new/CSharp_Console/Program.cs index f095a9f74..c81448f54 100644 --- a/src/dotnet/commands/dotnet-new/CSharp_Console/Program.cs +++ b/src/dotnet/commands/dotnet-new/CSharp_Console/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; class Program { diff --git a/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template b/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template deleted file mode 100644 index 0e21757b8..000000000 --- a/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "1.0.0-*", - "buildOptions": { - "debugType": "portable", - "emitEntryPoint": true - }, - "dependencies": {}, - "frameworks": { - "netcoreapp1.0": { - "dependencies": { - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.1" - } - }, - "imports": "dnxcore50" - } - } -} diff --git a/src/dotnet/commands/dotnet-new/CSharp_Lib/$projectName$.csproj b/src/dotnet/commands/dotnet-new/CSharp_Lib/$projectName$.csproj new file mode 100755 index 000000000..6fd86870d --- /dev/null +++ b/src/dotnet/commands/dotnet-new/CSharp_Lib/$projectName$.csproj @@ -0,0 +1,24 @@ + + + + + netstandard1.4 + + + + + + + + + + 1.6 + + + 1.0.0-alpha-20161012-3 + All + + + + + diff --git a/src/dotnet/commands/dotnet-new/CSharp_Lib/Library.cs b/src/dotnet/commands/dotnet-new/CSharp_Lib/Library.cs index 9e190dead..ee29fc477 100644 --- a/src/dotnet/commands/dotnet-new/CSharp_Lib/Library.cs +++ b/src/dotnet/commands/dotnet-new/CSharp_Lib/Library.cs @@ -4,8 +4,5 @@ namespace ClassLibrary { public class Class1 { - public void Method1() - { - } } } diff --git a/src/dotnet/commands/dotnet-new/CSharp_Lib/project.json.template b/src/dotnet/commands/dotnet-new/CSharp_Lib/project.json.template deleted file mode 100644 index 6f1ec549e..000000000 --- a/src/dotnet/commands/dotnet-new/CSharp_Lib/project.json.template +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "1.0.0-*", - "buildOptions": { - "debugType": "portable" - }, - "dependencies": {}, - "frameworks": { - "netstandard1.6": { - "dependencies": { - "NETStandard.Library": "1.6.0" - } - } - } -} diff --git a/src/dotnet/commands/dotnet-new/CSharp_MSBuild/Program.cs b/src/dotnet/commands/dotnet-new/CSharp_MSBuild/Program.cs deleted file mode 100644 index 51233cffa..000000000 --- a/src/dotnet/commands/dotnet-new/CSharp_MSBuild/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ConsoleApplication -{ - public class Program - { - public static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} diff --git a/src/dotnet/commands/dotnet-new/Program.cs b/src/dotnet/commands/dotnet-new/Program.cs index 29a2ea7a2..4a5436d13 100644 --- a/src/dotnet/commands/dotnet-new/Program.cs +++ b/src/dotnet/commands/dotnet-new/Program.cs @@ -28,10 +28,10 @@ namespace Microsoft.DotNet.Tools.New return parts[parts.Length - 2] + "." + parts[parts.Length - 1]; } - public int CreateEmptyProject(string languageName, string templateDir) + public int CreateEmptyProject(string languageName, string templateDir, bool isMsBuild) { // Check if project.json exists in the folder - if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "project.json"))) + if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "project.json")) && !isMsBuild) { Reporter.Error.WriteLine($"Creating new {languageName} project failed, project already exists."); return 1; @@ -69,8 +69,12 @@ namespace Microsoft.DotNet.Tools.New archive.ExtractToDirectory(projectDirectory); - ReplaceProjectJsonTemplateValues(projectDirectory); ReplaceFileTemplateNames(projectDirectory); + + if (!isMsBuild) + { + ReplaceProjectJsonTemplateValues(projectDirectory); + } } catch (IOException ex) { @@ -128,15 +132,31 @@ namespace Microsoft.DotNet.Tools.New app.Description = "Initializes empty project for .NET Platform"; app.HelpOption("-h|--help"); - var csharp = new { Name = "C#", Alias = new[] { "c#", "cs", "csharp" }, TemplatePrefix = "CSharp", Templates = new[] { "Console", "Web", "Lib", "xunittest", "nunittest", "MSBuild" } }; - var fsharp = new { Name = "F#", Alias = new[] { "f#", "fs", "fsharp" }, TemplatePrefix = "FSharp", Templates = new[] { "Console", "Lib" } }; + var csharp = new { Name = "C#", Alias = new[] { "c#", "cs", "csharp" }, TemplatePrefix = "CSharp", + Templates = new[] + { + new { Name = "Console", isMsBuild = true }, + new { Name = "Web", isMsBuild = false }, + new { Name = "Lib", isMsBuild = true }, + new { Name = "xunittest", isMsBuild = false }, + new { Name = "nunittest", isMsBuild = false } + } + }; + + var fsharp = new { Name = "F#", Alias = new[] { "f#", "fs", "fsharp" }, TemplatePrefix = "FSharp", + Templates = new[] + { + new { Name = "Console", isMsBuild = false }, + new { Name = "Lib", isMsBuild = false } + } + }; var languages = new[] { csharp, fsharp }; string langValuesString = string.Join(", ", languages.Select(l => l.Name)); var typeValues = from l in languages - let values = string.Join(", ", l.Templates) + let values = string.Join(", ", l.Templates.Select(t => t.Name)) select $"Valid values for {l.Name}: {values}."; string typeValuesString = string.Join(" ", typeValues); @@ -157,10 +177,10 @@ namespace Microsoft.DotNet.Tools.New return -1; } - string typeValue = type.Value() ?? language.Templates.First(); + string typeValue = type.Value() ?? language.Templates.First().Name; - string templateName = language.Templates.FirstOrDefault(t => StringComparer.OrdinalIgnoreCase.Equals(typeValue, t)); - if (templateName == null) + var template = language.Templates.FirstOrDefault(t => StringComparer.OrdinalIgnoreCase.Equals(typeValue, t.Name)); + if (template == null) { Reporter.Error.WriteLine($"Unrecognized type: {typeValue}".Red()); Reporter.Error.WriteLine($"Available types for {language.Name} :".Red()); @@ -171,9 +191,9 @@ namespace Microsoft.DotNet.Tools.New return -1; } - string templateDir = $"{language.TemplatePrefix}_{templateName}"; + string templateDir = $"{language.TemplatePrefix}_{template.Name}"; - return dotnetNew.CreateEmptyProject(language.Name, templateDir); + return dotnetNew.CreateEmptyProject(language.Name, templateDir, template.isMsBuild); }); try diff --git a/test/EndToEnd/EndToEndTest.cs b/test/EndToEnd/EndToEndTest.cs deleted file mode 100644 index 5904a7108..000000000 --- a/test/EndToEnd/EndToEndTest.cs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Microsoft.DotNet.InternalAbstractions; -using Microsoft.DotNet.PlatformAbstractions; -using Microsoft.DotNet.Tools.Test.Utilities; -using Xunit; - -namespace Microsoft.DotNet.Tests.EndToEnd -{ - public class EndToEndTest : TestBase - { - private static readonly string NetCoreAppTfm = "netcoreapp1.0"; - private static readonly string s_expectedOutput = "Hello World!" + Environment.NewLine; - private static readonly string s_testdirName = "e2etestroot"; - private static readonly string s_outputdirName = "test space/bin"; - - private static string RestoredTestProjectDirectory { get; set; } - - private string Rid { get; set; } - private string TestDirectory { get; set; } - private string TestProject { get; set; } - private string OutputDirectory { get; set; } - - static EndToEndTest() - { - EndToEndTest.SetupStaticTestProject(); - } - - public static void Main() - { - Console.WriteLine("Dummy Entrypoint."); - } - - public EndToEndTest() - { - TestInstanceSetup(); - } - - [Fact] - public void TestDotnetBuild() - { - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, framework: NetCoreAppTfm); - - buildCommand.Execute().Should().Pass(); - - TestOutputExecutable(OutputDirectory, buildCommand.GetPortableOutputName(), s_expectedOutput); - } - - [Fact] - public void TestDotnetIncrementalBuild() - { - // first build - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, framework: NetCoreAppTfm); - buildCommand.Execute().Should().Pass(); - TestOutputExecutable(OutputDirectory, buildCommand.GetPortableOutputName(), s_expectedOutput); - - var binariesOutputDirectory = GetCompilationOutputPath(OutputDirectory, false); - var latestWriteTimeFirstBuild = GetLastWriteTimeUtcOfDirectoryFiles( - binariesOutputDirectory); - - // second build; should get skipped (incremental because no inputs changed) - buildCommand.Execute().Should().Pass(); - TestOutputExecutable(OutputDirectory, buildCommand.GetPortableOutputName(), s_expectedOutput); - - var latestWriteTimeUtcSecondBuild = GetLastWriteTimeUtcOfDirectoryFiles( - binariesOutputDirectory); - Assert.Equal(latestWriteTimeFirstBuild, latestWriteTimeUtcSecondBuild); - - TouchSourceFileInDirectory(TestDirectory); - - // third build; should get compiled because the source file got touched - buildCommand.Execute().Should().Pass(); - TestOutputExecutable(OutputDirectory, buildCommand.GetPortableOutputName(), s_expectedOutput); - - var latestWriteTimeUtcThirdBuild = GetLastWriteTimeUtcOfDirectoryFiles( - binariesOutputDirectory); - Assert.NotEqual(latestWriteTimeUtcSecondBuild, latestWriteTimeUtcThirdBuild); - } - - [Fact(Skip = "Native compilation isn't shipping in 1.0 and we're moving it out anyway")] - public void TestDotnetBuildNativeRyuJit() - { - if (!IsNativeCompilationSupported()) - { - return; - } - - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, framework: NetCoreAppTfm); - - buildCommand.Execute().Should().Pass(); - - TestNativeOutputExecutable(OutputDirectory, buildCommand.GetOutputExecutableName(), s_expectedOutput); - } - - [Fact(Skip = "Native compilation isn't shipping in 1.0 and we're moving it out anyway")] - public void TestDotnetBuildNativeCpp() - { - if (!IsNativeCompilationSupported()) - { - return; - } - - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, nativeCppMode: true, framework: NetCoreAppTfm); - - buildCommand.Execute().Should().Pass(); - - TestNativeOutputExecutable(OutputDirectory, buildCommand.GetOutputExecutableName(), s_expectedOutput); - } - - [Fact(Skip = "Native compilation isn't shipping in 1.0 and we're moving it out anyway")] - public void TestDotnetCompileNativeCppIncremental() - { - if (!IsNativeCompilationSupported()) - { - return; - } - - // first build - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, nativeCppMode: true, framework: NetCoreAppTfm); - var binariesOutputDirectory = GetCompilationOutputPath(OutputDirectory, false); - - buildCommand.Execute().Should().Pass(); - - TestNativeOutputExecutable(OutputDirectory, buildCommand.GetOutputExecutableName(), s_expectedOutput); - - var latestWriteTimeUtcFirstBuild = GetLastWriteTimeUtcOfDirectoryFiles(binariesOutputDirectory); - - // second build; should be skipped because nothing changed - buildCommand.Execute().Should().Pass(); - - TestNativeOutputExecutable(OutputDirectory, buildCommand.GetOutputExecutableName(), s_expectedOutput); - - var latestWriteTimeUtcSecondBuild = GetLastWriteTimeUtcOfDirectoryFiles(binariesOutputDirectory); - Assert.Equal(latestWriteTimeUtcFirstBuild, latestWriteTimeUtcSecondBuild); - } - - [Fact] - public void TestDotnetRun() - { - var restoreCommand = new TestCommand("dotnet"); - restoreCommand.Execute($"restore {TestProject}") - .Should() - .Pass(); - var runCommand = new RunCommand(TestProject); - - runCommand.Execute() - .Should() - .Pass(); - } - - [Fact] - public void TestDotnetPack() - { - var packCommand = new PackCommand(TestDirectory, output: OutputDirectory); - - packCommand.Execute() - .Should() - .Pass(); - } - - [Fact] - public void TestDotnetPublish() - { - var publishCommand = new PublishCommand(TestProject, output: OutputDirectory); - publishCommand.Execute().Should().Pass(); - - TestExecutable(OutputDirectory, publishCommand.GetPortableOutputName(), s_expectedOutput); - } - - [Fact] - public void TestDotnetHelp() - { - var helpCommand = new HelpCommand(); - helpCommand.Execute().Should().Pass(); - } - - [Fact] - public void TestDotnetHelpBuild() - { - var helpCommand = new HelpCommand(); - helpCommand.Execute("build").Should().Pass(); - } - - private void TestInstanceSetup() - { - var root = Temp.CreateDirectory(); - - var testInstanceDir = root.CopyDirectory(RestoredTestProjectDirectory); - - TestDirectory = testInstanceDir.Path; - TestProject = Path.Combine(TestDirectory, "project.json"); - OutputDirectory = Path.Combine(TestDirectory, s_outputdirName); - - Rid = DotnetLegacyRuntimeIdentifiers.InferLegacyRestoreRuntimeIdentifier(); - } - - private static void SetupStaticTestProject() - { - RestoredTestProjectDirectory = Path.Combine(AppContext.BaseDirectory, "bin", s_testdirName); - - // Ignore Delete Failure - try - { - Directory.Delete(RestoredTestProjectDirectory, true); - } - catch (Exception) { } - - Directory.CreateDirectory(RestoredTestProjectDirectory); - - // Todo: this is a hack until corefx is on nuget.org remove this After RC 2 Release - NuGetConfig.Write(RestoredTestProjectDirectory); - - var currentDirectory = Directory.GetCurrentDirectory(); - Directory.SetCurrentDirectory(RestoredTestProjectDirectory); - - new NewCommand().Execute().Should().Pass(); - new RestoreCommand().Execute().Should().Pass(); - - Directory.SetCurrentDirectory(currentDirectory); - } - - private bool IsNativeCompilationSupported() - { - bool isSupported = true; - var platform = RuntimeEnvironment.OperatingSystem.ToLower(); - switch (platform) - { - case "centos": - case "rhel": - case "fedora": - case "opensuse": - Console.WriteLine("Skipping native compilation tests on OpenSUSE/Fedora/CentOS/RHEL - https://github.com/dotnet/cli/issues/453"); - isSupported = false; - break; - case "debian": - Console.WriteLine("Skipping native compilation tests on Debian - https://github.com/dotnet/cli/issues/1666"); - isSupported = false; - break; - case "windows": - Console.WriteLine("Skipping native compilation tests on Windows x86 - https://github.com/dotnet/cli/issues/1550"); - isSupported = RuntimeInformation.ProcessArchitecture != Architecture.X86; - break; - default: - break; - } - - return isSupported; - } - - private static DateTime GetLastWriteTimeUtcOfDirectoryFiles(string outputDirectory) - { - return Directory.EnumerateFiles(outputDirectory).Max(f => File.GetLastWriteTimeUtc(f)); - } - - private static void TouchSourceFileInDirectory(string directory) - { - var csFile = Directory.EnumerateFiles(directory).First(f => Path.GetExtension(f).Equals(".cs")); - File.SetLastWriteTimeUtc(csFile, DateTime.UtcNow); - } - } -} diff --git a/test/EndToEnd/GivenDotNetUsesMSBuild.cs b/test/EndToEnd/GivenDotNetUsesMSBuild.cs index 994215f93..393538592 100644 --- a/test/EndToEnd/GivenDotNetUsesMSBuild.cs +++ b/test/EndToEnd/GivenDotNetUsesMSBuild.cs @@ -9,6 +9,10 @@ namespace Microsoft.DotNet.Tests.EndToEnd { public class GivenDotNetUsesMSBuild : TestBase { + public static void Main() + { + } + [Fact] public void ItCanNewRestoreBuildRunCleanMSBuildProject() { @@ -18,13 +22,13 @@ namespace Microsoft.DotNet.Tests.EndToEnd new NewCommand() .WithWorkingDirectory(projectDirectory) - .Execute("-t msbuild") + .Execute("") .Should() .Pass(); new Restore3Command() .WithWorkingDirectory(projectDirectory) - .Execute() + .Execute("/p:SkipInvalidConfigurations=true") .Should() .Pass(); diff --git a/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCachePrimer.cs b/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCachePrimer.cs index 4e0b81477..e1fffcd88 100644 --- a/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCachePrimer.cs +++ b/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCachePrimer.cs @@ -69,7 +69,7 @@ namespace Microsoft.DotNet.Configurer.UnitTests SetupCommandMock(_dotnetRestoreCommandMock); commandFactoryMock .Setup(c => c.Create( - "restore", + "restore3", It.IsAny>(), null, Constants.DefaultConfiguration)) @@ -150,7 +150,7 @@ namespace Microsoft.DotNet.Configurer.UnitTests { _commandFactoryMock.Verify( c => c.Create( - "restore", + "restore3", new [] {"-s", $"{PACKAGES_ARCHIVE_PATH}"}, null, Constants.DefaultConfiguration), @@ -174,7 +174,7 @@ namespace Microsoft.DotNet.Configurer.UnitTests commandFactoryMock.Verify( c => c.Create( - "restore", + "restore3", It.IsAny>(), It.IsAny(), It.IsAny()), diff --git a/test/Performance/HelloWorld.cs b/test/Performance/HelloWorld.cs index 828613a32..ecfb8d0a8 100644 --- a/test/Performance/HelloWorld.cs +++ b/test/Performance/HelloWorld.cs @@ -11,13 +11,14 @@ namespace Microsoft.DotNet.Tests.Performance public class HelloWorld : TestBase { private static readonly string s_testdirName = "helloworldtestroot"; + private static readonly string s_testProject = $"{s_testdirName}.csproj"; private static readonly string s_outputdirName = "test space/bin"; private static string AssetsRoot { get; set; } private static string RestoredTestProjectDirectory { get; set; } + private string ProjectPath { get; set; } private string TestDirectory { get; set; } - private string TestProject { get; set; } private string OutputDirectory { get; set; } static HelloWorld() @@ -38,24 +39,24 @@ namespace Microsoft.DotNet.Tests.Performance TestInstanceSetup(); // Setup the build command. - var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, framework: DefaultFramework); + var buildCommand = new Build3Command(); using (iter.StartMeasurement()) { // Execute the build command. - buildCommand.Execute(); + buildCommand.Execute($"{ProjectPath} --output \"{OutputDirectory}\" --framework {DefaultFramework}"); } } } private void TestInstanceSetup() { - var root = Temp.CreateDirectory(); + var root = Temp.CreateDirectory(); var testInstanceDir = root.CopyDirectory(RestoredTestProjectDirectory); TestDirectory = testInstanceDir.Path; - TestProject = Path.Combine(TestDirectory, "project.json"); OutputDirectory = Path.Combine(TestDirectory, s_outputdirName); + ProjectPath = Path.Combine(TestDirectory, s_testProject); } private static void SetupStaticTestProject() @@ -79,9 +80,10 @@ namespace Microsoft.DotNet.Tests.Performance newCommand.WorkingDirectory = RestoredTestProjectDirectory; newCommand.Execute().Should().Pass(); - var restoreCommand = new RestoreCommand(); + var restoreCommand = new Restore3Command(); restoreCommand.WorkingDirectory = RestoredTestProjectDirectory; - restoreCommand.Execute().Should().Pass(); + restoreCommand.Execute("/p:SkipInvalidConfigurations=true") + .Should().Pass(); } } } diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs index f70322703..0afedc509 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs @@ -29,7 +29,9 @@ namespace Microsoft.DotNet.Migration.Tests [InlineData("TestAppWithContents")] public void It_migrates_apps(string projectName) { - var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i").WithLockFiles().Path; + var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i") + .WithLockFiles() + .Path; CleanBinObj(projectDirectory); @@ -37,11 +39,14 @@ namespace Microsoft.DotNet.Migration.Tests var outputsIdentical = outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs); + if (!outputsIdentical) { OutputDiagnostics(outputComparisonData); } + outputsIdentical.Should().BeTrue(); + VerifyAllMSBuildOutputsRunnable(projectDirectory); } @@ -55,49 +60,60 @@ namespace Microsoft.DotNet.Migration.Tests var outputsIdentical = outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs); + if (!outputsIdentical) { OutputDiagnostics(outputComparisonData); } + outputsIdentical.Should().BeTrue(); + VerifyAllMSBuildOutputsRunnable(projectDirectory); + VerifyAllMSBuildOutputsAreSigned(projectDirectory); } [Fact] public void It_migrates_dotnet_new_console_with_identical_outputs() { - var projectDirectory = Path.Combine(AppContext.BaseDirectory, "newconsoletest"); - if (Directory.Exists(projectDirectory)) - { - Directory.Delete(projectDirectory, true); - } - Directory.CreateDirectory(projectDirectory); + var testInstance = TestAssetsManager + .CreateTestInstance("ProjectJsonConsoleTemplate"); + + var projectDirectory = testInstance.Path; - var outputComparisonData = GetDotnetNewComparisonData(projectDirectory, "console"); + var outputComparisonData = GetComparisonData(projectDirectory); var outputsIdentical = outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs); + if (!outputsIdentical) { OutputDiagnostics(outputComparisonData); } + outputsIdentical.Should().BeTrue(); + VerifyAllMSBuildOutputsRunnable(projectDirectory); } [Fact(Skip="https://github.com/dotnet/cli/issues/4299")] public void It_migrates_dotnet_new_web_with_outputs_containing_project_json_outputs() { - var projectDirectory = Temp.CreateDirectory().Path; - var outputComparisonData = GetDotnetNewComparisonData(projectDirectory, "web"); + var testInstance = TestAssetsManager + .CreateTestInstance("ProjectJsonWebTemplate"); + + var projectDirectory = testInstance.Path; + + var outputComparisonData = GetComparisonData(projectDirectory); var outputsIdentical = outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs); + if (!outputsIdentical) { OutputDiagnostics(outputComparisonData); } + outputsIdentical.Should().BeTrue(); } @@ -108,6 +124,7 @@ namespace Microsoft.DotNet.Migration.Tests { var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i").WithLockFiles().Path; + var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory, projectName); var outputsIdentical = @@ -129,6 +146,7 @@ namespace Microsoft.DotNet.Migration.Tests { var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i").WithLockFiles().Path; + var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory, Path.GetFileNameWithoutExtension(projectName)); var outputsIdentical = @@ -156,6 +174,7 @@ namespace Microsoft.DotNet.Migration.Tests MigrateProject(new [] { Path.Combine(projectDirectory, projectName) }); string[] migratedProjects = expectedProjects.Split(new char[] { ',' }); + VerifyMigration(migratedProjects, projectDirectory); } @@ -192,6 +211,7 @@ namespace Microsoft.DotNet.Migration.Tests } string[] migratedProjects = new string[] { "ProjectA", "ProjectB", "ProjectC", "ProjectD", "ProjectE", "ProjectF", "ProjectG", "ProjectH", "ProjectI", "ProjectJ" }; + VerifyMigration(migratedProjects, projectDirectory); } @@ -201,9 +221,11 @@ namespace Microsoft.DotNet.Migration.Tests var projectDirectory = TestAssetsManager.CreateTestInstance("TestAppDependencyGraph").Path; var project = Path.Combine(projectDirectory, "ProjectA", "project.json"); + MigrateProject(new [] { project }); string[] migratedProjects = new string[] { "ProjectA", "ProjectB", "ProjectC", "ProjectD", "ProjectE" }; + VerifyMigration(migratedProjects, projectDirectory); } @@ -212,7 +234,9 @@ namespace Microsoft.DotNet.Migration.Tests public void It_migrates_and_builds_P2P_references() { var assetsDir = TestAssetsManager.CreateTestInstance("TestAppDependencyGraph").WithLockFiles().Path; + var projectDirectory = Path.Combine(assetsDir, "ProjectF"); + var restoreDirectories = new string[] { projectDirectory, @@ -230,6 +254,7 @@ namespace Microsoft.DotNet.Migration.Tests } outputsIdentical.Should().BeTrue(); + VerifyAllMSBuildOutputsRunnable(projectDirectory); } @@ -258,7 +283,7 @@ namespace Microsoft.DotNet.Migration.Tests restoreDirectories); var outputsIdentical = outputComparisonData.ProjectJsonBuildOutputs - .SetEquals(outputComparisonData.MSBuildBuildOutputs); + .SetEquals(outputComparisonData.MSBuildBuildOutputs); if (!outputsIdentical) { @@ -278,11 +303,13 @@ namespace Microsoft.DotNet.Migration.Tests var projectDirectory = TestAssetsManager.CreateTestDirectory("Migration_outputs_error_when_no_projects_found"); string argstr = string.Empty; + string errorMessage = string.Empty; if (useGlobalJson) { var globalJsonPath = Path.Combine(projectDirectory.Path, "global.json"); + using (FileStream fs = File.Create(globalJsonPath)) { using (StreamWriter sw = new StreamWriter(fs)) @@ -294,11 +321,13 @@ namespace Microsoft.DotNet.Migration.Tests } argstr = globalJsonPath; + errorMessage = "Unable to find any projects in global.json"; } else { argstr = projectDirectory.Path; + errorMessage = $"No project.json file found in '{projectDirectory.Path}'"; } @@ -321,17 +350,19 @@ namespace Microsoft.DotNet.Migration.Tests .Where(s => Directory.EnumerateFiles(Path.GetDirectoryName(s), "*.csproj").Count() == 1) .Where(s => Path.GetFileName(Path.GetDirectoryName(s)).Contains("Project")) .Select(s => Path.GetFileName(Path.GetDirectoryName(s))); + migratedProjects.Should().BeEquivalentTo(expectedProjects); } - private MigratedBuildComparisonData GetDotnetNewComparisonData(string projectDirectory, string dotnetNewType) + private MigratedBuildComparisonData GetComparisonData(string projectDirectory) { - DotnetNew(projectDirectory, dotnetNewType); File.Copy("NuGet.tempaspnetpatch.config", Path.Combine(projectDirectory, "NuGet.Config")); + Restore(projectDirectory); var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory, Path.GetFileNameWithoutExtension(projectDirectory)); + return outputComparisonData; } @@ -358,7 +389,9 @@ namespace Microsoft.DotNet.Migration.Tests foreach (var dll in runnableDlls) { var assemblyName = AssemblyLoadContext.GetAssemblyName(dll); + var token = assemblyName.GetPublicKeyToken(); + token.Should().NotBeNullOrEmpty(); } } @@ -366,8 +399,10 @@ namespace Microsoft.DotNet.Migration.Tests private MigratedBuildComparisonData BuildProjectJsonMigrateBuildMSBuild(string projectDirectory, string projectName) { - return BuildProjectJsonMigrateBuildMSBuild(projectDirectory, projectName, - new [] { projectDirectory }, new [] { projectDirectory }); + return BuildProjectJsonMigrateBuildMSBuild(projectDirectory, + projectName, + new [] { projectDirectory }, + new [] { projectDirectory }); } private MigratedBuildComparisonData BuildProjectJsonMigrateBuildMSBuild(string projectDirectory, @@ -376,7 +411,9 @@ namespace Microsoft.DotNet.Migration.Tests string[] restoreDirectories) { BuildProjectJson(projectDirectory); + var projectJsonBuildOutputs = new HashSet(CollectBuildOutputs(projectDirectory)); + CleanBinObj(projectDirectory); // Remove lock file for migration @@ -386,6 +423,7 @@ namespace Microsoft.DotNet.Migration.Tests } MigrateProject(migrateArgs); + DeleteXproj(projectDirectory); foreach(var dir in restoreDirectories) @@ -424,6 +462,7 @@ namespace Microsoft.DotNet.Migration.Tests private void BuildProjectJson(string projectDirectory) { var projectFile = Path.Combine(projectDirectory, "project.json"); + var result = new BuildCommand(projectPath: projectFile) .ExecuteWithCapturedOutput(); @@ -438,21 +477,12 @@ namespace Microsoft.DotNet.Migration.Tests result.Should().Be(0); } - private void DotnetNew(string projectDirectory, string dotnetNewType) - { - new NewCommand().WithWorkingDirectory(projectDirectory) - .ExecuteWithCapturedOutput($"-t {dotnetNewType}") - .Should() - .Pass(); - } - private void Restore(string projectDirectory) { new TestCommand("dotnet") .WithWorkingDirectory(projectDirectory) .Execute("restore") - .Should() - .Pass(); + .Should().Pass(); } private void Restore3(string projectDirectory, string projectName=null) @@ -462,15 +492,13 @@ namespace Microsoft.DotNet.Migration.Tests if (projectName != null) { - command.Execute($"{projectName}.csproj") - .Should() - .Pass(); + command.Execute($"{projectName}.csproj /p:SkipInvalidConfigurations=true") + .Should().Pass(); } else { - command.Execute() - .Should() - .Pass(); + command.Execute("/p:SkipInvalidConfigurations=true") + .Should().Pass(); } } @@ -488,8 +516,7 @@ namespace Microsoft.DotNet.Migration.Tests .ExecuteWithCapturedOutput($"{projectName} /p:Configuration={configuration}"); result - .Should() - .Pass(); + .Should().Pass(); return result.StdOut; } @@ -497,6 +524,7 @@ namespace Microsoft.DotNet.Migration.Tests private void DeleteXproj(string projectDirectory) { var xprojFiles = Directory.EnumerateFiles(projectDirectory, "*.xproj"); + foreach (var xprojFile in xprojFiles) { File.Delete(xprojFile); @@ -511,23 +539,27 @@ namespace Microsoft.DotNet.Migration.Tests private void OutputDiagnostics(HashSet msbuildBuildOutputs, HashSet projectJsonBuildOutputs) { Console.WriteLine("Project.json Outputs:"); + Console.WriteLine(string.Join("\n", projectJsonBuildOutputs)); Console.WriteLine(""); Console.WriteLine("MSBuild Outputs:"); + Console.WriteLine(string.Join("\n", msbuildBuildOutputs)); } private class MigratedBuildComparisonData { public HashSet ProjectJsonBuildOutputs { get; } + public HashSet MSBuildBuildOutputs { get; } public MigratedBuildComparisonData(HashSet projectJsonBuildOutputs, HashSet msBuildBuildOutputs) { ProjectJsonBuildOutputs = projectJsonBuildOutputs; + MSBuildBuildOutputs = msBuildBuildOutputs; } } diff --git a/test/dotnet-new.Tests/GivenThatIWantANewCSApp.cs b/test/dotnet-new.Tests/GivenThatIWantANewCSApp.cs index b1fb921df..1d651c29e 100644 --- a/test/dotnet-new.Tests/GivenThatIWantANewCSApp.cs +++ b/test/dotnet-new.Tests/GivenThatIWantANewCSApp.cs @@ -4,10 +4,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; +using Microsoft.Build.Construction; using Microsoft.DotNet.Tools.Test.Utilities; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; using FluentAssertions; @@ -15,48 +13,49 @@ namespace Microsoft.DotNet.Tests { public class GivenThatIWantANewCSApp : TestBase { - [Fact] + [Fact(Skip="https://github.com/dotnet/cli/issues/4381")] public void When_NewtonsoftJson_dependency_added_Then_project_restores_and_runs() { - var rootPath = Temp.CreateDirectory().Path; - var projectJsonFile = Path.Combine(rootPath, "project.json"); + var rootPath = TestAssetsManager.CreateTestDirectory().Path; + var projectName = new DirectoryInfo(rootPath).Name; + var projectFile = Path.Combine(rootPath, $"{projectName}.csproj"); new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute("new"); - AddProjectJsonDependency(projectJsonFile, "Newtonsoft.Json", "7.0.1"); + AddProjectDependency(projectFile, "Newtonsoft.Json", "7.0.1"); new TestCommand("dotnet") { WorkingDirectory = rootPath } - .Execute("restore") + .Execute("restore3 /p:SkipInvalidConfigurations=true") .Should().Pass(); new TestCommand("dotnet") { WorkingDirectory = rootPath } - .Execute("run") + .Execute("run3") .Should().Pass(); } [Fact] - public void When_dotnet_build_is_invoked_Then_project_builds_without_warnings() + public void When_dotnet_build_is_invoked_Then_app_builds_without_warnings() { - var rootPath = Temp.CreateDirectory().Path; + var rootPath = TestAssetsManager.CreateTestDirectory().Path; new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute("new"); new TestCommand("dotnet") { WorkingDirectory = rootPath } - .Execute("restore"); + .Execute("restore3 /p:SkipInvalidConfigurations=true"); var buildResult = new TestCommand("dotnet") { WorkingDirectory = rootPath } - .ExecuteWithCapturedOutput("build"); + .ExecuteWithCapturedOutput("build3"); - buildResult.Should().Pass(); - buildResult.Should().NotHaveStdErr(); + buildResult.Should().Pass() + .And.NotHaveStdErr(); } [Fact] public void When_dotnet_new_is_invoked_mupliple_times_it_should_fail() { - var rootPath = Temp.CreateDirectory().Path; + var rootPath = TestAssetsManager.CreateTestDirectory().Path; new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute("new"); @@ -70,40 +69,17 @@ namespace Microsoft.DotNet.Tests Assert.Equal(expectedState, actualState); - result.Should().Fail(); - result.Should().HaveStdErr(); + result.Should().Fail() + .And.HaveStdErr(); } - private static void AddProjectJsonDependency(string projectJsonPath, string dependencyId, string dependencyVersion) + private static void AddProjectDependency(string projectFilePath, string dependencyId, string dependencyVersion) { - var projectJsonRoot = ReadProject(projectJsonPath); + var projectRootElement = ProjectRootElement.Open(projectFilePath); - var dependenciesNode = projectJsonRoot - .Descendants() - .OfType() - .First(p => p.Name == "dependencies"); + projectRootElement.AddItem("PackageReference", dependencyId, new Dictionary{{"Version", dependencyVersion}}); - ((JObject)dependenciesNode.Value).Add(new JProperty(dependencyId, dependencyVersion)); - - WriteProject(projectJsonRoot, projectJsonPath); - } - - private static JObject ReadProject(string projectJsonPath) - { - using (TextReader projectFileReader = File.OpenText(projectJsonPath)) - { - var projectJsonReader = new JsonTextReader(projectFileReader); - - var serializer = new JsonSerializer(); - return serializer.Deserialize(projectJsonReader); - } - } - - private static void WriteProject(JObject projectRoot, string projectJsonPath) - { - string projectJson = JsonConvert.SerializeObject(projectRoot, Formatting.Indented); - - File.WriteAllText(projectJsonPath, projectJson + Environment.NewLine); + projectRootElement.Save(); } } } diff --git a/test/dotnet-new.Tests/GivenThatIWantANewCSLIbrary.cs b/test/dotnet-new.Tests/GivenThatIWantANewCSLIbrary.cs index d580ea0ca..7328c8838 100644 --- a/test/dotnet-new.Tests/GivenThatIWantANewCSLIbrary.cs +++ b/test/dotnet-new.Tests/GivenThatIWantANewCSLIbrary.cs @@ -15,44 +15,37 @@ namespace Microsoft.DotNet.Tests { public class GivenThatIWantANewCSLibrary : TestBase { - [Fact] public void When_library_created_Then_project_restores() { - var rootPath = Temp.CreateDirectory().Path; - var projectJsonFile = Path.Combine(rootPath, "project.json"); + var rootPath = TestAssetsManager.CreateTestDirectory().Path; new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute("new --type lib") - .Should() - .Pass(); + .Should().Pass(); new TestCommand("dotnet") { WorkingDirectory = rootPath } - .Execute("restore") + .Execute("restore3 /p:SkipInvalidConfigurations=true") .Should().Pass(); } [Fact] - public void When_dotnet_build_is_invoked_Then_project_builds_without_warnings() + public void When_dotnet_build_is_invoked_Then_library_builds_without_warnings() { - var rootPath = Temp.CreateDirectory().Path; + var rootPath = TestAssetsManager.CreateTestDirectory().Path; new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute("new --type lib"); new TestCommand("dotnet") { WorkingDirectory = rootPath } - .Execute("restore"); + .Execute("restore3 /p:SkipInvalidConfigurations=true"); var buildResult = new TestCommand("dotnet") .WithWorkingDirectory(rootPath) - .ExecuteWithCapturedOutput("build") - .Should() - .Pass() - .And - .NotHaveStdErr(); + .ExecuteWithCapturedOutput("build3") + .Should().Pass() + .And.NotHaveStdErr(); } - - } } diff --git a/test/dotnet-new.Tests/MSBuild.exe b/test/dotnet-new.Tests/MSBuild.exe new file mode 100644 index 000000000..2b4d0f999 --- /dev/null +++ b/test/dotnet-new.Tests/MSBuild.exe @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 \ No newline at end of file diff --git a/test/dotnet-new.Tests/MSBuild.exe.config b/test/dotnet-new.Tests/MSBuild.exe.config new file mode 100644 index 000000000..2b4d0f999 --- /dev/null +++ b/test/dotnet-new.Tests/MSBuild.exe.config @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 \ No newline at end of file diff --git a/test/dotnet-new.Tests/project.json b/test/dotnet-new.Tests/project.json index 8a67276d7..0e8ae3f83 100644 --- a/test/dotnet-new.Tests/project.json +++ b/test/dotnet-new.Tests/project.json @@ -8,10 +8,10 @@ "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - "Newtonsoft.Json": "9.0.1", "dotnet": { "target": "project" }, + "Microsoft.Build": "0.1.0-preview-00034-160909", "xunit": "2.2.0-beta3-build3330", "dotnet-test-xunit": "1.0.0-rc2-350904-49" }, diff --git a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs index 3b6912986..5a2012847 100644 --- a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs +++ b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs @@ -28,6 +28,7 @@ namespace Microsoft.DotNet.Tests .WithWorkingDirectory(testDirectory.Path); command.Environment["NUGET_PACKAGES"] = testNugetCache; command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = ""; + command.Environment["SkipInvalidConfigurations"] = "true"; _firstDotnetNonVerbUseCommandResult = command.ExecuteWithCapturedOutput("--info"); _firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new");