Test Automation Using Atata: Handle Confirmation Popups

0
23

Introduction

In this article, I would like to show how to easily handle confirmation popups using Atata C#/.NET web UI test automation framework. I will handle: JS confirm, Bootstrap modal and jQuery Confirm box. The ideas, covered in the article, can be applied to any other kinds of popups.

Confirmation popup

Atata Series

  1. Atata – New Test Automation Framework
  2. Test Automation Using Atata: Verification of Web Pages
  3. Test Automation Using Atata: Verification of Validation Messages
  4. Test Automation Using Atata: Visual Studio Team Services Configuration
  5. Test Automation Using Atata: Handle Confirmation Popups

Background

It is recommended to read Atata – New Test Automation Framework article to get familiar with basic Atata Framework concepts.

Sample Page

For testing purposes of this article, I created the following test page: https://atata-framework.github.io/atata-sample-app/#!/products.

Products page

The page contains a table of items. Each item has 3 different delete buttons that confirm deletion via appropriate popups: JS confirm, Bootstrap Modal and jquery-confirm.

Set Up Test Project

First of all, let’s configure the testing environment. We need to create Atata UI Tests project.

  1. In Visual Studio create a project for Atata automated testing using the guide.
  2. Add a reference to Atata.Bootstrap NuGet package. It contains BSModal component that will be needed later.

JS Confirm

Let’s start with the simplest JS confirm popup. It’s being used rarely nowadays, but anyway it can be met on the web.

Create ProductsPage page object class. Define the table with specific ProductTableRow sub-class, which describes product item row.

using Atata;

namespace AtataSamples.ConfirmationPopups
{
 using _ = ProductsPage;

 [Url("products")]
 public class ProductsPage : Page<_>
 {
 public Table<ProductTableRow, _> Products { get; private set; }

 public class ProductTableRow : TableRow<_>
 {
 public Text<_> Name { get; private set; }

 public Currency<_> Price { get; private set; }

 public Number<_> Amount { get; private set; }

 [CloseConfirmBox]
 public ButtonDelegate<_> DeleteUsingJSConfirm { get; private set; }
 }
 }
}

The popup closing functionality is bound to DeleteUsingJSConfirm button with [CloseConfirmBox] Atata attribute which does the magic. Check CloseConfirmBoxAttribute sources if you are interested in what is going on behind the scene.

Create ProductTests test class inherited from UITestFixture with the test:

using Atata;
using NUnit.Framework;

namespace AtataSamples.ConfirmationPopups
{
 public class ProductTests : UITestFixture
 {
 [Test]
 public void Products_DeleteUsingJSConfirm()
 {
 int count;

 Go.To<ProductsPage>().
 Products.Rows.Count.Get(out count).

 Products.Rows[x => x.Name == "Table"].DeleteUsingJSConfirm().
 Products.Rows[x => x.Name == "Table"].Should.Not.Exist().
 Products.Rows.Count.Should.Equal(count - 1);
 }
 }
}

The test does:

  1. Rrecords products count to a variable.
  2. Deletes product with confirmation.
  3. Verifies that the product does not exist any more in the list.
  4. Verifies products count to be less by one.

Bootstrap Modal

Now let’s get to Bootstrap modal confirmation popup.

Bootstrap modal

Create DeletionConfirmationBSModal<TNavigateTo> page object class inherited from BSModal (defined in Atata.Bootstrap package). TNavigateTo is a type of parent page object, where to navigate to after the popup is closed.

using Atata;
using Atata.Bootstrap;

namespace AtataSamples.ConfirmationPopups
{
 [Name("Deletion Confirmation")]
 [WindowTitle("Confirmation")]
 public class DeletionConfirmationBSModal<TNavigateTo> : BSModal<DeletionConfirmationBSModal<TNavigateTo>>
 where TNavigateTo : PageObject<TNavigateTo>
 {
 public ButtonDelegate<TNavigateTo, DeletionConfirmationBSModal<TNavigateTo>> Delete { get; private set; }

 public ButtonDelegate<TNavigateTo, DeletionConfirmationBSModal<TNavigateTo>> Cancel { get; private set; }
 }
}

Add a property to ProductTableRow:

public class ProductTableRow : TableRow<_>
{
 

 [FindByContent("Delete Using BS Modal")]
 public ButtonDelegate<DeletionConfirmationBSModal<_>, _> DeleteUsingBSModal { get; private set; }
}

In ProductTests class implement a test:

[Test]
public void Products_DeleteUsingBSModal()
{
 int count;

 Go.To<ProductsPage>().
 Products.Rows.Count.Get(out count).

 Products.Rows[x => x.Name == "Chair"].DeleteUsingBSModal().
 Cancel(). 
 Products.Rows[x => x.Name == "Chair"].Should.Exist().
 Products.Rows.Count.Should.Equal(count).

 Products.Rows[x => x.Name == "Chair"].DeleteUsingBSModal().
 Delete(). 
 Products.Rows[x => x.Name == "Chair"].Should.Not.Exist().
 Products.Rows.Count.Should.Equal(count - 1);
}

The test does:

  1. Rrecords products count to a variable.
  2. Tries to delete product and clicks ‘Cancel’.
  3. Verifies that the product still exists.
  4. Tries to delete product and clicks ‘Delete’.
  5. Verifies that the product does not exist any more in the list.
  6. Verifies products count to be less by one.

Handle Bootstrap Modal Via Trigger

Another option to close simple popups is to use a custom trigger. Let’s create one.

using Atata;

namespace AtataSamples.ConfirmationPopups
{
 public class ConfirmDeletionViaBSModalAttribute : TriggerAttribute
 {
 public ConfirmDeletionViaBSModalAttribute(TriggerEvents on = TriggerEvents.AfterClick, TriggerPriority priority = TriggerPriority.Medium)
 : base(on, priority)
 {
 }

 protected override void Execute<TOwner>(TriggerContext<TOwner> context)
 {
 Go.To<DeletionConfirmationBSModal<TOwner>>(temporarily: true).
 Delete();
 }
 }
}

The trigger, after the button is clicked, navigates to deletion confirmation modal and clicks ‘Delete’.

Now we need to bind this trigger to a button. Add a property to ProductTableRow:

public class ProductTableRow : TableRow<_>
{
 
 
 [FindByContent("Delete Using BS Modal")]
 [ConfirmDeletionViaBSModal]
 public ButtonDelegate<_> DeleteUsingBSModalViaTrigger { get; private set; }
}

And implement a test:

[Test]
public void Products_DeleteUsingBSModal_ViaTrigger()
{
 int count;

 Go.To<ProductsPage>().
 Products.Rows.Count.Get(out count).

 Products.Rows[x => x.Name == "Chair"].DeleteUsingBSModalViaTrigger().
 Products.Rows[x => x.Name == "Chair"].Should.Not.Exist().
 Products.Rows.Count.Should.Equal(count - 1);
}

The approach with trigger is easy to use, as you just invoke a method (e.g. DeleteUsingBSModalViaTrigger) and popup is confirmed behind the scene.

JQuery Confirm

And finally jquery-confirm.

jQuery confirm box

We need to check the HTML of confirmation popup using browser developer tools. Here is the simplified HTML of it:

<div class="jconfirm-box jconfirm-hilight-shake jconfirm-type-orange jconfirm-type-animated" role="dialog">
 <div class="jconfirm-closeIcon" style="display: block;">×</div>
 <div class="jconfirm-title-c">
 <span class="jconfirm-icon-c"/>
 <span class="jconfirm-title">Confirmation</span>
 </div>
 <div class="jconfirm-content-pane">
 <div class="jconfirm-content" id="jconfirm-box52328">Are you sure you want to delete "<strong>Table</strong>" product?</div>
 </div>
 <div class="jconfirm-buttons">
 <button type="button" class="btn btn-default">delete</button>
 <button type="button" class="btn btn-default">cancel</button>
 </div>
 <div class="jconfirm-clear"/>
</div>

Let’s define generic base page object for jQuery confirm box:

using Atata;

namespace AtataSamples.ConfirmationPopups
{
 [PageObjectDefinition("div", ContainingClass = "jconfirm-box", ComponentTypeName = "confirm box")]
 [WindowTitleElementDefinition("span", ContainingClass = "jconfirm-title")]
 public class JQueryConfirmBox<TOwner> : PopupWindow<TOwner>
 where TOwner : JQueryConfirmBox<TOwner>
 {
 }
}

And now, for our deletion confirmation popup we can implement specific page object:

using Atata;

namespace AtataSamples.ConfirmationPopups
{
 [Name("Deletion Confirmation")]
 [WindowTitle("Confirmation")]
 public class DeletionJQueryConfirmBox<TNavigateTo> : JQueryConfirmBox<DeletionJQueryConfirmBox<TNavigateTo>>
 where TNavigateTo : PageObject<TNavigateTo>
 {
 [Term(TermCase.MidSentence)]
 public ButtonDelegate<TNavigateTo, DeletionJQueryConfirmBox<TNavigateTo>> Delete { get; private set; }

 [Term(TermCase.MidSentence)]
 public ButtonDelegate<TNavigateTo, DeletionJQueryConfirmBox<TNavigateTo>> Cancel { get; private set; }
 }
}

The same way as for Bootstrap Modal, implement ConfirmDeletionViaJQueryConfirmBoxAttribute trigger:

using Atata;

namespace AtataSamples.ConfirmationPopups
{
 public class ConfirmDeletionViaJQueryConfirmBoxAttribute : TriggerAttribute
 {
 public ConfirmDeletionViaJQueryConfirmBoxAttribute(TriggerEvents on = TriggerEvents.AfterClick, TriggerPriority priority = TriggerPriority.Medium)
 : base(on, priority)
 {
 }

 protected override void Execute<TOwner>(TriggerContext<TOwner> context)
 {
 Go.To<DeletionJQueryConfirmBox<TOwner>>(temporarily: true).
 Delete();
 }
 }
}

Add 2 properties to ProductTableRow:

public class ProductTableRow : TableRow<_>
{
 

 [FindByContent("Delete Using jquery-confirm")]
 public ButtonDelegate<DeletionJQueryConfirmBox<_>, _> DeleteUsingJQueryConfirm { get; private set; }

 [FindByContent("Delete Using jquery-confirm")]
 [ConfirmDeletionViaJQueryConfirmBox]
 public ButtonDelegate<_> DeleteUsingJQueryConfirmViaTrigger { get; private set; }
}

In ProductTests class implement 2 tests for jQuery Confirm using different approaches:

[Test]
public void Products_DeleteUsingJQueryConfirm()
{
 int count;

 Go.To<ProductsPage>().
 Products.Rows.Count.Get(out count).

 Products.Rows[x => x.Name == "Desk"].DeleteUsingJQueryConfirm().
 Cancel(). 
 Products.Rows[x => x.Name == "Desk"].Should.Exist().
 Products.Rows.Count.Should.Equal(count).

 Products.Rows[x => x.Name == "Desk"].DeleteUsingJQueryConfirm().
 Delete(). 
 Products.Rows[x => x.Name == "Desk"].Should.Not.Exist().
 Products.Rows.Count.Should.Equal(count - 1);
}

[Test]
public void Products_DeleteUsingJQueryConfirm_ViaTrigger()
{
 int count;

 Go.To<ProductsPage>().
 Products.Rows.Count.Get(out count).

 Products.Rows[x => x.Name == "Desk"].DeleteUsingJQueryConfirmViaTrigger().
 Products.Rows[x => x.Name == "Desk"].Should.Not.Exist().
 Products.Rows.Count.Should.Equal(count - 1);
}

Video Guide

You may also check the video, that shows the tests development step by step.

[embedded content]

References

History

  • July 13, 2017: Initial version posted.

LEAVE A REPLY