2

I'm starting to embrace and utilize the screenplay pattern with selenium testing. It's a new concept for me which has me un-learning old behavior. I've been slowly translating some sample tests using screenpy from an existing test suite.

One of the things I am struggling with is how to handle "traditional" PageObject style of checking isLoaded. This would normally be a method that looks at several locators using specific search strategies (is_enabled, is_displayed, etc) to determine if that particular page object had indeed loaded.

Using the screenplay pattern I've got a separate module for each unique area which contains all the locators for it. For instance login_page, header_menu, dash_board_content, etc.

I then have action/task classes which which handle things like combining common selenium actions to allow a user to perform domain actions; e.g. LoginSuccessfully, MakeFeatureSelection, DeleteDataEntry.

I find myself wanting to gravitate to the same behavior in PageObjects by trying to figure out how to implement isLoaded vs waiting for PageObject to load. In the "traditional" PageObject style, I would combine the target and the search strategy and then just call either the Wait or the find methods.

In screenplay I can call the two different Wait or Find action classes on the Targets, but since they are missing the search strategies I've ended up with duplicating the set of Targets. It feels wrong to have the set of targets in two different places. I can't tell if this is because I'm so used to using page objects.

# under the hood this is doing a driver.find_by(locator).is_enabled()
class CheckIfLoginPageIsLoaded:
    @beat("{} attempts to CheckIfLoginPageIsLoaded")
    def perform_as(self, actor: "Actor"):
        actor.should_see(
            (Element(login_page.PASSWORD), IsClickable()),
            (Element(login_page.USERNAME), IsClickable()),
            (Element(login_page.FORGOTPASS), IsClickable()),
            )
#under the hood this is doing a WebDriverWait().until(EC.element_to_be_clickable(locator))
class WaitForLoginPage:
    def perform_as(self, actor: "Actor"):
        actor.attempts_to(
            Wait.for_the(login_page.PASSWORD).to_be_clickable(),
            Wait.for_the(login_page.USERNAME).to_be_clickable(),
            Wait.for_the(login_page.FORGOTPASS).to_be_clickable(),
            )

I need a guru to smack my hand and say "No.. bad kitty" but I feel blinded by old habits. Am I over thinking this or is there a solution in screenplay pattern that I'm not seeing?

2 Answers 2

1

You can have lambdas to check for the character of each component ("is clickable?", "is visible?"). You can combine them to have a "is loaded" function for each page.

For the "wait for" actions, you can simply pool these lambdas, alongside a wait time and an exit strategy.

Thus, whenever you want to change how to interact with one component, you just change the lambda. If you need to change how to determine if the page is loaded, you just change which lambdas you call.

1
  • 1
    That doesn't feel like it would fit within the spirit of screenplay pattern. Or did I misunderstand? Feb 5, 2021 at 18:52
0

I incorporated an Eventually class into screenpy which basically handles the logic of retrying the actors actions until they finish or time-out. This took the responsibility of retries out of selenium's hands.

the_actor.should(Eventually(
    See.the(Text.of_the(WELCOME_BANNER), ContainsTheText("Welcome!"))),
)

the_actor.should(Eventually(CheckIfLoginPageIsLoaded()))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.