2-6-3 Utiliser SendAsync pour soumettre un formulaire
À la dernière étape, on a utilisé AngleSharp
pour extraire de la réponse le jeton CSRF
du formulaire et l'envoyer avec le dictionnaire représentant le formulaire.
On peut aller plus loin et laisser le soin à la librairie de faire l'envoi complet du formulaire, qui contiendra automatiquement la protection CSRF
.
Microsoft propose une fonction d'extension SendAsync
sur le HttpClient
que nous allons utiliser (ref.: https://github.com/dotnet/AspNetCore.Docs.Samples/blob/main/test/integration-tests/IntegrationTestsSample/tests/RazorPagesProject.Tests/Helpers/HttpClientExtensions.cs).
Ajouter HttpClientExtensions
- Toujours dans le dossier
Helpers
du projetSnowfall.Tests
->Add
->Class/Interface
- Nommez la classe
HttpClientExtensions
et ajoutez-y le code suivant:Snowfall.Tests/Helpers/HttpClientExtensions.cspublic static class HttpClientExtensions
{
public static Task<HttpResponseMessage> SendAsync(
this HttpClient client,
IHtmlFormElement form,
IHtmlElement submitButton)
{
return client.SendAsync(form, submitButton, new Dictionary<string, string>());
}
public static Task<HttpResponseMessage> SendAsync(
this HttpClient client,
IHtmlFormElement form,
IEnumerable<KeyValuePair<string, string>> formValues)
{
var submitElement = Assert.Single(form.QuerySelectorAll("[type=submit]"));
var submitButton = Assert.IsAssignableFrom<IHtmlElement>(submitElement);
return client.SendAsync(form, submitButton, formValues);
}
public static Task<HttpResponseMessage> SendAsync(
this HttpClient client,
IHtmlFormElement form,
IHtmlElement submitButton,
IEnumerable<KeyValuePair<string, string>> formValues)
{
foreach (var kvp in formValues)
{
var element = Assert.IsAssignableFrom<IHtmlInputElement>(form[kvp.Key]);
element.Value = kvp.Value;
}
var submit = form.GetSubmission(submitButton);
var target = (Uri)submit.Target;
if (submitButton.HasAttribute("formaction"))
{
var formaction = submitButton.GetAttribute("formaction");
target = new Uri(formaction, UriKind.Relative);
}
var submission = new HttpRequestMessage(new HttpMethod(submit.Method.ToString()), target)
{
Content = new StreamContent(submit.Body)
};
foreach (var header in submit.Headers)
{
submission.Headers.TryAddWithoutValidation(header.Key, header.Value);
submission.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return client.SendAsync(submission);
}
}
Utiliser SendAsync
pour soumettre le formulaire de connexion
La méthode d'extension du client HTTP prend en paramètre:
IHtmlFormElement
soit un élément de formulaireIHtmlButtonElement
soit le bouton pour soumettre le formulaireIEnumerable<KeyValuePair<string,string>>
soit le dictionnaire de valeur pour remplir le formulaire
La fonction s'occupera d'extraire l'URL d'action du formulaire, le jeton CSRF
et de soumettre le tout avec les valeurs passées en paramètre.
Le test peut donc devenir:
[Fact]
public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
{
// Arrange
var client = Application.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
reponse.EnsureSuccessStatusCode();
var connexionViewModelDictionary = new Dictionary<string, string>
{
{ "Email", "u@ser.com" },
{ "Password", "!User122432" }
};
// Act
IHtmlDocument pageConnexion = await HtmlHelpers.GetDocumentAsync(reponse);
reponse = await client.SendAsync(
(pageConnexion.QuerySelector("main form") as IHtmlFormElement)!,
(pageConnexion.QuerySelector("main form button.btn-primary") as IHtmlButtonElement)!,
connexionViewModelDictionary);
// Assert
Assert.Equal(HttpStatusCode.Redirect, reponse.StatusCode);
Assert.Equal("/", reponse.Headers.Location?.OriginalString);
reponse = await client.GetAsync(reponse.Headers.Location.ToString());
Assert.Contains("Bonjour, Uti", await reponse.Content.ReadAsStringAsync());
}