Web Automation with Selenium & Python Part 6: [Category Update]

Hello, devs, so far we have learned how to fill input fields, click buttons. We even learned how to execute Javascript on the webpage just using Selenium and Python. We have also learned how to handle exceptions while using Selenium. In our last episode, we did the feature called category creation. Today we will test the feature called update category. This will much similar to the previous episode. But there are some differences. So that’s why I made two different articles. In order to update a form, we need to remove the old values right? Yes, that’s the thing we are gonna learn today alongside the feature. Our update category form contains the same fields as category creation form.

  • Title [required]
  • Parent Category
  • Coin
  • Question Limit [required]
  • Quiz Limit [required]
  • Time Limit [required]
  • Serial [required]
  • Status [required]
  • Description
  • Thumbnail Image
  • White Image

web automation

Lt’s see our application structure.

The Automation Testing Application Structure

.
├── application
│   ├── app.py
│   ├── __init__.py
│   ├── selenium_base.py
│   └── utils
│       ├── constants.py
│       ├── helpers.py
│       ├── __init__.py
│       └── urls.py
├── category
│   ├── constants.py
│   ├── create.py
│   ├── edit.py
│   ├── __init__.py
│   └── test.py
├── homepage
│   ├── homepage.py
│   ├── __init__.py
├── login
│   ├── __init__.py
│   ├── login.py
├── README.md
├── registration
│   ├── __init__.py
│   └── registration.py
├── requirements.txt
└── settings.py
└── .env

We have described our new python files in the last episode. Feel free to read the article. Links are given at the bottom of this article. Now Let’s add constants.

[ Step:01 ] Add Constants And URLs In Our Application

PARENT_CATEGORY_OPTION_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 
                                'div/div/div/form/div[1]/div[2]/div/div/' 
                                'select/option[53]'
STATUS_ACTIVE_OPTION_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 
                              'div/div/div/form/div[1]/div[8]/div/' 
                              'div/select/option[1]'
SAMPLE_IMAGE_FILE_PATH = '/home/trex/Downloads/python_image.jpg'

SUCCESS_VALIDATION_BOX_XPATH = '//*[@id="notification_box"]'

CATEGORY_CREATION_SUCCESS_VALIDATION_MESSAGE = "Sub Category " 
                                               "Created Successfully"
CATEGORY_UPDATE_SUCCESS_VALIDATION_MESSAGE = "Sub Category Updated Successfully"

TARGET_CATEGORY_X_PATH = '//*[@id="category-table"]/tbody/tr[21]/td[4]/a'
TARGET_SUBCATEGORY_X_PATH = '//*[@id="category-table"]/tbody/tr[1]/td[9]/ul' 
                            '/a[1]'

EDIT_FORM_HEADER_X_PATH = '/html/body/div[1]/div[2]/div[2]/div/div/div/div/h2'
EDIT_FORM_HEADER_TEXT = 'Edit Sub Category'

ERROR_VALIDATION_MESSAGES = [
    "This name already taken",
    "Title field can not be empty",
    "Max limit field can not be empty",
    "Quiz limit field can not be empty",
    "Time limit field can not be empty",
    "Serial field can not be empty",

]
CATEGORY_FORM_EDIT_DATA = dict(
    title='Python2.4.3',
    parent_category=PARENT_CATEGORY_OPTION_X_PATH,
    coin=200,
    question_limit=20,
    quiz_limit=20,
    time_limit=2,
    serial=405,
    status=STATUS_ACTIVE_OPTION_X_PATH,
    description='Programming category',
    thumbnail_image=SAMPLE_IMAGE_FILE_PATH,
    white_image=SAMPLE_IMAGE_FILE_PATH
)

We have reused some constant’s from the last episode.

[ Step: 02 ] Developing The Category Package

Import

Well in our edit.py file in category package let’s import all the necessary things.

"""Category Edit"""
from time import sleep
import unittest
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import (
    ElementNotInteractableException,
    NoSuchElementException,
    JavascriptException)
from application.selenium_base import SeleniumBase
from application.utils.helpers import console_print
from category.constants import *

Create CategoryEditTest Class

Now let’s create the CategoryEditTest class. Our init method will take a parameter called browser which we will give from the login.py.

class CategoryEditTest(SeleniumBase, unittest.TestCase):
    def __init__(self, browser):
        """
        :param browser: chained previous browser
        """
        super().__init__(browser)
        if browser is not None:
            self.browser = browser

Go to the edit form

To edit a category/subcategory we need to go to the edit form first. So we will create a method for that which will navigate to the form.

def get_edit_form(self):
    self.browser.find_element_by_xpath(TARGET_CATEGORY_X_PATH).click()
    self.browser.find_element_by_xpath(TARGET_SUBCATEGORY_X_PATH).click()
    sleep(1)
    header_text = self.browser.find_element_by_xpath(
        EDIT_FORM_HEADER_X_PATH
    ).text
    # print(len(header_text.strip()))
    # print(len(EDIT_FORM_HEADER_TEXT.strip()))
    if header_text.strip() == EDIT_FORM_HEADER_TEXT.strip():
        console_print('success', '[Found edit form]')
        sleep(1)
        return True
    console_print('failed', '[Can not find edit form]')

    return False

Clear all the field

Now every time we get a field, we need to clear it first right? So we will also create a method for that.

def clear_field(self, x_path: str):
    try:
        field = self.browser.find_element_by_xpath(x_path)
        field.send_keys(Keys.CONTROL + "a")
        field.send_keys(Keys.DELETE)
    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

We user Keys.CONTROL + “a” which will basically press the CTRL+A in the keyboard. And Keys.DELETE will press the Del keyword in the keyboard.

Update Form

Now let’s invoke this method inside every method where we intend to type some values in the form input field.

def edit_title(self, title: str):
    """
    Set title
    :param title: category title
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['title'])
        title_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['title'])
        title_field.send_keys(title)
        console_print('success', '[Title has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_parent_category(self, option_xpath: str):
    """
    Set parent category
    :param option_xpath: option xpath
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['parent_category'])
        parent_category_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['parent_category'])
        parent_category_field.click()
        option = self.browser.find_element_by_xpath(option_xpath)
        option.click()
        console_print('success', '[Parent category has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_coin(self, coin: str):
    """
    Set coin
    :param coin: category coin
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['coin'])
        coin_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['coin'])
        coin_field.send_keys(coin)
        console_print('success', '[Coin has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_question_limit(self, question_limit: str):
    """
    Set question_limit
    :param question_limit: category question_limit
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['question_limit'])
        question_limit_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['question_limit'])
        question_limit_field.send_keys(question_limit)
        console_print('success', '[Question limit has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_quiz_limit(self, quiz_limit: str):
    """
    Set quiz_limit
    :param quiz_limit: category quiz_limit
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['quiz_limit'])
        quiz_limit_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['quiz_limit'])
        quiz_limit_field.send_keys(quiz_limit)
        console_print('success', '[Quiz limit has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_time_limit(self, time_limit: str):
    """
    Set time_limit
    :param time_limit: category time_limit
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['time_limit'])
        time_limit_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['time_limit'])
        time_limit_field.send_keys(time_limit)
        console_print('success', '[Time limit has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_serial(self, serial: str):
    """
    Set serial
    :param serial: category serial
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['serial'])
        serial_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['serial'])
        serial_field.send_keys(serial)
        console_print('success', '[Serial has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_status(self, option_xpath: str):
    """
    Set status
    :param option_xpath: option xpath
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['status'])
        status_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['status'])
        status_field.click()
        option = self.browser.find_element_by_xpath(option_xpath)
        option.click()
        console_print('success', '[Status has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_description(self, description: str):
    """
    Set description
    :param description: category description
    """
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['description'])
        description_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['description'])
        description_field.send_keys(description)
        console_print('success', '[Serial has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        console_print('failed', str(error))
        self.close_browser()

        raise

But we do not need to invoke the clear_field() method in image upload methods.

def edit_thumbnail_image(self, thumbnail_image_path: str):
    """
    Upload thumbnail image for category
    :param thumbnail_image_path: given image path in local machine
    """
    try:
        # self.clear_field(CATEGORY_FORM_X_PATHS['thumbnail_image'])
        thumbnail_image_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['thumbnail_image'])
        thumbnail_image_field.send_keys(thumbnail_image_path)
        console_print('success', '[Thumbnail image has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

def edit_white_image(self, white_image_path: str):
    """
    Upload white image for category
    :param white_image_path: given image path in local machine
    """
    try:
        # self.clear_field(CATEGORY_FORM_X_PATHS['white_image'])
        white_image_field = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['white_image'])
        white_image_field.send_keys(white_image_path)
        console_print('success', '[White image has been set]')
        self.assertTrue(True)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

Now we will submit the form after updating all the values.

def submit(self):
    """
    Submit category creation form
    """
    try:
        submit_button = self.browser.find_element_by_xpath(
            CATEGORY_FORM_X_PATHS['submit'])
        submit_button.click()
        console_print('success', '[Submit button clicked]')
        self.assertTrue(True)
        sleep(1)

    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))
        self.close_browser()

        raise

And just like before, we will call all the methods in the update() method.

def update(self, form_data: dict):
    """
    Set all input fields by invoking their methods
    and submit the create form
    :param form_data: given form data as dictionary from constant file
    """
    try:
        self.edit_title(form_data['title'])
        self.edit_parent_category(form_data['parent_category'])
        self.edit_coin(form_data['coin'])
        self.edit_question_limit(form_data['question_limit'])
        self.edit_quiz_limit(form_data['quiz_limit'])
        self.edit_time_limit(form_data['time_limit'])
        self.edit_serial(form_data['serial'])
        self.edit_status(form_data['status'])
        self.edit_description(form_data['description'])
        self.edit_thumbnail_image(form_data['thumbnail_image'])
        self.edit_white_image(form_data['white_image'])
        self.submit()
        console_print('success', '[Form has been submitted]')
        self.assertTrue(True)

    except Exception as error:
        console_print('failed', str(error))

        raise

And just to clear all the input fields we can add another method like below.

def clear_all_input_field(self):
    try:
        self.clear_field(CATEGORY_FORM_X_PATHS['title'])
        self.clear_field(CATEGORY_FORM_X_PATHS['parent_category'])
        self.clear_field(CATEGORY_FORM_X_PATHS['coin'])
        self.clear_field(CATEGORY_FORM_X_PATHS['question_limit'])
        self.clear_field(CATEGORY_FORM_X_PATHS['quiz_limit'])
        self.clear_field(CATEGORY_FORM_X_PATHS['time_limit'])
        self.clear_field(CATEGORY_FORM_X_PATHS['serial'])
        self.clear_field(CATEGORY_FORM_X_PATHS['status'])
        self.clear_field(CATEGORY_FORM_X_PATHS['description'])

        console_print('success', '[Form has been cleared]')
    except Exception as error:
        console_print('failed', str(error))

        raise

Of course, we need to check the success message after a successful category update.

def success_validation_message_is_ok(self, message):
    """
    Check the validation message of a successful category creation
    :param message: success validation message
    """
    try:
        message_box = self.browser.find_element_by_xpath(
            SUCCESS_VALIDATION_BOX_XPATH)
        inner_text = message_box.text
        clean_text = inner_text.replace('x', '')
        print(len(clean_text.strip()))
        print(len(message.strip()))
        if clean_text.strip() == message.strip():
            console_print('success', '[Success validation message matched]')
            self.assertTrue(True)
        else:
            console_print('failed', '[Success validation didnt'
                                    ' message match]')
    except (
            ElementNotInteractableException,
            NoSuchElementException,
            JavascriptException) as error:
        console_print('failed', str(error))

        raise

And to test all the error validation we will add another method to do that.

def error_validation_message_is_ok(self):
    """
    Check all error validation message by giving empty string to all fields
    """
    try:
        for message_x_path in ERROR_VALIDATION_MESSAGE_X_PATHS:
            message = self.browser.find_element_by_xpath(
                ERROR_VALIDATION_MESSAGE_X_PATHS[message_x_path]).text
            if message not in ERROR_VALIDATION_MESSAGES:
                console_print('failed', '[' + message_x_path
                              + 'validation message didnt match!]')
                print(message)
            else:
                console_print('success',
                              '[All validation message matched!]')
    except (
            ElementNotInteractableException,
            NoSuchElementException) as error:
        console_print('failed', str(error))

        raise

After adding all the methods, our category/edit.py will look like this.

"""Category Edit"""
from time import sleep
import unittest
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import (
    ElementNotInteractableException,
    NoSuchElementException,
    JavascriptException)
from application.selenium_base import SeleniumBase
from application.utils.helpers import console_print
from category.constants import *


class CategoryEditTest(SeleniumBase, unittest.TestCase):
    def __init__(self, browser):
        """
        :param browser: chained previous browser
        """
        super().__init__(browser)
        if browser is not None:
            self.browser = browser

    def get_edit_form(self):
        self.browser.find_element_by_xpath(TARGET_CATEGORY_X_PATH).click()
        self.browser.find_element_by_xpath(TARGET_SUBCATEGORY_X_PATH).click()
        sleep(1)
        header_text = self.browser.find_element_by_xpath(
            EDIT_FORM_HEADER_X_PATH
        ).text
        # print(len(header_text.strip()))
        # print(len(EDIT_FORM_HEADER_TEXT.strip()))
        if header_text.strip() == EDIT_FORM_HEADER_TEXT.strip():
            console_print('success', '[Found edit form]')
            sleep(1)
            return True
        console_print('failed', '[Can not find edit form]')

        return False

    def clear_field(self, x_path: str):
        try:
            field = self.browser.find_element_by_xpath(x_path)
            field.send_keys(Keys.CONTROL + "a")
            field.send_keys(Keys.DELETE)
        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_title(self, title: str):
        """
        Set title
        :param title: category title
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['title'])
            title_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['title'])
            title_field.send_keys(title)
            console_print('success', '[Title has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_parent_category(self, option_xpath: str):
        """
        Set parent category
        :param option_xpath: option xpath
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['parent_category'])
            parent_category_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['parent_category'])
            parent_category_field.click()
            option = self.browser.find_element_by_xpath(option_xpath)
            option.click()
            console_print('success', '[Parent category has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_coin(self, coin: str):
        """
        Set coin
        :param coin: category coin
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['coin'])
            coin_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['coin'])
            coin_field.send_keys(coin)
            console_print('success', '[Coin has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_question_limit(self, question_limit: str):
        """
        Set question_limit
        :param question_limit: category question_limit
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['question_limit'])
            question_limit_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['question_limit'])
            question_limit_field.send_keys(question_limit)
            console_print('success', '[Question limit has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_quiz_limit(self, quiz_limit: str):
        """
        Set quiz_limit
        :param quiz_limit: category quiz_limit
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['quiz_limit'])
            quiz_limit_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['quiz_limit'])
            quiz_limit_field.send_keys(quiz_limit)
            console_print('success', '[Quiz limit has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_time_limit(self, time_limit: str):
        """
        Set time_limit
        :param time_limit: category time_limit
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['time_limit'])
            time_limit_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['time_limit'])
            time_limit_field.send_keys(time_limit)
            console_print('success', '[Time limit has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_serial(self, serial: str):
        """
        Set serial
        :param serial: category serial
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['serial'])
            serial_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['serial'])
            serial_field.send_keys(serial)
            console_print('success', '[Serial has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_status(self, option_xpath: str):
        """
        Set status
        :param option_xpath: option xpath
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['status'])
            status_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['status'])
            status_field.click()
            option = self.browser.find_element_by_xpath(option_xpath)
            option.click()
            console_print('success', '[Status has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_description(self, description: str):
        """
        Set description
        :param description: category description
        """
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['description'])
            description_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['description'])
            description_field.send_keys(description)
            console_print('success', '[Serial has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_thumbnail_image(self, thumbnail_image_path: str):
        """
        Upload thumbnail image for category
        :param thumbnail_image_path: given image path in local machine
        """
        try:
            # self.clear_field(CATEGORY_FORM_X_PATHS['thumbnail_image'])
            thumbnail_image_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['thumbnail_image'])
            thumbnail_image_field.send_keys(thumbnail_image_path)
            console_print('success', '[Thumbnail image has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def edit_white_image(self, white_image_path: str):
        """
        Upload white image for category
        :param white_image_path: given image path in local machine
        """
        try:
            # self.clear_field(CATEGORY_FORM_X_PATHS['white_image'])
            white_image_field = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['white_image'])
            white_image_field.send_keys(white_image_path)
            console_print('success', '[White image has been set]')
            self.assertTrue(True)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def submit(self):
        """
        Submit category creation form
        """
        try:
            submit_button = self.browser.find_element_by_xpath(
                CATEGORY_FORM_X_PATHS['submit'])
            submit_button.click()
            console_print('success', '[Submit button clicked]')
            self.assertTrue(True)
            sleep(1)

        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))
            self.close_browser()

            raise

    def update(self, form_data: dict):
        """
        Set all input fields by invoking their methods
        and submit the create form
        :param form_data: given form data as dictionary from constant file
        """
        try:
            self.edit_title(form_data['title'])
            self.edit_parent_category(form_data['parent_category'])
            self.edit_coin(form_data['coin'])
            self.edit_question_limit(form_data['question_limit'])
            self.edit_quiz_limit(form_data['quiz_limit'])
            self.edit_time_limit(form_data['time_limit'])
            self.edit_serial(form_data['serial'])
            self.edit_status(form_data['status'])
            self.edit_description(form_data['description'])
            self.edit_thumbnail_image(form_data['thumbnail_image'])
            self.edit_white_image(form_data['white_image'])
            self.submit()
            console_print('success', '[Form has been submitted]')
            self.assertTrue(True)

        except Exception as error:
            console_print('failed', str(error))

            raise

    def clear_all_input_field(self):
        try:
            self.clear_field(CATEGORY_FORM_X_PATHS['title'])
            self.clear_field(CATEGORY_FORM_X_PATHS['parent_category'])
            self.clear_field(CATEGORY_FORM_X_PATHS['coin'])
            self.clear_field(CATEGORY_FORM_X_PATHS['question_limit'])
            self.clear_field(CATEGORY_FORM_X_PATHS['quiz_limit'])
            self.clear_field(CATEGORY_FORM_X_PATHS['time_limit'])
            self.clear_field(CATEGORY_FORM_X_PATHS['serial'])
            self.clear_field(CATEGORY_FORM_X_PATHS['status'])
            self.clear_field(CATEGORY_FORM_X_PATHS['description'])

            console_print('success', '[Form has been cleared]')
        except Exception as error:
            console_print('failed', str(error))

            raise

    def success_validation_message_is_ok(self, message):
        """
        Check the validation message of a successful category creation
        :param message: success validation message
        """
        try:
            message_box = self.browser.find_element_by_xpath(
                SUCCESS_VALIDATION_BOX_XPATH)
            inner_text = message_box.text
            clean_text = inner_text.replace('x', '')
            print(len(clean_text.strip()))
            print(len(message.strip()))
            if clean_text.strip() == message.strip():
                console_print('success', '[Success validation message matched]')
                self.assertTrue(True)
            else:
                console_print('failed', '[Success validation didnt'
                                        ' message match]')
        except (
                ElementNotInteractableException,
                NoSuchElementException,
                JavascriptException) as error:
            console_print('failed', str(error))

            raise

    def error_validation_message_is_ok(self):
        """
        Check all error validation message by giving empty string to all fields
        """
        try:
            for message_x_path in ERROR_VALIDATION_MESSAGE_X_PATHS:
                message = self.browser.find_element_by_xpath(
                    ERROR_VALIDATION_MESSAGE_X_PATHS[message_x_path]).text
                if message not in ERROR_VALIDATION_MESSAGES:
                    console_print('failed', '[' + message_x_path
                                  + 'validation message didnt match!]')
                    print(message)
                else:
                    console_print('success',
                                  '[All validation message matched!]')
        except (
                ElementNotInteractableException,
                NoSuchElementException) as error:
            console_print('failed', str(error))

            raise

[ Step: 03 ] Test Category Creation

Now let’s test the code in test.py file. First import all the necessary things.

"""Category automation testing unit"""

from application.utils.urls import (LOGIN_URL, ADMIN_DASHBOARD)
from category.edit import CategoryEditTest
from login.login import LoginTest
from category.create import CategoryCreateTest
from application.utils.constants import LOGIN_PAGE_X_PATHS
from settings import (
    ADMIN_EMAIL, ADMIN_PASSWORD
)
from category.constants import *

We will test the create a category first, then we will update that. You may need to change the form of data values in your constants.

if __name__ == '__main__':
    # Successful Login
    login_test = LoginTest(LOGIN_URL, LOGIN_PAGE_X_PATHS, ADMIN_DASHBOARD)
    login_test.visit_url(LOGIN_URL)
    login_test.login_attempt(ADMIN_EMAIL, ADMIN_PASSWORD)

    # Category Successful create Test
    category_create_test = CategoryCreateTest(login_test.browser)
    category_create_test.visit_url(CREATE_CATEGORY_URL)
    category_create_test.create(CATEGORY_FORM_DATA)
    category_create_test.success_validation_message_is_ok(
        CATEGORY_CREATION_SUCCESS_VALIDATION_MESSAGE)

    # Category create validation message Test
    category_create_test.visit_url(CREATE_CATEGORY_URL)
    category_create_test.submit()
    category_create_test.error_validation_message_is_ok()

    # Try to save with duplicate name
    category_create_test.duplicate_category_creation(CATEGORY_FORM_DATA)

    # Edit test
    category_edit_test = CategoryEditTest(login_test.browser)
    category_edit_test.visit_url(CATEGORY_LIST_URL)
    category_edit_test.get_edit_form()
    category_edit_test.update(CATEGORY_FORM_EDIT_DATA)
    category_edit_test.success_validation_message_is_ok(
        CATEGORY_UPDATE_SUCCESS_VALIDATION_MESSAGE)

    category_edit_test.visit_url(CATEGORY_LIST_URL)
    category_edit_test.get_edit_form()
    category_edit_test.clear_all_input_field()
    category_edit_test.submit()
    category_edit_test.error_validation_message_is_ok()

If everything works fine without any error, you will see a similar result like the video below.

In our next episode, I will refactor some code with you guys to give an insight into refactoring. Till then stay safe and share this article with your friends. Here are the GitHub link and previous episodes.

Github Repository: Web Automation Of Quiz Application

Previous Episodes:

Web Automation with Selenium & Python Part 1 [Setup & Installation]

Web Automation with Selenium & Python Part 2 [Homepage Automation]

Web Automation with Selenium & Python Part 3 [Login Automation]

Web Automation with Selenium & Python Part 4 [Registration Automation]

Happy coding!