Add functional test of `[Remote]`

- #439 (3 of 3)
- extended the Validation web site to include use of `[Remote]`
- also confirm operation of the validation actions (which all reject their input)
This commit is contained in:
Doug Bunting 2015-01-28 09:59:40 -08:00
parent 80b004678d
commit 1a62f104b4
16 changed files with 669 additions and 0 deletions

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create in Aria area. - My ASP.NET Application</title>
</head>
<body>
<div>
<div>
<div>
<a href="/">Validation web site</a>
</div>
<div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/Aria">Aria Home</a></li>
</ul>
</div>
</div>
</div>
<div>
<h2>Create in Aria area.</h2>
<form action="/Aria/RemoteAttribute_Home/Create" method="post"> <div class="form-horizontal">
<h4>Person</h4>
<hr />
<div class="form-group">
<label class="control-label col-md-2" for="Id">Id</label>
<div class="col-md-10">
<input class="form-control text-box single-line" id="Id" name="Id" type="number" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="Id" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId1">UserId1</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId1&#39; is invalid." data-val-remote-additionalfields="*.UserId1" data-val-remote-url="/Aria/RemoteAttribute_Verify/IsIdAvailable" id="UserId1" name="UserId1" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId1" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId2">UserId2</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId2&#39; is invalid." data-val-remote-additionalfields="*.UserId2" data-val-remote-type="Post" data-val-remote-url="/RemoteAttribute_Verify/IsIdAvailable" id="UserId2" name="UserId2" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId2" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId3">UserId3</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="/Aria/RemoteAttribute_Verify/IsIdAvailable rejects you." data-val-remote-additionalfields="*.UserId3" data-val-remote-url="/Aria/RemoteAttribute_Verify/IsIdAvailable" id="UserId3" name="UserId3" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId3" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId4">UserId4</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId4&#39; is invalid." data-val-remote-additionalfields="*.UserId4,*.UserId1,*.UserId2,*.UserId3" data-val-remote-url="/AnotherAria/RemoteAttribute_Verify/IsIdAvailable" id="UserId4" name="UserId4" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId4" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a href="/Aria">Go back to home</a>
</div>
</div>
<script src="/lib/jquery/js/jquery.js"></script>
<script src="/lib/jquery-validation/jquery.validate.js"></script>
<script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create in root area. - My ASP.NET Application</title>
</head>
<body>
<div>
<div>
<div>
<a href="/">Validation web site</a>
</div>
<div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/Aria">Aria Home</a></li>
</ul>
</div>
</div>
</div>
<div>
<h2>Create in root area.</h2>
<form action="/RemoteAttribute_Home/Create" method="post"> <div class="form-horizontal">
<h4>Person</h4>
<hr />
<div class="form-group">
<label class="control-label col-md-2" for="Id">Id</label>
<div class="col-md-10">
<input class="form-control text-box single-line" id="Id" name="Id" type="number" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="Id" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId1">UserId1</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId1&#39; is invalid." data-val-remote-additionalfields="*.UserId1" data-val-remote-url="/RemoteAttribute_Verify/IsIdAvailable" id="UserId1" name="UserId1" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId1" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId2">UserId2</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId2&#39; is invalid." data-val-remote-additionalfields="*.UserId2" data-val-remote-type="Post" data-val-remote-url="/RemoteAttribute_Verify/IsIdAvailable" id="UserId2" name="UserId2" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId2" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId3">UserId3</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="/Aria/RemoteAttribute_Verify/IsIdAvailable rejects you." data-val-remote-additionalfields="*.UserId3" data-val-remote-url="/Aria/RemoteAttribute_Verify/IsIdAvailable" id="UserId3" name="UserId3" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId3" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="UserId4">UserId4</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-remote="&#39;UserId4&#39; is invalid." data-val-remote-additionalfields="*.UserId4,*.UserId1,*.UserId2,*.UserId3" data-val-remote-url="/AnotherAria/RemoteAttribute_Verify/IsIdAvailable" id="UserId4" name="UserId4" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="UserId4" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a href="/">Go back to home</a>
</div>
</div>
<script src="/lib/jquery/js/jquery.js"></script>
<script src="/lib/jquery-validation/jquery.validate.js"></script>
<script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>

View File

@ -0,0 +1,103 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class RemoteAttributeValidationTest
{
private static readonly Assembly _resourcesAssembly =
typeof(RemoteAttributeValidationTest).GetTypeInfo().Assembly;
private readonly IServiceProvider _provider = TestHelper.CreateServices(nameof(ValidationWebSite));
private readonly Action<IApplicationBuilder> _app = new ValidationWebSite.Startup().Configure;
[Theory]
[InlineData("Aria", "/Aria")]
[InlineData("Root", "")]
public async Task RemoteAttribute_LeadsToExpectedValidationAttributes(string areaName, string pathSegment)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var expectedContent = await _resourcesAssembly.ReadResourceAsStringAsync(
"compiler/resources/ValidationWebSite." + areaName + ".RemoteAttribute_Home.Create.html");
var url = "http://localhost" + pathSegment + "/RemoteAttribute_Home/Create";
// Act
var response = await client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/html", response.Content.Headers.ContentType.MediaType);
Assert.Equal("utf-8", response.Content.Headers.ContentType.CharSet);
var responseContent = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedContent, responseContent);
}
[Theory]
[InlineData("", "\"/RemoteAttribute_Verify/IsIdAvailable rejects Joe1.\"")]
[InlineData("/Aria", "false")]
[InlineData("/AnotherAria",
"\"/AnotherAria/RemoteAttribute_Verify/IsIdAvailable rejects 'Joe4' with 'Joe1', 'Joe2', and 'Joe3'.\"")]
public async Task RemoteAttribute_VerificationAction_GetReturnsExpectedJson(
string pathSegment,
string expectedContent)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var url = "http://localhost" + pathSegment +
"/RemoteAttribute_Verify/IsIdAvailable?UserId1=Joe1&UserId2=Joe2&UserId3=Joe3&UserId4=Joe4";
// Act
var response = await client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
Assert.Equal("utf-8", response.Content.Headers.ContentType.CharSet);
var responseContent = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedContent, responseContent);
}
[Theory]
[InlineData("", "\"/RemoteAttribute_Verify/IsIdAvailable rejects Jane1.\"")]
[InlineData("/Aria", "false")]
public async Task RemoteAttribute_VerificationAction_PostReturnsExpectedJson(
string pathSegment,
string expectedContent)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var url = "http://localhost" + pathSegment + "/RemoteAttribute_Verify/IsIdAvailable";
var contentDictionary = new Dictionary<string, string>
{
{ "UserId1", "Jane1" },
{ "UserId2", "Jane2" },
{ "UserId3", "Jane3" },
{ "UserId4", "Jane4" },
};
var content = new FormUrlEncodedContent(contentDictionary);
// Act
var response = await client.PostAsync(url, content);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
Assert.Equal("utf-8", response.Content.Headers.ContentType.CharSet);
var responseContent = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedContent, responseContent);
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
using ValidationWebSite.Models;
namespace ValidationWebSite.AnotherArea.Controllers
{
[Area("AnotherAria")]
[Route("[Area]/[Controller]/[Action]", Order = -2)]
public class RemoteAttribute_VerifyController : Controller
{
// Demonstrates validation action when AdditionalFields causes client to send multiple values.
[HttpGet]
public IActionResult IsIdAvailable(Person person)
{
return Json(data: string.Format(
"/AnotherAria/RemoteAttribute_Verify/IsIdAvailable rejects '{0}' with '{1}', '{2}', and '{3}'.",
person.UserId4,
person.UserId1,
person.UserId2,
person.UserId3));
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
using ValidationWebSite.Models;
namespace ValidationWebSite.Controllers
{
public class RemoteAttribute_HomeController : Controller
{
private static Person _person;
[HttpGet]
[Route("[Controller]/[Action]")]
public IActionResult Create()
{
return View();
}
[HttpPost]
[Route("[Controller]/[Action]")]
public IActionResult Create(Person person)
{
ModelState.Remove("id");
if (!ModelState.IsValid)
{
return View(person);
}
_person = person;
return RedirectToAction(nameof(Details));
}
[Route("", Name = "Home", Order = -1)]
[Route("[Controller]/Index")]
public IActionResult Details()
{
return View(_person);
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ValidationWebSite.Controllers
{
[Route("[Controller]/[Action]")]
public class RemoteAttribute_VerifyController : Controller
{
// This action is overloaded and may receive requests to validate either UserId1 or UserId2.
[AcceptVerbs("Get", "Post")]
public IActionResult IsIdAvailable(string userId1, string userId2)
{
return Json(data: string.Format("/RemoteAttribute_Verify/IsIdAvailable rejects {0}.", userId1 ?? userId2));
}
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ValidationWebSite.Models
{
public class Person
{
public int Id { get; set; }
// Controller in current area.
[Remote("IsIdAvailable", "RemoteAttribute_Verify")]
public string UserId1 { get; set; }
// Controller in root area.
[Remote("IsIdAvailable", "RemoteAttribute_Verify", null, HttpMethod = "Post")]
public string UserId2 { get; set; }
// Controller in MyArea area.
[Remote(
"IsIdAvailable",
"RemoteAttribute_Verify",
"Aria",
ErrorMessage = "/Aria/RemoteAttribute_Verify/IsIdAvailable rejects you.")]
public string UserId3 { get; set; }
// Controller in AnotherArea area.
[Remote(
"IsIdAvailable",
"RemoteAttribute_Verify",
"AnotherAria",
AdditionalFields = "UserId1, UserId2, UserId3")]
public string UserId4 { get; set; }
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
using ValidationWebSite.Models;
namespace ValidationWebSite.MyArea.Controllers
{
[Area("Aria")]
[Route("[Area]/[Controller]/[Action]", Order = -2)]
public class RemoteAttribute_HomeController : Controller
{
private static Person _person;
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Person person)
{
ModelState.Remove("id");
if (!ModelState.IsValid)
{
return View(person);
}
_person = person;
return RedirectToAction(nameof(Details));
}
[Route("/[Area]", Name = "AriaHome", Order = -3)]
[Route("/[Area]/[Controller]/Index", Order = -2)]
public IActionResult Details()
{
return View(_person);
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace ValidationWebSite.MyArea.Controllers
{
[Area("Aria")]
public class RemoteAttribute_VerifyController : Controller
{
// This action is overloaded and may receive requests to validate either UserId1 or UserId3.
// Demonstrates use of the default error message.
[AcceptVerbs("Get", "Post")]
[Route("[Area]/[Controller]/[Action]", Order = -2)]
public IActionResult IsIdAvailable(string userId1, string userId3)
{
return Json(data: false);
}
}
}

View File

@ -12,6 +12,9 @@ namespace ValidationWebSite
{
var configuration = app.GetTestConfiguration();
// Set up file serving for JavaScript files.
app.UseFileServer();
// Set up application services
app.UseServices(services =>
{

View File

@ -0,0 +1,75 @@
@model ValidationWebSite.Models.Person
@{
object areaObject;
ViewContext.ActionDescriptor.RouteValueDefaults.TryGetValue("area", out areaObject);
var areaName = (areaObject as string) ?? "root";
ViewBag.Title = "Create in " + areaName + " area.";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm())
{
<div class="form-horizontal">
<h4>Person</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.Id, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Id, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Id, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.UserId1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserId1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserId1, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.UserId2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserId2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserId2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.UserId3, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserId3, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserId3, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.UserId4, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserId4, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserId4, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Go back to home", "Details")
</div>
@section Scripts {
@* Until script helpers are available, add script references manually. *@
@* @Scripts.Render("~/bundles/jqueryval") *@
<script src="@Url.Content("~/lib/jquery-validation/jquery.validate.js")"></script>
<script src="@Url.Content("~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js")"></script>
}

View File

@ -0,0 +1,76 @@
@model ValidationWebSite.Models.Person
@{
object areaObject;
ViewContext.ActionDescriptor.RouteValueDefaults.TryGetValue("area", out areaObject);
var areaName = (areaObject as string) ?? "root";
ViewBag.Title = "Details in " + areaName + " area.";
}
<h2>@ViewBag.Title</h2>
<div>
<h4>Person</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Id)
</dt>
<dd>
@Html.DisplayFor(model => model.Id)
</dd>
</dl>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.UserId1)
</dt>
<dd>
@Html.DisplayFor(model => model.UserId1)
</dd>
</dl>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.UserId2)
</dt>
<dd>
@Html.DisplayFor(model => model.UserId2)
</dd>
</dl>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.UserId3)
</dt>
<dd>
@Html.DisplayFor(model => model.UserId3)
</dd>
</dl>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.UserId4)
</dt>
<dd>
@Html.DisplayFor(model => model.UserId4)
</dd>
</dl>
</div>
<p>
@Html.ActionLink("Create a new one", "Create") |
@if (string.Equals("Aria", areaName, StringComparison.OrdinalIgnoreCase))
{
@Html.RouteLink("Go back to home", "Home")
}
else
{
@Html.RouteLink("Go to Aria home", "AriaHome")
}
</p>

View File

@ -0,0 +1,7 @@
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Error";
}
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
</head>
<body>
<div>
<div>
<div>
@Html.ActionLink("Validation web site", "Details", "RemoteAttribute_Home", routeValues: new { area = (object)null })
</div>
<div>
<ul>
<li>@Html.ActionLink("Home", "Details", "RemoteAttribute_Home", routeValues: new { area = string.Empty })</li>
<li>@Html.ActionLink("Aria Home", "Details", "RemoteAttribute_Home", routeValues: new { area = "Aria" })</li>
</ul>
</div>
</div>
</div>
<div>
@RenderBody()
</div>
<script src="~/lib/jquery/js/jquery.js"></script>
@RenderSection("scripts", required: false)
</body>
</html>

View File

@ -0,0 +1,3 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}