Interactive Testing

In some circumstances not all tests can be completely automated.

htf offers interactive testing which allows you to interact with your tests via your web browser. A web server is started on port 8080.

A QR code is printed on the terminal so that you can interact using your mobile phone or a tablet easily.

This feature can be used to script and revision manual tests for example.

Interaction is done via the interaction fixture.

htf needs to run in interactive mode. When running from command line add the -i argument or set interactive=True in htf.main.

By default a browser is opened to be used for interaction. If you do not wish to open the browser automatically use --no-browser or pass open_browser=False to htf.main.

To set the listening address use --interactive-address or interactive_address in htf.main To set the port to listen on use --interactive-port or interactive_port in htf.main.

All interaction dialogs will be put into the HTML test report with the appropriate answers.

Interaction Dialogs

Message Dialog

To inform the user you can create a message_dialog. The message dialog has an ok button that will hide it immediately.

def test_message_dialog(interaction):
    """
    This test shows how to use a message dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    interaction.message_dialog("Ready", "Test is ready to start.")
_images/message_dialog.png

Waiting Dialog

To let the user wait for a given time you can use waiting_dialog. The timeout must not be None.

def test_waiting_dialog(interaction):
    """
    This test shows how to use a waiting dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    interaction.waiting_dialog("Please wait a minute", "Setup is running. Please wait.", timeout=60)
_images/waiting_dialog.png

Input Dialog

If your test needs user input you can use input_dialog.

def test_input_dialog(interaction):
    """
    This test shows how to use an input dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.input_dialog("Measurement value", "Please enter the current measurement value.")
    # value is a string
_images/input_dialog.png

You can optionally validate the user’s inputs on server side that only valid inputs are accepted. The validator can also act as a type cast.

def test_input_dialog(interaction):
    """
    This test shows how to use an input dialog with a float validator.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.input_dialog("Measurement value", "Please enter the current measurement value.",
                          validator=htf.float_validator)
    # value is a float

There are some built-in validators.

htf.int_validator(value: Any) int

An int validator for interactive tests.

htf.float_validator(value: Any) float

A float validator for interactive tests.

htf.regex_validator(pattern: str, exception_message: str | None = None) Callable[[str], str]

A regex validator factory.

Parameters:
  • pattern – the regex pattern to be checked

  • exception_message=None – the optional exception message supplied to the user in its browser

Returns:

an interaction validator for pattern

def test_input_dialog(interaction):
    """
    This test shows how to use an input dialog with a regex validator.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.input_dialog("Serial number", "Please enter the device serial number.",
                          validator=htf.regex_validator(pattern=r"[A-Z]{4}-[0-9]{4}"))
    # value is a valid serial number
_images/input_regex_dialog.png

Yes-No Dialog

To ask the user a yes-no question you can use yes_no_dialog.

def test_yes_no_dialog(interaction):
    """
    This test shows how to use a yes-no dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.yes_no_dialog("Continue?", "Do you want to continue?")
    # value is 'yes' or 'no' or None in case of a timeout
_images/yesno_dialog.png

Button Dialog

To ask the user a question with one answer out of multiple predefined choices you can use the button_dialog.

def test_button_dialog(interaction):
    """
    This test shows how to use a button dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.button_dialog("Favorite food", "What is your favorite food?",
                                      choices=[
                                          ("Pizza", "pizza"),
                                          ("Burger", "burger"),
                                          ("Noodles", "noodles"),
                                      ])
    # value is 'pizza', 'burger', 'noodles' or None in case of a timeout
_images/button_dialog.png

Radio Button Dialog

To ask the user a question with one answer out of multiple predefined choices you can use the radio_button_dialog.

def test_radio_button_dialog(interaction):
    """
    This test shows how to use a radio button dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.radio_button_dialog("Favorite food", "What is your favorite food?",
                choices=[
                    "Pizza",  # label and answer are the same
                    ["Burger", "burger"],  # label is left and answer is right
                    ("Noodles", "noodles"),  # supplied as a tuple
                    ["Steak", True],  # label and answer are the same and the checkbox is checked
                    ["Fish", "fish", False]  #  # label is left and answer is right and the checkbox is not checked
                ])
    # value is 'Pizza', 'burger', 'noodles', 'Steak', 'fish' or None in case of a timeout
_images/radio_dialog.png

Checkbox Dialog

To ask the user a question with multiple predefined answers you can use the checkbox_dialog.

def test_checkbox_dialog(interaction):
    """
    This test shows how to use a checkbox dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.checkbox_dialog("Favorite food", "What is your favorite food?",
                choices=[
                    "Pizza",  # label and answer are the same
                    ["Burger", "burger"],  # label is left and answer is right
                    ("Noodles", "noodles"),  # supplied as a tuple
                    ["Steak", True],  # label and answer are the same and the checkbox is checked
                    ["Fish", "fish", False]  #  # label is left and answer is right and the checkbox is not checked
                ])
    # value is a list containing no elements or multiple choices from
    # 'Pizza', 'burger', 'noodles', 'Steak', 'fish' or None in case of a timeout
_images/checkbox_dialog.png

File Upload Dialog

To allow the user to upload a file you can use the file_upload_dialog.

def test_file_upload_dialog(interaction):
    """
    This test shows how to use a file_upload dialog.

    Args:
        interaction (htf.fixtures.interaction): the interaction fixture
    """
    value = interaction.file_upload_dialog("Favorite food", "Please upload a picture of your favorite food", ".png, image/*")
    # value is a tuple( file name, file type, uploaded file in binary form) or None in case of a timeout.
_images/file_upload_dialog.png

Captcha Functionality

Using the captcha library allows you to simulate Captcha functionality in dialogs.

def test_solve_captchas(interaction, assertions):
    """
    This test shows how to interactively solve captchas.

    Args:
         interaction (htf.fixtures.interaction): the interaction fixture
         assertions (htf.fixtures.assertions): the assertions fixture
    """
    try:
        for number_solved in range(1, 6):  # max 5 captchas can be solved
            image = ImageCaptcha()
            captcha_value = str(randint(1000, 10000))
            image.write(captcha_value, 'captcha.png')

            captcha = """

            .. |captcha| image:: captcha.png
            """

            answer = interaction.input_dialog(title='Captcha',
                                              text="Please solve the following captcha:\n\n|captcha|" + captcha)
            assertions.assert_equal(answer, captcha_value)
            if number_solved < 5:
                again = interaction.yes_no_dialog(title='Again',
                                                  text="Would you like to solve another one?")
                if again == 'no':  # stop if the user has had enough
                    break
    except AssertionError:
        number_solved -= 1
        raise
    finally:
        interaction.message_dialog(title='Congratulations', text="You solved {} captchas!".format(number_solved))
_images/captcha_dialog.png

Embed Images in Dialogs

The text parameter of all dialogs can be reStructuredText. It is possible to embed images that appear in the dialog and in the HTML test report.

Images can be embedded as a paragraph or inline.

Images as Paragraphs

To add an image as a paragraph use

.. image:: path/to/image.png
_images/image.png

Inline Images

To add an image inline use substitution definitions.

This |inlineimage| is located inline.

.. |inlineimage| image:: path/to/image.png
_images/inline_image.png

Repeat or Cancel Tests

On the top right corner in the browser a test control panel can be found. Users can repeat or cancel a test by clicking the corresponding button and entering a message.

Note

Cancelling or repeating a test is performed in the same thread in which the test is executed. Thus, a test can block the reaction (e.g. if it runs time.sleep or any other blocking call). The task of waiting for a longer time should therefore be split into several shorter calls to time.sleep, etc.

Reference

class htf.fixtures.interaction(interaction_server: BrowserInteractionServer, result: TestResult)
button_dialog(title: str, text: str, choices: List[str] | Tuple[str], timeout: float = 300) Any

Create a dialog with buttons and definable answers.

Parameters:
  • title – the title of the button dialog

  • text – the content of the button dialog

  • choices – each choice can be a string containing the label and the answer a tuple or list of two strings where the first entry is the label and the second label is the answer.

  • timeout=300 – the timeout until the button dialog disappears in seconds (None means no timeout)

Returns:

the answer for the clicked button or None in case of a timeout

Return type:

object or None

checkbox_dialog(title: str, text: str, choices: List[str] | Tuple[str], timeout: float = 300) Any

Create a dialog with definable checkboxes for multiple choices.

Parameters:
  • title – the title of the checkbox dialog

  • text – the content of the checkbox dialog

  • choices – each choice can be a string containing the label and the answer a tuple or list of two strings where the first entry is the label and the second label is the answer. You can also supply a list or tuple with two entries where the first entry is the label and the second entry is a boolean that states if the radio button is checked. You can also supply a list or tuple with three entries where the first entry is the label, the second entry is the answer and the third entry is a boolean that states if the radio button is checked.

  • timeout=300 – the timeout until the checkbox dialog disappears in seconds (None means no timeout)

Returns:

a list containing the selected answers or None in case of a timeout

Return type:

list of str or None

file_upload_dialog(title: str, text: str, file_types: str | None = None, timeout: float = 300) File | None

Create a dialog which allows the user to upload a file.

Parameters:
  • title – the title of the file upload dialog

  • text – the content of the file upload dialog

  • file_types – desired file type of files to be uploaded. This should be respected by the file chooser in all modern browsers. File types can be specified as file extensions or MIME types, with every entry separated by a comma.

  • timeout=300 – the timeout until the file upload dialog disappears in seconds (None means no timeout)

Returns:

tuple of str or tuple or None. ( file name, file type, uploaded file in binary form).

input_dialog(title: str, text: str, timeout: float = 300, validator: Callable[[...], Any] | None = None) Any

Create an input dialog to let the user interact with some textual input.

Parameters:
  • title – the title of the input dialog

  • text – the content of the input dialog

  • timeout=300 – the timeout until the input dialog disappears in seconds (None means no timeout)

  • validator=None – the validator to validate the inputs on server side

Returns:

the string the user supplied, the return value of the validator or None

in case of a timeout

Return type:

object or None

message_dialog(title: str, text: str, timeout: float = 300) Any

Create an interactive message dialog to display a message.

Parameters:
  • title – the title of the message dialog

  • text – the content of the message dialog:

  • timeout=300 – the timeout until the message dialog disappears in seconds (None means no timeout)

Returns:

"ok" in case of a click on the ok button or None in case of a timeout.

radio_button_dialog(title: str, text: str, choices: List[str] | Tuple[str], timeout: float = 300) Any

Create a dialog with definable radio buttons to select one answer out of many choices.

Parameters:
  • title – the title of the radio button dialog

  • text – the content of the radio button dialog

  • choices – each choice can be a string containing the label and the answer a tuple or list of two strings where the first entry is the label and the second label is the answer. You can also supply a list or tuple with two entries where the first entry is the label and the second entry is a boolean that states if the radio button is checked. You can also supply a list or tuple with three entries where the first entry is the label, the second entry is the answer and the third entry is a boolean that states if the radio button is checked.

  • timeout=300 – the timeout until the radio button dialog disappears in seconds (None means no timeout)

Returns:

the answer for the selected radio button or None in case of a timeout

Return type:

str or None

waiting_dialog(title: str, text: str, timeout: float) Any

Create a waiting dialog.

Parameters:
  • title – the title of the waiting dialog

  • text – the content of the waiting dialog

  • timeout – the timeout until the waiting dialog disappears in seconds

Returns:

'Ok' after the timeout is over.

yes_no_dialog(title: str, text: str, timeout: float = 300) Any

Create a yes-no dialog to ask the user a question.

Parameters:
  • title – the title of the yes-no dialog

  • text – the content of the yes-no dialog

  • timeout=300 – the timeout until the yes_no_dialog disappears in seconds (None means no timeout)

Returns:

'yes' or 'no' depending on the user input or None in case of a timeout

Return type:

str or None