Jan 29 • Artem Bondar

Do not force.

Using {force: true} often can be a quick solution for non-clickable web elements in Playwright or Cypress. At first, it may seem like a great solution but a big danger is hidden behind it.
Recently, one of my followers asked me to look into his test automation script. He was not able to click on the particular element on the web page and he could not figure out why. I have quickly found a root cause and we both found a workaround. But what caught my attention is that {force: true} flag was used almost everywhere in the action commands such as click(), check(), and so on. The test was also very flaky because of that. Let me share more about it.

The power of {force:true}

Cypress and Playwright frameworks both have a fantastic auto-waiting mechanism. When you interact with web elements, for example, would like to click on a button, the framework automatically runs many validation checks before the action. Some of those checks are: is the element visible, stable, can receive events, and a few others. The framework will automatically wait until all those conditions are satisfied before acting, for example, clicking the button. This improves the stability of the test execution a lot! But at the same time, if at least one of the actionability checks does not pass - the action is not performed at all and the test fails.

Web applications are not perfect and flaky by design. Sometimes it just does not make sense, why the button is not clickable, as you can see it on the page! However the framework does not have eyes, it only can read the code. And if the code says, the element is not ready for the click event, it patiantly will wait for this condition to be satisfied until the timeout expires.

So you, as a superhuman, have the magic power - the force! :)
When you pass the {force:true} flag into the action command, you are essentially telling the framework: "Hey, I know that you don't like something about this button, but I see it on the screen, I can click this button by mouse, so please do the same". The framework reply will be: "All right buddy, you set the rules here, I will click this button, but I am not responsible for the result anymore. Will it actually be clicked or not - it's now your problem, not mine."

Unfortunately, new to Cypress and Playwright frameworks engineers find this as a perfect solution to quickly fix the problems. But in reality - this results only in more problems.

Test flakiness

Disabling the actionability check by using {force: true} you may fix the problem - your script is working, and the framework can perform the click on the desired button. Quick and easy solution. At the same time, you remove the "stability fuse" from your script! Framework is not helping you anymore, it's just doing what you are told to do.

Removing the actionability checks inavitably results into more flaky tests. The more force you use - the more flaky tests you get. And it can grow into a complete mess where will be hard to understand the root cause of the stability issues.

What is the solution?
Need to find a way to write the test script without using force.
A few things that you can start with are the following:
  • Analyze the test script and the log output. It often may give you a clue about what is happening
  • Understand your application. Talk to developers and ask questions about observed behavior. They can provide the suggestions or make a fix (it can be a bug)
  • Look for workaround, to avoid using force as best as you can

Giving the "force" back

Let me show you one example, based on the code of the person I have mentioned above. The method was trying to select a checkbox, and since there was an issue in the application itself, the {force: true} flag was used to overcome this.

This is the UI of the application and the checkbox that we need to select
The form with checkbox that should be clicked
Here is the HTML of this section of application:
html code of the form
By looking at this, you may say: easy! There is a dedicated ID for the checkbox "hobbies-checkbox-2". Just click on that, and you are all set. Unfortunately, it didn't work.
Here is how the original code looked in the script:
This method in the page object class was responsible for clicking the checkbox. And it sometimes works, sometimes not :) No wonder, because {force:true} was used to perform a check of the checkbox. Also, if you look at the very first line of the method, looks like the author was battling with other flakiness and was trying to fix it by waiting for the load state.

I have tried to fix the flakiness, and the very first thing was to remove {force:true} and check the log, trying to understand the application behavior.
Here is the log:
playwright log execution with 3 attempts
By reading the log, we can see that the first two attempts to perform the click on the checkbox were intercepted by some element with the id "fixedban". That page has a bunch of advertising banners, and probably those banners interfered with the attempt to select the checkbox. But on the third attempt, we see that the label of the checkbox now intercepted the pointer event. Why did that happen? Hard to say. But this label is for sure related to the checkbox that we try to click. This label is a sibling element in relation to the <input> element of the checkbox that we need to select and somehow receives the event instead of the <input> field.

When I realized that, the very first idea to try, was to click on the parent <div> for those <label> and <input> elements. Since the <div> is a parent element in relation to <label>, the <label> element will not be able to intercept the pointer event.
Since the Playwright's best practice is to use user-visible locators, I have refactored the code above into just a single line and given the power back to the framework to control the stability of the execution
Now, the Playwright is responsible for waiting for the actionable state of this element and making sure that the click will be performed only when an element is ready for it.
Unfortunately, I had to replace check() with click(), so my method is not able to determine the status of the checkbox, but for this scenario, it was not important, so using click() was also acceptable. But this change made the test significantly more stable.

Final Thoughts

There are situations when no matter what you try, you can't perform action to a desired web element. In such cases, {force:true} flag can be a possible solution. But, before doing that, make sure that you really tried everything. Also, run your tests multiple times in different environments making sure, that this particular step with a "force" flag is not flaky. And when your test fail, revisit the places where you have used force, making sure it's not the place of the root cause.

Write a good and clean code, stable tests, and...
may the force be with you
Microsoft Playwright growing in popularity on the market very quickly and soon will be a mainstream framework and replace Selenium over time.
Get the new skills at Bondar Academy with SDET with Playwright course. Start from scratch and become an expert to increase your value on the market!