ScreenPy Selenium Recipes
This collection contains examples of common ScreenPy Selenium use-cases. For other recipes of interest, see ScreenPy’s ScreenPy Recipes.
Setting Up Actors
Set up an Actor to browse the web:
Perry = AnActor.who_can(BrowseTheWeb.using_firefox())
Set up an Actor to browse the web with a specific driver:
options = webdriver.ChromeOptions()
options.set_headless()
# ... other setup, maybe
driver = webdriver.Chrome(options=options)
Perry = AnActor.who_can(BrowseTheWeb.using(driver))
Attach Debug Information at the End of Tests
You can tell your Actors to take a screenshot just before the test is over.
Using pytest
Creating your Actors as pytest fixtures allows you to easily take screenshots and save the console log after the test is over:
from datetime import datetime
import pytest
from screenpy import AnActor
from screenpy_selenium import BrowseTheWeb, SaveConsoleLog, SaveScreenshot
@pytest.fixture()
def Sammy():
# arrange
the_actor = AnActor.named("Sammy").who_can(BrowseTheWeb.using_firefox())
# act
yield the_actor # assert happens somewhere in here, probably
# cleanup
timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H%M%S")
file_prefix = f"{DEBUG_DIR_PATH}/{timestamp}_sammy_endtest"
the_actor.attempts_to(
SaveScreenshot.as_(f"{file_prefix}_screenshot.png").and_attach_it(),
SaveConsoleLog.as_(f"{file_prefix}_js_log.txt").and_attach_it(),
)
the_actor.exit()
If you only want this information when a test fails, you can tweak this fixture using the information from pytest’s examples.
Using unittest.TestCase
During the teardown steps of your test, you can use your Actor to take screenshots before exiting:
from datetime import datetime
import unittest
from screenpy import AnActor
from screenpy_selenium import BrowseTheWeb, SaveConsoleLog, SaveScreenshot
class TestSomething(unittest.TestCase):
def setUp(self):
self.actor = AnActor.named("Sammy").who_can(BrowseTheWeb.using_firefox())
def test_the_thing(self):
Sammy = self.actor
...
def tearDown(self):
timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H%M%S")
file_prefix = f"{DEBUG_DIR_PATH}/{timestamp}_sammy_endtest"
self.actor.attempts_to(
SaveScreenshot.as_(f"{file_prefix}_screenshot.png").and_attach_it(),
SaveConsoleLog.as_(f"{file_prefix}_js_log.txt").and_attach_it(),
)
self.actor.exit()
If you only want this information when a test fails, you can tweak this code following this StackOverflow answer (sorry there isn’t a better source!).
Waiting
Automated tests go fast. You will probably need to wait for your pages to be ready for your Actions. Here are some strategies you can use.
Using Wait
Wait
tells the Actor to stop until some condition is met.
Here are some examples of its use.
Bread-and-butter default wait, waits 20 seconds for the login modal to appear:
Perry.attempts_to(Wait.for_the(LOGIN_MODAL))
Wait for a non-default timeout and a different condition:
Perry.attempts_to(Wait(42).seconds_for(THE_WELCOME_BANNER).to_disappear())
Using a custom condition, wait 20 seconds for the application to meet the condition:
class appear_in_greyscale:
def __init__(self, locator):
self.locator = locator
def __call__(self, driver):
element = driver.find_element(*self.locator)
return element.value_of_css_property("filter") == "grayscale(100%)"
Perry.attempts_to(Wait.for_the(PROFILE_ICON).to(appear_in_greyscale))
Using a custom condition which does not use a Target:
def url_minimum_length_with_text(length, text):
def _predicate(driver):
return text in driver.url and len(driver.url) >= length
return _predicate
Perry.attempts_to(
# ⇩ note the parentheses here
Wait().using(url_minimum_length_with_text).with_(20, "hello")
)
Using Eventually
The Eventually
Action
deserves a special mention in this section.
Eventually
can take the place
of Wait
,
if you’re only waiting
for an element to appear
or to be clickable.
This can reduce the length of your tests
while still being quite readable
and robust.
Here’s a quick before/after example:
# ⇩ before
when(Brody).attempts_to(
Wait.for_the(REGISTER_LINK).to_be_clickable(),
Click.on_the(REGISTER_LINK),
Wait.for_the(NICKNAME_FIELD).to_appear(),
Enter.the_text("Brody").into_the(NICKNAME_FIELD)),
Enter.the_text("Brodiferous").into_the(FULL_NAME_FIELD),
Click.on_the(SUBMIT_BUTTON),
Wait.for_the(WELCOME_BANNER).to_appear(),
)
then(Brody).should(
See.the(Text.of_the(WELCOME_BANNER), ContainsTheText("Brody")),
)
# ⇩ after
when(Brody).attempts_to(
Eventually(Click.on_the(REGISTER_LINK)),
Eventually(Enter.the_text("Brody").into_the(NICKNAME_FIELD)),
Enter.the_text("Brodiferous").into_the(FULL_NAME_FIELD),
Click.on_the(SUBMIT_BUTTON),
)
then(Brody).should(
Eventually(
See.the(Text.of_the(WELCOME_BANNER), ContainsTheText("Brody"))
),
)
See the entry in the ScreenPy Cookbook for more information on using this class.