Reduce waiting time for refute_has in Wallaby tests

almirsarajcic

almirsarajcic

Created 7 months ago

You might be testing that some element on the page should disappear after you take an action and you might be using the refute_has/2 function provided by Wallaby. But there’s a better way to go about it.

The problem with this approach is that refute_has/2 will wait for the :max_wait_time configured, which is 3 seconds by default. I say, why wait 3 seconds when the element has probably already disappeared after a few milliseconds?

Skip waiting and assert the element has disappeared immediately after it has by using a custom helper function:

@type session :: Wallaby.Session.t()

@spec lazily_refute_has(session(), Wallaby.Query) :: session()
def lazily_refute_has(parent, %Query{} = query) do
  conditions = Keyword.put(query.conditions, :count, 0)
  assert_has(parent, %Query{query | conditions: conditions})
end

Basically, it asserts that there are 0 occurrences of the selector on the page. Another way to write it would be

assert_has(session, Query.css(".asset-publish-button", count: 0))

I’ve put it in a macro that’s part of my FeatureCase which all my Wallaby tests use to make it available to all of them.

defmodule StoryDeckWeb.FeatureCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      use StoryDeckWeb, :verified_routes
      use Wallaby.DSL

      import StoryDeckWeb.FeatureCase
      import Wallaby.Feature

      alias Wallaby.Query

      @type session :: Wallaby.Session.t()

      @spec lazily_refute_has(session(), Wallaby.Query) :: session()
      def lazily_refute_has(parent, %Query{} = query) do
        conditions = Keyword.put(query.conditions, :count, 0)
        assert_has(parent, %Query{query | conditions: conditions})
      end
    end
  end

  # ...
end

Then I use it like this:

session
|> assert_text("Delete message")
|> click(Query.css(".delete-message-button"))
|> lazily_refute_has(Query.text("User message to delete"))