Aller au contenu principal

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

  1. Toujours dans le dossier Helpers du projet Snowfall.Tests -> Add -> Class/Interface
  2. Nommez la classe HttpClientExtensions et ajoutez-y le code suivant:
    Snowfall.Tests/Helpers/HttpClientExtensions.cs
    public 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 formulaire
  • IHtmlButtonElement soit le bouton pour soumettre le formulaire
  • IEnumerable<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:

Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
[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());
}