Find element in dynamically loaded HTML content with Wallaby

Hello!

I’m new to JavaScript, so I might use wrong terminology, please bear with me :slight_smile: I need to test a page that’s heavy on JavaScript. On loading the page the browser executes a JavaScript function that eventually loads a new form into the page using Ajax. Then I need to click on a button in the freshly loaded HTML code. I can run this in Chrome, I see that the loaded HTML is rendered and the button I need to click is rendered like this:

<div _ngcontent-c2="" class="checkout-header">
      <!---->
      <h1 _ngcontent-c2="" class="heading">
        Foo bar baz
      </h1>
      <i _ngcontent-c2="" alt="X" class="close-button psi psi-times-v1 psi-hover"></i>
    </div>

I have no control over this particular HTML code. I don’t quite understand why the <i> element is used, but the Developer tools in Chrome shows this is the element I need to click on. However, when I try to find this element using Wallaby, I can’t find it. This is the code I’m trying to use:

    selector = "div.checkout-header > i"
    button = Query.css(selector)

but the result is this

  conditions: [at: :all, selected: :any, count: 1, text: nil, visible: true],
  html_validation: nil,
  method: :css,
  result: [],
  selector: "div.checkout-header > i"
}

I see in the screenshot that the downloaded HTML is rendered. What am I doing wrong? I also tried waiting:

   selector = "div.checkout-header > i"
   session
   |> assert_has(Query.css(selector))

but that failed too:

     ** (Wallaby.ExpectationNotMetError) Expected to find 1, visible element that matched the css 'div.checkout-header > i' but 0, visible elements were found.

I don’t know if you figured it out, but as no one else has answered, I might take a stab at this.
I work on QA automation and, even though I haven’t used Elixir for that (thus have not used Wallaby) and have not worked on UI testing recently, maybe my prior experience with it might help here.

It seems that your code is not finding the element. If the selector is correct, then the issue is probably of timing. That happens a lot in UI tests when there’s content rendered dynamically (through JS) and it still isn’t loaded when you query it. My advice would be to:

  1. Check in the developer tools console if that selector is valid. Hint: you can use $$("<your CSS selector>") for CSS selectors and $x("<your XPath selector>") for XPath.
  2. Check in Wallaby’s documentation for some sort of way to wait until an element is found or a timeout limit is reached. After quickly glancing the docs, Browser.find or Browser.retry, but I’m just guessing here.

So far, that’s the most I can say with the current information I have on Wallaby and your problem.
Also, if you have the chance, see if you can use IEX.pry to put a sort of “breakpoint” and do some trial-and-error without having to reload the browser and start from scratch every time.

As for the <i> element, some icon libraries (like FontAwesome) use those elements to put icons, usually with specific classes; not sure if that’s the case here.

2 Likes

I think I can help with that: Wallaby is supposed to wait for the element to appear on the page. The execute_query function contains a Browser.retry call already: https://github.com/elixir-wallaby/wallaby/blob/master/lib/wallaby/browser.ex#L1180 and will try again until all validations pass (including number of elements we want to see) or until its timeout (3s by default but you can configure it: https://github.com/elixir-wallaby/wallaby/blob/master/lib/wallaby/browser.ex#L1209 ).

@NAR You can try increasing the :max_wait_time config value. (I think it would be cool to be able to tune it per query execution, we could look into adding support for that to Wallaby). Another thing you could do is have a custom helper for waiting that takes the timeout as an argument. You could use Browser.execute_query or Browser.has and/or Browser.retry as its building blocks (or copy&paste&modify them to hack something together :wink: ). You could make a few versions of it, i.e. one that ignores query result and returns the parent, one that takes a function block to execute on the found element, and one that would return the found element. I’m curious what you end up with so please do share your findings or problems you stumble upon.

3 Likes

Hello!

Thanks for the answers. It turned out the problem was that the new HTML was loaded in an iframe, so I needed to move the focus there using focus_frame. My test still fails occasionally, that :max_wait_time option should help with that.

2 Likes