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.")
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)
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
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.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
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
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
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.
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))
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
Inline Images
To add an image inline use substitution definitions.
This |inlineimage| is located inline.
.. |inlineimage| image:: path/to/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:
- 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
- the string the user supplied, the return value of the validator or
- 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 orNone
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 orNone
in case of a timeout- Return type:
str or None