Session.Set not saving ViewModel past function (only with playwright).

Alain Schaerer 0 Reputation points
2024-02-06T14:10:54.9966667+00:00

The following problem only occurs when the web page is used by playwright (C#):

The web page contains a radio button that lets the user change the marital status of himself. When the radio button is used, the viewmodel is updated via session data (Session.Set). There is also a "Save" button to ultimately save the data in the database.
When using playwright, the radio button correctly sets the viewmodel to have the correct marital status, but as soon as the save button is clicked (post request), the updated data is set back to the old data.

Here is the backend code that saves the viewmodel in the session:

        public async Task<JsonResult> OnPostUpdateMaritalStatus(string maritalStatus)         {             try             {                 var vm = GetSessionData();                 vm.MaritalStatus = maritalStatus;                 SaveSessionData(vm);                 vm = GetSessionData(); // This is to test if SaveSessionData does anything. It seems to save it here correctly, but the data is lost in OnPostSaveBeneficiariesAsync()/                 await HttpContext.Session.CommitAsync(); // Hasn't yielded any results.                 return new JsonResult("Pass");             }             catch (Exception)             {                 return new JsonResult("Fail");                 throw;             }         } 

Here is "SaveSessionData":

        private void SaveSessionData(BeneficiaryViewModel viewModel)         {             HttpContext.Session.Set(SessionString, viewModel);         } 

Here is "OnPostSaveAsync" which is called after pressing the save button:

        public async Task<IActionResult> OnPostSaveBeneficiariesAsync()         {             string aeId = HttpContext.Session.GetString(ParticipantSessionStrings.AccountEnrollmentId);             var viewModel = GetSessionData();              if (!string.IsNullOrWhiteSpace(viewModel.MaritalStatus))             {                 if (viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds).Equals(100) && !viewModel.TableData.Any(x => x.Type == "S"))                 {                      if (viewModel.MaritalStatus.Equals("Y", StringComparison.OrdinalIgnoreCase) && viewModel.TableData.FirstOrDefault(x => x.BeneRelationship == Relationships.Spouse)?.Beneproceeds != 100m)                     {                         BeneficiaryCurrentStep = BeneficiaryWizardSteps.SpousalConsent;                         SetTransactionInfo(viewModel);                         return RedirectToPage();                     }                      BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                     SetTransactionInfo(viewModel);                 }                 else if (viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds).Equals(100) && viewModel.TableData.Where(x => x.Type == "S").Sum(x => x.Beneproceeds).Equals(100))                 {                     if (viewModel.MaritalStatus.Equals("Y", StringComparison.OrdinalIgnoreCase) && viewModel.TableData.FirstOrDefault(x => x.BeneRelationship == Relationships.Spouse)?.Beneproceeds != 100m)                     {                         BeneficiaryCurrentStep = BeneficiaryWizardSteps.SpousalConsent;                         SetTransactionInfo(viewModel);                         return RedirectToPage();                     }                      BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                     SetTransactionInfo(viewModel);                 }                 else                 {                     BeneficiaryCurrentStep = BeneficiaryWizardSteps.DataEntry;                     if (viewModel.TableData.Any(x => x.Type == "P") && viewModel.TableData.Any(x => x.Type == "S"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = String.Format(_localizer["Beneficiary_Primary_Secondary"], viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds), viewModel.TableData.Where(x => x.Type == "S").Sum(x => x.Beneproceeds)) });                     }                      else if (viewModel.TableData.Any(x => x.Type == "P"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = String.Format(_localizer["Beneficiary_Total"], viewModel.TableData.Sum(x => x.Beneproceeds)) });                     }                      else if (viewModel.TableData.Any(x => x.Type == "S") && !viewModel.TableData.Any(x => x.Type == "P"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = _localizer["Beneficiary_None"] });                     }                      else                     {                         BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                         SetTransactionInfo(viewModel);                     }                 }             }             else if (!_planService.PlanRequiresSpouseAsBeneficiary(_accountsProvidedId))             {                 if (viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds).Equals(100) && !viewModel.TableData.Any(x => x.Type == "S"))                 {                     BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                     SetTransactionInfo(viewModel);                 }                 else if (viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds).Equals(100) && viewModel.TableData.Where(x => x.Type == "S").Sum(x => x.Beneproceeds).Equals(100))                 {                     BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                     SetTransactionInfo(viewModel);                 }                 else                 {                     BeneficiaryCurrentStep = BeneficiaryWizardSteps.DataEntry;                     if (viewModel.TableData.Any(x => x.Type == "P") && viewModel.TableData.Any(x => x.Type == "S"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = String.Format(_localizer["Beneficiary_Primary_Secondary"], viewModel.TableData.Where(x => x.Type == "P").Sum(x => x.Beneproceeds), viewModel.TableData.Where(x => x.Type == "S").Sum(x => x.Beneproceeds)) });                     }                      else if (viewModel.TableData.Any(x => x.Type == "P"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = String.Format(_localizer["Beneficiary_Total"], viewModel.TableData.Sum(x => x.Beneproceeds)) });                     }                      else if (viewModel.TableData.Any(x => x.Type == "S") && !viewModel.TableData.Any(x => x.Type == "P"))                     {                         TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = _localizer["Beneficiary_None"] });                     }                      else                     {                         BeneficiaryCurrentStep = BeneficiaryWizardSteps.Summary;                         SetTransactionInfo(viewModel);                     }                 }             }             else             {                 TempData["UserMessage"] = JsonConvert.SerializeObject(new AlertViewModel() { Title = _localizer["Beneficiary_Error"], Message = _localizer["Beneficiary_Marital"] });             }             await HttpContext.Session.CommitAsync();             return RedirectToPage();         } 

In "OnPostSaveAsync()", the "GetSessionData()" function retrieves the viewmodel.
Here is "GetSessionData()":

        private BeneficiaryViewModel GetSessionData()         {             return HttpContext.Session.Get<BeneficiaryViewModel>(SessionString);         }  

Here is the playwright code, which selects the radio button with checkmarks[0].ClickAsync() and clicks the save button with await page.Locator("input[id='save-changes-btn'].First.ClickAsync() Interestingly enough, if I add delays (commented out) in the playwright code, it works. So it seems as if it is a timing issue, but the web app should be resillient enough to serve a very fast user.

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,400 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,417 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 61,731 Reputation points
    2024-02-06T16:48:09.4866667+00:00

    you don't show the code, but my guess is you are using javascript and ajax to update session. most likely your playwright code is clicking the button before the javascript has loaded and hooked up events. it common in javascript to hookup events on the dom load event.

    this is a common issue html testers, and the fix is often just adding a delay. you can remove the delay requirement by moving the javascript includes to the <head> (may impact page load times), then have the element hookup the event, rather than javascript.

    <input type="radio" onclick="do click()" ...>