elm-explorations / test / Test.Html.Query

Querying HTML structure.


type alias Single msg =
Internal.Single msg

A query that expects to find exactly one element.

Contrast with Multiple.


type alias Multiple msg =
Internal.Multiple msg

A query that may find any number of elements, including zero.

Contrast with Single.

fromHtml : Html msg -> Single msg

Translate a Html value into a Single query. This is how queries typically begin.

import Html
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (text)


test "Button has the expected text" <|
    \() ->
        Html.button [] [ Html.text "I'm a button!" ]
            |> Query.fromHtml
            |> Query.has [ text "I'm a button!" ]

Querying

find : List Test.Html.Selector.Selector -> Single msg -> Single msg

Find exactly one descendant element which matches all the given selectors. If no descendants match, or if more than one matches, the test will fail.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The list has both the classes 'items' and 'active'" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.find [ tag "ul" ]
            |> Query.has [ classes [ "items", "active" ] ]

findAll : List Test.Html.Selector.Selector -> Single msg -> Multiple msg

Find the descendant elements which match all the given selectors.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag)
import Expect


test "The list has three items" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "li" ]
            |> Query.count (Expect.equal 3)

children : List Test.Html.Selector.Selector -> Single msg -> Multiple msg

Return the matched element's immediate child elements.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The <ul> only has <li> children" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [ class "item"] [ text "first item" ]
                , li [ class "item selected"] [ text "second item" ]
                , li [ class "item"] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.find [ class "items" ]
            |> Query.children [ class "selected" ]
            |> Query.count (Expect.equal 1)

first : Multiple msg -> Single msg

Return the first element in a match. If there were no matches, the test will fail.

Query.first is a shorthand for Query.index 0 - they do the same thing.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The first <li> is called 'first item'" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "li" ]
            |> Query.first
            |> Query.has [ text "first item" ]

index : Basics.Int -> Multiple msg -> Single msg

Return the element in a match at the given index. For example, Query.index 0 would match the first element, and Query.index 1 would match the second element.

You can pass negative numbers to get elements from the end - for example, Query.index -1 will match the last element, and Query.index -2 will match the second-to-last.

If the index falls outside the bounds of the match, the test will fail.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The second <li> is called 'second item'" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "li" ]
            |> Query.index 1
            |> Query.has [ text "second item" ]

keep : Test.Html.Selector.Selector -> Multiple msg -> Multiple msg

Find the descendant elements of the result of findAll which match all the given selectors.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag)
import Expect


test "The list has three items" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ a [] [ text "first item" ]]
                , li [] [ a [] [ text "second item" ]]
                , li [] [ a [] [ text "third item" ]]
                , li [] [ button [] [ text "button" ]]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "li" ]
            |> Query.keep ( tag "a" )
            |> Expect.all
                [ Query.each (Query.has [ tag "a" ])
                , Query.first >> Query.has [ text "first item" ]
                ]

Expecting

count : (Basics.Int -> Expectation) -> Multiple msg -> Expectation

Expect the number of elements matching the query fits the given expectation.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag)
import Expect


test "The list has three items" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "li" ]
            |> Query.count (Expect.equal 3)

contains : List (Html msg) -> Single msg -> Expectation

Expect the element to have at least one descendant matching each node in the list.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The list has two li: one with the text \"third item\" and \
    another one with \"first item\"" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.contains
                [ li [] [ text "third item" ]
                , li [] [ text "first item" ]
                ]

has : List Test.Html.Selector.Selector -> Single msg -> Expectation

Expect the element to match all of the given selectors.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The list has both the classes 'items' and 'active'" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.find [ tag "ul" ]
            |> Query.has [ tag "ul", classes [ "items", "active" ] ]

hasNot : List Test.Html.Selector.Selector -> Single msg -> Expectation

Expect the element to not match all of the given selectors.

import Html exposing (div)
import Html.Attributes as Attributes
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, class)


test "The div element has no progress-bar class" <|
    \() ->
        div [ Attributes.class "button" ] []
            |> Query.fromHtml
            |> Query.find [ tag "div" ]
            |> Query.hasNot [ tag "div", class "progress-bar" ]

each : (Single msg -> Expectation) -> Multiple msg -> Expectation

Expect that a Single expectation will hold true for each of the Multiple matched elements.

import Html exposing (div, ul, li)
import Html.Attributes exposing (class)
import Test.Html.Query as Query
import Test exposing (test)
import Test.Html.Selector exposing (tag, classes)


test "The list has both the classes 'items' and 'active'" <|
    \() ->
        div []
            [ ul [ class "items active" ]
                [ li [] [ text "first item" ]
                , li [] [ text "second item" ]
                , li [] [ text "third item" ]
                ]
            ]
            |> Query.fromHtml
            |> Query.findAll [ tag "ul" ]
            |> Query.each
                (Expect.all
                    [ Query.has [ tag "ul" ]
                    , Query.has [ classes [ "items", "active" ] ]
                    ]
                )