Search code examples
javascriptandroidwebdriverandroid-espressoandroid-espresso-web

How to assert element property value with Espresso-web?


I'm working on a subclass of default Android WebView with some additional functionality (content-filtering) on the top. So i override shouldInterceptRequest(...) and prevent loading of some resource (let's say images with filename "image.png").

Now i want to test this functionality with the help of Espresso-Web. So i do start embedded http server (with WireMock) and load some webpage that uses that "image.png" and it's expected to be blocked.

How can i assert the state of DOM element with id "someId"?

I expected to have smth like:

    onWebView().
        .withElement(findElement(Locator.ID, blockImageId)) // finds the DOM node
        .check(webMatches(getProperty("naturalWidth"), equalTo("0"))) // "getProperty"?

What i need is smth like "getProperty()" that just generates JS to access the property of node found by findElement(Locator.ID, blockImageId), is there anything out-of-box?

I was able to find getText() but it seems it does it in completely different way (so i can't just request another "node property"):

/** Returns the visible text beneath a given DOM element. */
  public static Atom<String> getText() {
    return new GetTextTransformingAtom(new GetVisibleTextSimpleAtom(), castOrDie(String.class));
  }

I was able to do it in JS way:


      // JS-way helpers
    
      const val naturalWidth = "naturalWidth"
      const val notBlockedImageWidth = 50
      const val blockedImageWidth = 0

      fun getPropertyForElementId(elementId: String, property: String): Atom<String> =
          Atoms.script(
              """return String(document.getElementById("$elementId").$property);""",
              Atoms.castOrDie(String::class.java))

      fun imageIsBlocked(elementId: String): WebAssertion<String>? = WebViewAssertions.webMatches(
          getPropertyForElementId(elementId, naturalWidth),
          equalTo(blockedImageWidth.toString())) {
          """An image with id="$elementId" IS expected to be blocked, but it's NOT."""
      }

      // part of the test

      val redImage = "image.png"
      load(
            listOf(blockingPathPrefix),
            """
            |<html>
            |<body>
            |  <img id="$blockImageId" src="${blockingPathPrefix}$redImage"/>
            |  <img id="$notBlockImageId" src="$greenImage"/>
            |</body>
            |</html>
            |""".trimMargin()
        )

        onWebView()
            .withTimeout(1, TimeUnit.SECONDS)  // it's already loaded
            .check(imageIsBlocked(blockImageId))       // red image IS blocked
            .check(imageIsNotBlocked(notBlockImageId)) // green image is NOT blocked

I do understand that the way i did it is suboptimal as it joins everything: searching of the node and accessing at once and i wonder what's the right way to do it. Any suggestions, links?

PS. "naturalWidth" is just a property that helps me in this particular case. In common case i just need to access a property of found DOM node and it can be some other property next time (eg "style.display" to check element visibility).

PS. Can anybody explain how to write WebDriverAtomScripts, eg. smth similar to WebDriverAtomScripts.GET_VISIBLE_TEXT_ANDROID?


Solution

  • is there anything out-of-box?

    The answer to your question is "no", I don't think there is anything better out-of-the-box than creating an Atom like you are doing (or similar approaches like Atoms.scriptWithArgs or subclassing SimpleAtom).

    Your best bet is to file a feature request here (and then maybe propose/contribute an implementation): https://github.com/android/android-test

    You can assert on the html document with xpath, but that won't work for computed DOM node attributes like you are looking for.

    onWebView()
    .check(webContent(hasElementWithXpath("//*[@id=\"myButton\" and @type=\"button\"]")))