View Javadoc

1   package com.atlassian.pageobjects.elements.search;
2   
3   import com.atlassian.annotations.PublicApi;
4   
5   import javax.annotation.Nonnull;
6   
7   /**
8    * <h3>Search Overview</h3>
9    * Access point to advanced search within the context of the whole page, or a particular
10   * {@link com.atlassian.pageobjects.elements.PageElement DOM element}. The search query object provided by this method
11   * allows for issuing a sophisticated, multi-level search across the DOM and returning the result in a desired form.
12   * The following paragraphs discuss the search query API.
13   *
14   * <h3>Search query</h3>
15   * The {@code SearchQuery} object is essentially a sequence of search/transform/filter steps that are applied
16   * sequentially, starting with the root context (either the entire page, or a specific DOM element, depending on what
17   * this {@code PageElementSearch} refers to). Each single step will be applied to <i>all parents</i> that were found
18   * by the previous steps, resulting in a new list of elements that will be roots for the next step, and so on until the
19   * result is requested.
20   *
21   * <p/>
22   * Each search query starts off as an instance of {@link PageElementQuery} - with element type
23   * {@link com.atlassian.pageobjects.elements.PageElement}, however they may be converted into a more generic
24   * {@link AnyQuery} by transforming/mapping the results, which limits the applicable operations to only those
25   * applicable to any object (see {@link AnyQuery} and {@link SearchQuery}).
26   *
27   * <h4>Search Steps</h4>
28   * Each search step <i>must</i> be specified using a {@link org.openqa.selenium.By by locator}, using one of the
29   * {@code by()} methods on {@link PageElementQuery}. The search steps are only applicable if the element type of the
30   * query is {@link com.atlassian.pageobjects.elements.PageElement}.
31   *
32   * <h4>Filter Steps</h4>
33   * Each query supports {@code filters} to further narrow down the results. Filters can be specified by
34   * {@link com.google.common.base.Predicate Guava predicates} - the
35   * {@link com.atlassian.pageobjects.elements.PageElements} class contains a collection of common predicates to use.
36   * Clients can also use {@link com.atlassian.webdriver.testing.matcher.MatcherPredicate} to convert {@code Hamcrest}
37   * matchers into {@code Guava} predicates. The filter steps are applicable to any object, however filter steps preserve
38   * the element type of the query.
39   *
40   * <h4>Mapping Steps</h4>
41   * Each query supports {@code mappers} to map or flat map query results into some other objects (or collections of
42   * thereof). Map steps are applicable to any object, however they do not preserve element type, and therefore can turn
43   * {@link PageElementQuery} into a more generic {@link AnyQuery}. Some specialized page-element specific mapping steps
44   * preserve the element type (see {@link PageElementQuery}).
45   *
46   * <h3>Query results</h3>
47   * The search query object offers multiple methods to obtain the results, depending on what the client wants to achieve:
48   * <ul>
49   *     <li>{@link SearchQuery#get()} - get the list of resulting objects now. Note that search query thus implements a
50   *     specific Guava supplier, which makes it usable in all contexts where supplier can be used</li>
51   *     <li>{@link SearchQuery#now()}} - synonym to {@link SearchQuery#get()}</li>
52   *     <li>{@link SearchQuery#first()} - obtain the first element of the result, or {@code null} if the results is empty
53   *     <li>{@link SearchQuery#hasResult()} - obtain a {@link com.atlassian.pageobjects.elements.query.TimedCondition}
54   *     that will periodically execute the search and allow the client to wait until there is any result/there is no
55   *     result matching the query, with a desired timeout - using
56   *     {@link com.atlassian.pageobjects.elements.query.Poller}</li>
57   *     <li>{@link SearchQuery#timed()} - obtain a {@link com.atlassian.pageobjects.elements.query.TimedQuery} that will
58   *     periodically execute the search and allow the client to wait until the result matches its expectations, with a
59   *     desired timeout - using {@link com.atlassian.pageobjects.elements.query.Poller}. This is <b>the most recommended
60   *     way</b> of obtaining the search query results</li>
61   * </ul>
62   *
63   * <h4>Example</h4>
64   * The following method (presumably in a page object) searches for all the {@code span} elements in a drop-down that
65   * represent users, and subsequently retrieves the username value from those elements, returning them as a
66   * {@link com.atlassian.pageobjects.elements.query.TimedQuery}. The subsequent call to {@code Poller} (presumably in a
67   * test) executes timed assertion that validates that the list contains 3 usernames ("alice", "bob" and "charlie")
68   * within the timeout of an AJAX load (expecting the hypothetical drop-down to issue an AJAX call to populate its
69   * contents).
70   * <p/>
71   * <pre>
72   * {@code
73   * public TimedQuery<String> getUsernames() {
74   *    return mySearch.search()
75   *              .by(By.id("user-dropdown"))
76   *              .by(By.tagName("li")) // can also use Elements.TAG_LI here
77   *                  .filter(isVisible()) // from PageElements
78   *                  .filter(hasClass("user-element")) // from PageElements
79   *              .by(By.tagName("span"), hasDataAttribute("username")) // from PageElements, check for "data-username" attribute
80   *              .withTimeout(TimeoutType.AJAX_ACTION)
81   *              .transform(getDataAttribute("username")) // from PageElements
82   *              .timed()
83   * }
84   *
85   * // ...
86   * Poller.waitUntil(myDropdown.getUsernames(), Matchers.contains("alice", "bob", "charlie"));
87   * }
88   * </pre>
89   *
90   * @since 2.3
91   *
92   * @see SearchQuery
93   * @see AnyQuery
94   * @see PageElementQuery
95   * @see DefaultQuery
96   */
97  @PublicApi
98  public interface PageElementSearch
99  {
100     /**
101      * @return a new search query object that allows execute the search in the current context
102      */
103     @Nonnull
104     DefaultQuery search();
105 }