Customization
Most of the functionality provided by SeleniumYAML can be customized and added to for suiting your own needs. This page aims to explain how to modify and add new Steps, Fields and Validators.
The Structure of a Step
A Step is defined as a derivative of the selenium_yaml.steps.BaseStep
class. There are 3 main things that must be added to each new step.
selenium_yaml.fields.Field
Attributes for each field that the step needs to receive as input in the YAML structure of the step- A Meta class containing a
fields
list that should have a list of each defined field in the step - A
perform
function that will be responsible for performing the actual functionality of the step
With that taken into account, here's what the core NavigateStep
looks like:
class NavigateStep(BaseStep):
""" Step that performs a ``driver.get`` action with the given
``target_url``
"""
url = fields.CharField(required=True)
def perform(self, driver, performance_data, performance_context):
""" Navigates the driver to the provided ``url`` """
driver.get(performance_data["url"])
class Meta:
fields = ["url"]
Let's go over each part:
url = fields.CharField(required=True)
This is what is going to be used to receive the url
field as input from the YAML. The CharField
implements it's own set of validators, which is validated prior to the bot execution. Here's what the step might look like in the YAML:
- title: Step Title
action: Step Identifier // This is explained in the Step Registration section
url: https://google.com
Now, so that the step-parser can actually recognize the existence of that field, it must be added to the Meta class.
class Meta:
fields = ["url"]
And finally, the actual performance method for the step:
def perform(self, driver, performance_data, performance_context):
""" Navigates the driver to the provided ``url`` """
driver.get(performance_data["url"])
The performance_data
variable contains the data provided to the step in the YAML rendered with the performance_context
. If you would like to access the raw data as provided in the YAML, you could access it from the step_data
attribute on the class (through self.step_data
in the perform
method). If you would like to access the data after validation, but prior to being rendered, you could access it through the validated_data
property on the class.
Registering a Step
Before a step can be used in the YAML, it needs to be registered. This can be done through the provided class decorator:
from selenium_yaml.steps.registered_steps import selenium_step
@selenium_step("step identifier")
class NavigateStep(BaseStep):
...
The Identifier passed into the selenium_step
decorator is what is going to be used as the "action" identifier for the step in the YAML. This must be unique; using duplicates will result in an error.
Custom Validators
Custom validators can be created to be used in fields for providing custom validation. All validators should derive from the validators.Validator
class. The validate()
method can be overwritten to provide custom validation for values, and the __init__
method can be overwritten for adding new arguments. Here's an example of a RegexValidator
that raises an exception if a value doesn't match the given regex:
import re
from selenium_yaml.validators import Validator
class RegexValidator(Validator):
""" Validator which checks to make sure that the given value is matches
the given regex
"""
def __init__(self, regex, *args, **kwargs):
""" Adds a ``options`` attribute to the validator prior to init """
self.regex = regex
super().__init__(*args, **kwargs)
def validate(self, value):
""" Validates that the value matches the regex """
if not re.match(self.regex, value):
raise exceptions.ValidationError(
f"The `{value}` does not match `{self.regex}`.")
In case of any validation errors, the validators should raise exceptions.ValidationError
exceptions so that they can be handled correctly by the parser.
Custom Fields
A Field class can provide it's own methods for Validation (validate
) and overwrite the __init__
method to add any validators. It is suggested to use validators
for validation, instead of just directly validating everything in the validate
method.
Here's a very basic example of a field that adds a custom "RegexValidator" to validate phone numbers:
from selenium_yaml.fields import Field
class PhoneNumberField(Field):
""" Field defining regex validation for phone numbers """
def __init__(self, *args, validators=None, **kwargs):
""" Creates an instance of a PhoneNumberField and adds a
``RegexValidator`` validator
"""
validators = validators or []
validators.append(field_validators.RegexValidator(r"\d{3}-\d{3}-\d{4}"))
super().__init__(*args, validators=validators, **kwargs)
These fields can then be used in Steps as required.
Running the Customized Bot
After all of the customization, you need to execute SeleniumYAML's engine:
from selenium_yaml import SeleniumYAML
# Define/Import/Register any custom steps here
if __name__ == "__main__":
engine = SeleniumYAML(
yaml_file="path to the yaml file/a file-like object",
save_screenshots=False, # If true, a screenshot of the driver will be saved after each step
template_context={}, # The template context used for rendering the YAML through Jinja
parse_template=False # If true, the template_context will be used to render the YAML through Jinja
)
engine.perform()