package com.abmash.REMOVE.api;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.remote.RemoteWebElement;
import com.abmash.REMOVE.core.htmlquery.condition.ClosenessCondition;
import com.abmash.REMOVE.core.htmlquery.condition.ClosenessCondition.Direction;
import com.abmash.REMOVE.core.htmlquery.condition.ColorCondition;
import com.abmash.REMOVE.core.htmlquery.condition.Condition;
import com.abmash.REMOVE.core.htmlquery.condition.Conditions;
import com.abmash.REMOVE.core.htmlquery.condition.ElementCondition;
import com.abmash.REMOVE.core.htmlquery.condition.ElementCondition.ElementType;
import com.abmash.REMOVE.core.htmlquery.condition.SelectorCondition;
import com.abmash.REMOVE.core.htmlquery.condition.SelectorCondition.QueryType;
import com.abmash.REMOVE.core.htmlquery.condition.TagnameCondition;
import com.abmash.REMOVE.core.htmlquery.selector.Selector;
import com.abmash.REMOVE.core.htmlquery.selector.SelectorGroup;
import com.abmash.REMOVE.core.htmlquery.selector.SelectorGroup.Type;
import com.abmash.REMOVE.core.htmlquery.selector.SelectorGroups;
import com.abmash.api.Browser;
import com.abmash.api.HtmlElement;
import com.abmash.api.HtmlElements;
import com.abmash.core.color.ColorName;
import com.abmash.core.color.Dominance;
import com.abmash.core.color.Tolerance;
import com.google.common.collect.Table;
/**
* Find elements by specifying query conditions for the current active web page, used by calling {@link Browser#query()}.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li><code>browser.query().has("result").findFirst();</code> searches for elements containing the attribute or
* inner text <em>result</em></li>
* <li><code>browser.query().isTitle().findFirst();</code> searches for title elements, i.e. {@code <h1>}, {@code <h2>}, ... {@code <h6>}
* and any element with a bigger font-size than the default on the current page</li>
* <li><code>browser.query().root(myHtmlElement).isClickable().findFirst();</code> searches for all clickable elements like links
* and buttons which are child elements of <code>myHtmlElement</code>
* <li><code>browser.query().below(myHtmlElement).isImage().has("description").findFirst();</code> searches for all image elements
* with are below <code>myHtmlElement</code> and have an attribute or inner text <em>description</em>
* </ul>
* <p>
* <strong>Description:</strong>
* <p>
* Create a new query instance by calling <code>browser.query()</code>. Conditions can be chained one after another.
* To get the matching {@link HtmlElements} or {@link HtmlElement}, call {@link #find()} or {@link #findFirst()}.
*
* @author Alper Ortac
*/
public class HtmlQuery {
private Browser browser;
private HtmlElements resultElements = null;
private HtmlElements rootElements = new HtmlElements();
private HtmlElements elementsToFilter = new HtmlElements();
private Conditions conditions = new Conditions();
private ArrayList<HtmlQuery> notQueries = new ArrayList<HtmlQuery>();
private ArrayList<HtmlQuery> orQueries = new ArrayList<HtmlQuery>();
private ArrayList<String> queryStrings = new ArrayList<String>();
// private ArrayList<String> textQueries = new ArrayList<String>();
// private ArrayList<String> attributeQueries = new ArrayList<String>();
// private ArrayList<String> cssAttributeQueries = new ArrayList<String>();
private int limit = 0;
// private OrderType orderBy = null;
private ArrayList<ElementType> elementTypes = new ArrayList<ElementType>();
/**
* Creates a new query instance for finding {@link HtmlElements} on the current page.
*
* @param browser the browser instance to use for finding elements
*/
public HtmlQuery(Browser browser) {
this.browser = browser;
}
public String toString() {
String queryString = "HtmlQuery with following conditions:";
for (Condition condition: conditions) {
queryString += "\n [" + condition + "]";
}
return queryString;
}
// filter by reference elements
/**
* Defines root elements from which to search on.
* <p>
* Each element will be searched separately.
* Only descendants will be searched whereas ancestors will be ignored.
*
* @param rootElements the root elements
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery childOf(HtmlElements rootElements) {
if(!(rootElements instanceof HtmlElements)) throw new RuntimeException("Error: root elements for query cannot be null.");
this.rootElements.addAll(rootElements);
return this;
}
/**
* Defines a root element from which to search on. All other elements in the document are ignored.
* <p>
* Only descendants will be searched whereas ancestors will be ignored.
*
* @param rootElement the root element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery childOf(HtmlElement rootElement) {
if(!(rootElement instanceof HtmlElement)) throw new RuntimeException("Error: root element for query cannot be null.");
this.rootElements.add(rootElement);
return this;
}
/**
* Defines subset elements which are used as base for filtering the results.
* <p>
* TODO examples
*
* @param elementsToFilter the elements which are used as subset for filtering
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery subsetOf(HtmlElements elementsToFilter) {
if(!(elementsToFilter instanceof HtmlElements)) throw new RuntimeException("Error: subset elements for query cannot be null.");
this.elementsToFilter.addAll(elementsToFilter);
return this;
}
// NOT and OR subqueries
/**
* Defines a "NOT" subquery. The position of the subquery in the condition chain does not matter and is ignored.
* {@link #childOf(HtmlElement)} elements are automatically passed to the subquery.
* <p>
* Note that the {@link #find()} method of the subquery it automatically called. If only one (or any other amount) of the result
* elements shall be returned, use {@code #limit(Integer)}.
*
* @param notQuery the {@link HtmlQuery} to be combined as NOT condition
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #limit(Integer)
*/
public HtmlQuery not(HtmlQuery notQuery) {
if(!(notQuery instanceof HtmlQuery)) throw new RuntimeException("Error: NOT query cannot be null.");
notQueries.add(notQuery);
return this;
}
/**
* Defines an "OR" subquery. The position of the subquery in the condition chain does not matter and is ignored.
* {@link #childOf(HtmlElement)} elements are automatically passed to the subquery.
* <p>
* Note that the {@link #find()} method of the subquery it automatically called. If only one (or any other amount) of the result
* elements shall be returned, use {@code #limit(Integer)}.
*
* @param orQuery the {@link HtmlQuery} to be combined as OR condition
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #limit(Integer)
*/
public HtmlQuery or(HtmlQuery orQuery) {
if(!(orQuery instanceof HtmlQuery)) throw new RuntimeException("Error: OR query cannot be null.");
orQueries.add(orQuery);
return this;
}
// text and attributes
// TODO
// queries with EXIST, EQUAL, etc...
//
/**
* Finds elements which contain the specified text or having an attribute value containing the query string.
* <p>
* Exact matches have more weight than partial matches. In most cases the query is case-insensitive.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().has("Name").findFirst()} finds the first element which contains the text <em>Name</em></li>
* <li>{@code browser.query().isInList().has("April").find()} finds all elements in a list which contain the text <em>April</em></li>
* </ul>
*
* @param text text or attribute value to search for
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery has(String text) {
queryStrings.add(text);
return this;
}
/**
* Finds elements which contain all of the specified texts or having attribute values containing all of the query strings.
* <p>
* Exact matches have more weight than partial matches. In most cases the query is case-insensitive.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isInList().has(["April", "July", "August"]).find()} finds all elements in a list which contain
* the texts <em>April</em>, <em>July</em> and <em>August</em></li>
* </ul>
*
* @param textList text or attribute values to search for
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery has(ArrayList<String> textList) {
queryStrings.addAll(textList);
return this;
}
/**
* Finds elements with containing text.
*
* @param query
* @return this {@link ElementQuery} instance
*/
// public ElementQuery containsText(String query) {
// textQueries.add(query);
// return this;
// }
/**
* Finds elements with specific attribute values.
*
* @param query
* @return this {@link ElementQuery} instance
*/
// public ElementQuery attribute(String query) {
// attributeQueries.add(query);
// return this;
// }
// element types and closeness
/**
* Finds elements by their tag name.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().tag("a").findFirst()} finds the first link element on the current page</li>
* <li>{@code browser.query().has("Today").tag("p").find()} finds all paragraph elements which contain the text <em>Today</em></li>
* </ul>
*
* @param name element tag name
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery tag(String name) {
conditions.add(new TagnameCondition(name));
return this;
}
/**
* Finds elements of the specified type.
*
* Possible element types are:
* ALL all elements on the page
* TEXT elements with text in them
* TITLE headlines and elements with bigger font size
* CLICKABLE clickable elements like links or buttons
* TYPABLE input elements which can be used to enter text
* CHOOSABLE input elements like drop downs or select lists
* DATEPICKER datepicker elements for selecting date and time
* IMAGE images
* LIST lists
* TABLE tables
* INLIST items in a list
* INTABLE cells in a table
* FRAME embedded frames and iframes
*
* Example: browser.query().is(ElementType.CLICKABLE).find()
*
* @param elementType
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
private HtmlQuery is(ElementType elementType) {
conditions.add(new ElementCondition(browser, elementType));
elementTypes.add(elementType);
return this;
}
/**
* Finds text elements, which is the most general search criteria. It prefers text passages,
* paragraphs, blocks, list items or table cells, but returns any element if there are no other matches.
* <p>
* {@code isText()} should usually be used in combination with {@link HtmlQuery#has(String)}.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isText().has("Hello").find()} finds all elements containing the text <em>Hello</em></li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery isText() {
return is(ElementType.TEXT);
}
/**
* Finds title elements, which are all elements with headline tags({@code <h1>}, {@code <h2>}, ..., {@code <h6>})
* and elements with a bigger font size than the default one.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isTitle().find()} finds all title elements</li>
* <li>{@code browser.query().isTitle().isClickable().find()} finds all clickable title elements</li>
* <li>{@code browser.query().isTitle().isClickable().has("Article").find()} finds all clickable title elements
* containing the text <em>Article</em></li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery isTitle() {
return is(ElementType.TITLE);
}
/**
* Finds clickable elements like links and buttons.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isClickable().find()} finds all clickable elements like links or buttons,
* see also {@link Browser#click(String)}</li>
* <li>{@code HtmlElements clickablesInTable = browser.query().isClickable().isInTable().find()} finds all clickable elements in table cells</li>
* <li>{@code ArrayList<String> urls = clickablesInTable.getUrls()} extracts all URLs from the clickable items, see also
* {@link HtmlElement#getUrl()} and {@link HtmlElements#getUrls()}.</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#click(String)
*/
public HtmlQuery isClickable() {
return is(ElementType.CLICKABLE);
}
/**
* Finds typable elements like input fields, text areas or rich-text-editors like tinyMCE, which can be used
* to enter text.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isTypable().find()} finds all typable elements which can be used
* to enter text, see also {@link Browser#type(String, String)}</li>
* <li>{@code HtmlElement input = browser.query().isTypable().has("address").findFirst()} finds text input labeled <em>address</em></li>
* <li>{@code input.type("Miller Street 123").submit()} enters the text <em>Miller Street 123</em> in the text field and submits the form, see
* also {@link HtmlElement#type(String)}, {@link HtmlElement#submit()} is optional.</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#type(String, String)
*/
public HtmlQuery isTypable() {
return is(ElementType.TYPABLE);
}
/**
* Finds choosable elements, which are usually drop-down boxes or input boxes with multiple selectable items.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isChoosable().find()} finds all choosable elements, see also {@link Browser#choose(String, String)}</li>
* <li>{@code HtmlElement language = browser.query().isChoosable().has("Language").findFirst()} finds choosable labeled <em>Language</em></li>
* <li>{@code language.select("English").submit()} selects the language item labeled <em>English</em> and submit the form, see
* also {@link HtmlElement#choose(String)}, {@link HtmlElement#submit()} is optional.</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery isChoosable() {
return is(ElementType.CHOOSABLE);
}
/**
* Finds date picker elements, which are usually drop-down boxes or calendar widgets.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>TODO</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery isDatepicker() {
return is(ElementType.DATEPICKER);
}
/**
* Finds image elements. Images defined as background image can not be found properly at the moment.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isImage().find()} finds all image elements on the current page</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
// TODO image size
public HtmlQuery isImage() {
return is(ElementType.IMAGE);
}
/**
* Finds list elements, which are the root elements of lists, usually {@code <ul>}, {@code <ol>} or {@code <dl>} elements.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code HtmlElement list = browser.query().isList().has("Country").findFirst()} finds first list element labeled or
* containing the text <em>Country</em>, see also {@link Browser#getList(String)}</li>
* <li>{@code List countryList = browser.getList(list)} returns the list representation of that element, see also
* {@link Browser#getList(HtmlElement)}</li>
* <li>{@code list.get(0)} gets the first item of that list, see also {@link List#get(int)}</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#getList(HtmlElement)
* @see Browser#getList(String)
* @see List
*/
public HtmlQuery isList() {
return is(ElementType.LIST);
}
/**
* Finds elements in lists, which are usually {@code <li>} or {@code <dt>} elements.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isInList().has("Name").find()} finds all elements in a list,
* which are labeled <em>Name</em></li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#getList(HtmlElement)
* @see Browser#getList(String)
* @see List
*/
public HtmlQuery isInList() {
return is(ElementType.INLIST);
}
/**
* Finds table elements, which are the root elements of tables, usually {@code <table>} elements.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code HtmlElement table = browser.query().isTable().has("Version").findFirst()} finds first table element labeled
* or containing the text <em>Version</em>, see also {@link Browser#getTable(String)}</li>
* <li>{@code Table countryTable = browser.getTable(table)} returns the table representation of that element, see also
* {@link Browser#getTable(HtmlElement)}</li>
* <li>{@code table.getCell(0,0)} gets the cell in the upper left corner of that table, see also {@link Table#getCell(int, int)}</li>
* <li>see {@link Table} for more examples how to handle Table representations</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#getTable(HtmlElement)
* @see Browser#getTable(String)
* @see Table
*/
public HtmlQuery isTable() {
return is(ElementType.TABLE);
}
/**
* Finds elements in tables, which are usually {@code <td>} or {@code <th>} elements.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code browser.query().isInTable().has("$").find()} finds all elements in a table,
* which contain the character <em>$</em></li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#getTable(HtmlElement)
* @see Browser#getTable(String)
* @see Table
*/
public HtmlQuery isInTable() {
return is(ElementType.INTABLE);
}
/**
* Finds frame elements, which are parts of the page with separated content, usually {@code <frame>} and {@code <iframe>} elements.
* <p>
* <strong>Examples:</strong>
* <ul>
* <li>{@code HtmlElement frame = browser.query().isFrame().findFirst()} finds the first frame on the page, see also {@link Browser#frame()} for switching directly (set focus) to a frame</li>
* <li>{@code browser.frame().switchTo(frame)} sets focus on the selected frame. all browser interactions will take place in this frame from now on</li>
* </ul>
*
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see Browser#frame()
*/
public HtmlQuery isFrame() {
return is(ElementType.FRAME);
}
/**
* Finds elements visually close to the given reference elements.
* <p>
* The Direction determines if found elements will be filtered out in case they are visually at another location.
* The found elements will be automatically ordered by closeness to the referenceElements.
* The distance is calculated by the euclidean measure.
*
* @param referenceElements
* @param direction see {@link Direction} for a description of all possible values
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery closestTo(HtmlElements referenceElements, Direction direction) {
conditions.add(new ClosenessCondition(referenceElements, direction));
return this;
}
/**
* Finds elements visually close to the given reference element with the specified label.
* <p>
* The found elements will be automatically ordered by closeness to the referenceElements.
* The distance is calculated by the euclidean measure.
*
* @param query the visible label or hidden attribute of the element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery closestTo(String query) {
return null;
// return closestTo(new HtmlElements(browser.query().has(query).findFirst()), Direction.CLOSE);
}
/**
* Finds elements visually close to the given reference element.
* <p>
* The found elements will be automatically ordered by closeness to the referenceElements.
* The distance is calculated by the euclidean measure.
*
* @param referenceElement elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery closestTo(HtmlElement referenceElement) {
return closestTo(new HtmlElements(referenceElement), Direction.CLOSE);
}
/**
* Finds elements visually close to the given reference elements.
* <p>
* The found elements will be automatically ordered by closeness to the referenceElements.
* The distance is calculated by the euclidean measure.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery closestToAny(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.CLOSE);
}
/**
* Finds elements <strong>above</strong> the element with the specified label.
* <p>
* Elements are above if their bottom y coordinate is lower than the top y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* - <span> -
* ----------
* ---------
* - <div> -
* ---------
* --------
* - <ul> -
* --------
* }</pre>
* In this example, the {@code <div>} is above the {@code <ul>}, but the {@code <span>} is not because its left border is not
* in horizontal bounds of {@code <ul>}. Then again, the {@code <span>} is above the {@code <div>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param query the visible label or hidden attribute of the element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery above(String query) {
return null;
// return closestTo(new HtmlElements(browser.query().has(query).findFirst()), Direction.ABOVE);
}
/**
* Finds elements <strong>above</strong> the reference element.
* <p>
* Elements are above if their bottom y coordinate is lower than the top y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* - <span> -
* ----------
* ---------
* - <div> -
* ---------
* --------
* - <ul> -
* --------
* }</pre>
* In this example, the {@code <div>} is above the {@code <ul>}, but the {@code <span>} is not because its left border is not
* in horizontal bounds of {@code <ul>}. Then again, the {@code <span>} is above the {@code <div>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param referenceElement elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery above(HtmlElement referenceElement) {
return closestTo(new HtmlElements(referenceElement), Direction.ABOVE);
}
/**
* Finds elements visually <strong>above any</strong> of the given reference elements.
* <p>
* Elements are above if their bottom y coordinate is lower than the top y coordinate of any reference element.
* In addition, the element has to be in horizontal bounds of any reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* - <span> -
* ----------
* --------- ---------
* - <div> - - <div> -
* --------- ---------
* -------- --------
* - <ul> - - <ul> -
* -------- --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s are above the {@code <ul>}s,
* but the {@code <span>} is not because its left and right border is not in horizontal bounds of any of the {@code <ul>}s.
* Then again, the {@code <span>} is above from both of the {@code <div>}s, if they are the reference elements.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #above(HtmlElement)
*/
public HtmlQuery aboveAny(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.ABOVE);
}
/**
* Finds elements visually <strong>above all</strong> of the given reference elements.
* <p>
* Elements are above if their bottom y coordinate is lower than the top y coordinate of any reference element.
* In addition, the element has to be in horizontal bounds of any reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* - <span> -
* ----------
* --------- ---------
* - <div> - - <div> -
* --------- ---------
* -------- --------
* - <ul> - - <ul> -
* -------- --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s and also the {@code <span>} are not
* above them, because they are not above from at least one of the {@code <ul>}s. But if the {@code <div>}s are the reference
* elements, the {@code <span>} is above both of them.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #above(HtmlElement)
*/
public HtmlQuery aboveAll(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.ABOVE_ALL);
}
/**
* Finds elements <strong>below</strong> the element with the specified label.
* <p>
* Elements are below if their top y coordinate is higher than the bottom y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* --------
* ---------
* - <div> -
* ---------
* ----------
* - <span> -
* ----------
* }</pre>
* In this example, the {@code <div>} is below the {@code <ul>}, but the {@code <span>} is not because its left border is not
* in horizontal bounds of {@code <ul>}. Then again, the {@code <span>} is below the {@code <div>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param query the visible label or hidden attribute of the element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery below(String query) {
return null;
// return closestTo(new HtmlElements(browser.query().has(query).findFirst()), Direction.BELOW);
}
/**
* Finds elements <strong>below</strong> the reference element.
* <p>
* Elements are below if their top y coordinate is higher than the bottom y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* --------
* ---------
* - <div> -
* ---------
* ----------
* - <span> -
* ----------
* }</pre>
* In this example, the {@code <div>} is below the {@code <ul>}, but the {@code <span>} is not because its left border is not
* in horizontal bounds of {@code <ul>}. Then again, the {@code <span>} is below the {@code <div>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param referenceElement elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery below(HtmlElement referenceElement) {
return closestTo(new HtmlElements(referenceElement), Direction.BELOW);
}
/**
* Finds elements visually <strong>below any</strong> of the given reference elements.
* <p>
* Elements are below if their top y coordinate is higher than the bottom y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* -------- --------
* - <ul> - - <ul> -
* -------- --------
* --------- ---------
* - <div> - - <div> -
* --------- ---------
* ----------
* - <span> -
* ----------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s are below the {@code <ul>}s,
* but the {@code <span>} is not because its left and right border is not in horizontal bounds of any of the {@code <ul>}s.
* Then again, the {@code <span>} is below from both of the {@code <div>}s, if they are the reference elements.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #below(HtmlElement)
*/
public HtmlQuery belowAny(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.BELOW);
}
/**
* Finds elements visually <strong>below all</strong> of the given reference elements.
* <p>
* Elements are below if their top y coordinate is higher than the bottom y coordinate of the reference element.
* In addition, the element has to be in horizontal bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* -------- --------
* - <ul> - - <ul> -
* -------- --------
* --------- ---------
* - <div> - - <div> -
* --------- ---------
* ----------
* - <span> -
* ----------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s and also the {@code <span>} are not
* above them, because they are not above from at least one of the {@code <ul>}s. But if the {@code <div>}s are the reference
* elements, the {@code <span>} is above both of them.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #below(HtmlElement)
*/
public HtmlQuery belowAll(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.BELOW_ALL);
}
/**
* Finds elements <strong>left to</strong> the element with the specified label.
* <p>
* Elements are left if their right x coordinate is lower than the left x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* -------- - <span> -
* - <ul> - ----------
* -------- ---------
* - <div> -
* ---------
* }</pre>
* In this example, the {@code <ul>} is left to the {@code <span>} and to the {@code <div>}, but the {@code <div>} is
* not left to the {@code <span>}, because its top border is not in vertical bounds of {@code <span>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param query the visible label or hidden attribute of the element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery leftTo(String query) {
return null;
// return closestTo(new HtmlElements(browser.query().has(query).findFirst()), Direction.LEFT);
}
/**
* Finds elements <strong>left to</strong> the reference element.
* <p>
* Elements are left if their right x coordinate is lower than the left x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* -------- - <span> -
* - <ul> - ----------
* -------- ---------
* - <div> -
* ---------
* }</pre>
* In this example, the {@code <ul>} is left to the {@code <span>} and to the {@code <div>}, but the {@code <div>} is
* not left to the {@code <span>}, because its top border is not in vertical bounds of {@code <span>}.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param referenceElement elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery leftTo(HtmlElement referenceElement) {
return closestTo(new HtmlElements(referenceElement), Direction.LEFT);
}
/**
* Finds elements visually <strong>left to any</strong> of the given reference elements.
* <p>
* Elements are left if their right x coordinate is lower than the left x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* --------- --------
* - <div> -
* ---------- ---------
* - <span> -
* ---------- ----------
* - <div> -
* --------- --------
* - <ul> -
* --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s are left to the {@code <ul>}s,
* but the {@code <span>} is not because its top and bottom border is not in vertical bounds of any of the {@code <ul>}s.
* Then again, the {@code <span>} is left to both of the {@code <div>}s, if they are the reference elements.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #leftTo(HtmlElement)
*/
public HtmlQuery leftToAny(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.LEFT);
}
/**
* Finds elements visually <strong>left to all</strong> of the given reference elements.
* <p>
* Elements are left if their right x coordinate is lower than the left x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* --------- --------
* - <div> -
* ---------- ---------
* - <span> -
* ---------- ----------
* - <div> -
* --------- --------
* - <ul> -
* --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s and also the {@code <span>} are not
* left to them, because they are not left to at least one of the {@code <ul>}s. But if the {@code <div>}s are the reference
* elements, the {@code <span>} is left to both of them.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #leftTo(HtmlElement)
*/
public HtmlQuery leftToAll(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.LEFT_ALL);
}
/**
* Finds elements <strong>right to</strong> the element with the specified label.
* <p>
* Elements are right if their left x coordinate is higher than the right x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* -------- - <span> -
* - <ul> - ----------
* -------- ---------
* - <div> -
* ---------
* }</pre>
* In this example, the {@code <span>} and the {@code <div>} are right to the {@code <ul>}, but the {@code <span>}
* is not right to the {@code <div>} because its bottom border is not in vertical bounds of the reference element.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param query the visible label or hidden attribute of the element
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery rightTo(String query) {
return null;
// return closestTo(new HtmlElements(browser.query().has(query).findFirst()), Direction.RIGHT);
}
/**
* Finds elements <strong>right to</strong> the reference element.
* <p>
* Elements are right if their left x coordinate is higher than the right x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* ----------
* -------- - <span> -
* - <ul> - ----------
* -------- ---------
* - <div> -
* ---------
* }</pre>
* In this example, the {@code <span>} and the {@code <div>} are right to the {@code <ul>}, but the {@code <span>}
* is not right to the {@code <div>} because its bottom border is not in vertical bounds of the reference element.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* the reference element.
*
* @param referenceElement elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery rightTo(HtmlElement referenceElement) {
return closestTo(new HtmlElements(referenceElement), Direction.RIGHT);
}
/**
* Finds elements visually <strong>right to any</strong> of the given reference elements.
* <p>
* Elements are right if their left x coordinate is higher than the right x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* -------- ---------
* - <div> -
* --------- ----------
* - <span> -
* --------- ----------
* - <div> -
* -------- ---------
* - <ul> -
* --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s are right to the {@code <ul>}s,
* but the {@code <span>} is not because its top and bottom borders are not in vertical bounds of any of the {@code <ul>}s.
* Then again, the {@code <span>} is right to both of the {@code <div>}s, if they are the reference elements.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #rightTo(HtmlElement)
*/
public HtmlQuery rightToAny(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.RIGHT);
}
/**
* Finds elements visually <strong>right to all</strong> of the given reference elements.
* <p>
* Elements are right if their left x coordinate is higher than the right x coordinate of the reference element.
* In addition, the element has to be in vertical bounds of the reference element.
* <p>
* <strong>Example:</strong> (visual representation of the web page)
* <pre>{@code
* .
* --------
* - <ul> -
* -------- ---------
* - <div> -
* --------- ----------
* - <span> -
* --------- ----------
* - <div> -
* -------- ---------
* - <ul> -
* --------
* }</pre>
* If the {@code <ul>}s are the reference elements in this example, the {@code <div>}s and also the {@code <span>} are not
* right to them, because they are not right to at least one of the {@code <ul>}s. But if the {@code <div>}s are the reference
* elements, the {@code <span>} is right to both of them.
* <p>
* Calling <code>find()</code> will order the result by closeness, so that the first result is the closest element to
* any of the reference elements.
*
* @param referenceElements elements which are taken as reference for measuring the distance
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
* @see #rightTo(HtmlElement)
*/
public HtmlQuery rightToAll(HtmlElements referenceElements) {
return closestTo(referenceElements, Direction.RIGHT_ALL);
}
// color selectors
/**
* Checks if image is covered by the specified color, considering the given tolerance.
*
* @see HtmlQuery#isColor(Color, double)
* @return true if image is covered by the color
*/
public HtmlQuery isColor(String color, Tolerance tolerance) {
ColorName colorName = null;
try {
// try to find the right color name
colorName = ColorName.valueOf(color.toUpperCase());
} catch (Exception e) {
return this;
}
return isColor(colorName.getColor(), tolerance.getValue());
}
/**
* Checks if image is covered by the specified color, considering the given tolerance.
*
* @see HtmlQuery#isColor(Color, double)
* @return true if image is covered by the color
*/
public HtmlQuery isColor(Color color, Tolerance tolerance) {
return isColor(color, tolerance.getValue());
}
/**
* Checks if image is covered by the specified color, considering the given tolerance.
*
* @param color the color value to check
* @param tolerance the lower the value, the lower the tolerance regarding the color
* distance. <code>1</code> returns always true, <code>0</code> is only true if the
* image is completely filled with that exact color
* @return true if image is covered by the color
*/
public HtmlQuery isColor(Color color, double tolerance) {
conditions.add(new ColorCondition(color, tolerance));
return this;
}
/**
* Checks if image contains the specified color, considering the given dominance and tolerance.
*
* @see HtmlQuery#hasColor(Color, double, double)
* @return true if image is covered by the color
*/
public HtmlQuery hasColor(String color, double tolerance, double dominance) {
ColorName colorName = null;
try {
// try to find the right color name
colorName = ColorName.valueOf(color.toUpperCase());
} catch (Exception e) {
return this;
}
return hasColor(colorName.getColor(), tolerance, dominance);
}
/**
* Checks if image contains the specified color, considering the given dominance and tolerance.
*
* @see HtmlQuery#hasColor(Color, double, double)
* @return true if image is covered by the color
*/
public HtmlQuery hasColor(Color color, Tolerance tolerance, double dominance) {
return hasColor(color, tolerance.getValue(), dominance);
}
/**
* Checks if image contains the specified color, considering the given dominance and tolerance.
*
* @see HtmlQuery#hasColor(Color, double, double)
* @return true if image is covered by the color
*/
public HtmlQuery hasColor(Color color, double tolerance, Dominance dominance) {
return hasColor(color, tolerance, dominance.getValue());
}
/**
* Checks if image contains the specified color, considering the given dominance and tolerance.
*
* @see HtmlQuery#hasColor(Color, double, double)
* @return true if image is covered by the color
*/
public HtmlQuery hasColor(Color color, Tolerance tolerance, Dominance dominance) {
return hasColor(color, tolerance.getValue(), dominance.getValue());
}
/**
* Checks if image contains the specified color, considering the given dominance and tolerance.
*
* @param color the color value to check, see also {@link ColorName#getColor()}
* @param dominance the lower the value, the lower the dominance of the color in the
* image within the tolerance parameter. <code>0</code> is always passed through,
* <code>1</code> is only true if the image has exclusively colors within the tolerance
* range
* @param tolerance the lower the value, the lower the tolerance regarding the color
* distance. <code>1</code> is always passed through, <code>0</code> is only true if the
* image has enough dominant pixels with that exact color
* @return true if image contains the color
*/
public HtmlQuery hasColor(Color color, double tolerance, double dominance) {
conditions.add(new ColorCondition(color, tolerance, dominance));
return this;
}
// custom selectors
/**
* Uses CSS selector to find elements.
* <p>
* <strong>Example:</strong>
* <pre>
* cssSelector("div#content > p.info > strong[name=author]")
* </pre>
*
* @param query the CSS selector expression
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery cssSelector(String query) {
conditions.add(new SelectorCondition(query, QueryType.CSS));
return this;
}
/**
* Uses Xpath selector to find elements.
* <p>
* <strong>Example:</strong>
* <pre>
* xPathSelector("//div[id=content]/p[class=info]/strong[name=author]")
* </pre>
*
* @param query the xPath selector expression
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery xPathSelector(String query) {
conditions.add(new SelectorCondition(query, QueryType.XPATH));
return this;
}
/**
* Uses JQuery selector to find elements.
* <p>
* <strong>Example:</strong>
* <pre>
* jQuerySelector("find(div button:only-child)")
* </pre>
*
* @param query the jQuery selector expression
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery jQuerySelector(String query) {
conditions.add(new SelectorCondition(query, QueryType.JQUERY));
return this;
}
// post processing of results
/**
* Limits the result set to the specified amount.
*
* @param count maximum number of results, no limit if null
* @return this {@link HtmlQuery} instance, which can be used to add more search criteria like this or to finally
* execute the query with {@link #find()} or {@link #findFirst()}
*/
public HtmlQuery limit(Integer count) {
limit = count;
return this;
}
/**
* Orders the result set by the specified order type.
*
* @param orderType
* @return this {@link ElementQuery} instance
*/
// public Finder orderBy(OrderType orderType) {
// orderBy = orderType;
// return this;
// }
// find logic
/**
* Finds best matching element which match the given search conditions. Waits for the element to appear.
* <p>
* Returns {@code null} if no element was found after the waiting time.
*
* @return the {@link HtmlElement} result
*/
public HtmlElement findFirstWithWait() {
limit = 1;
resultElements = findWithWait();
return resultElements instanceof HtmlElements && !resultElements.isEmpty() ? resultElements.first() : null;
}
/**
* Finds best matching element which match the given search conditions.
* <p>
* Returns {@code null} if no element was found.
*
* @return the {@link HtmlElement} result
*/
public HtmlElement findFirst() {
limit = 1;
resultElements = find();
return resultElements instanceof HtmlElements && !resultElements.isEmpty() ? resultElements.first() : null;
}
/**
* Finds all elements which match the given search conditions. Waits for the element to appear.
* <p>
* Returns {@code null} if no element was found after the waiting time.
*
* @return the {@link HtmlElements} result set
*/
public HtmlElements findWithWait() {
doFind();
if(resultElements == null || resultElements.isEmpty()) {
// try {
// browser.waitFor().query(this);
doFind();
// } catch (TimeoutException e) {
// element not found
// browser.log().info("Query: element not found for query " + this.toString());
// }
}
return resultElements;
}
/**
* Finds all elements which match the given search conditions.
* <p>
* Returns {@code null} if no element was found.
*
* @return the {@link HtmlElements} result set
*/
public HtmlElements find() {
return doFind();
}
private HtmlElements doFind() {
resultElements = null;
// if no condition is set, add the "all elements" matcher
if(elementsToFilter.isEmpty() && (conditions.isEmpty() || !conditions.hasElementFinder())) {
conditions.add(new ElementCondition(browser, ElementType.ALL));
}
Map<String, HtmlElements> referenceElements = new HashMap<String, HtmlElements>();
Map<String, HtmlElements> labelElements = new HashMap<String, HtmlElements>();
JSONArray jsonConditions = new JSONArray();
JSONArray jsonClosenessConditions = new JSONArray();
JSONArray jsonColorConditions = new JSONArray();
boolean hasColorConditions = false;
// building queries, which will be executed as javascript/jquery commands
try {
for(Condition condition: conditions) {
// TODO remove isElementFinder
if(condition.isElementFinder()) {
if(condition instanceof ElementCondition) {
if(!queryStrings.isEmpty()) {
// TODO queries for frame is "frame name"
((ElementCondition) condition).addQueries(queryStrings);
}
}
JSONObject jsonCondition = new JSONObject();
JSONArray jsonSelectorGroups = new JSONArray();
JSONArray jsonLabelSelectorGroups = new JSONArray();
JSONArray jsonFallbackSelectorGroups = new JSONArray();
SelectorGroups selectorGroups = condition.getSelectorGroups();
for(SelectorGroup selectorGroup: selectorGroups) {
Type selectorGroupType = selectorGroup.getType();
JSONObject jsonSelectorGroup = new JSONObject();
JSONArray jsonSelectors = new JSONArray();
for(Selector selector: selectorGroup) {
JSONObject jsonSelector = new JSONObject();
jsonSelector.put("weight", selector.getWeight());
jsonSelector.put("command", selector.getExpressionAsJQueryCommand());
jsonSelectors.put(jsonSelector);
}
if(selectorGroup.hasReferenceElements()) {
String referenceId = Long.toString(System.currentTimeMillis()) + Double.toString(Math.random());
jsonSelectorGroup.put("referenceId", referenceId);
referenceElements.put(referenceId, selectorGroup.getReferenceElements());
}
jsonSelectorGroup.put("weight", selectorGroup.getWeight());
jsonSelectorGroup.put("limit", selectorGroup.getLimit());
jsonSelectorGroup.put("selectors", jsonSelectors);
if(selectorGroupType.equals(Type.NORMAL)) {
jsonSelectorGroups.put(jsonSelectorGroup);
} else if(selectorGroupType.equals(Type.LABEL)) {
jsonLabelSelectorGroups.put(jsonSelectorGroup);
} else if(selectorGroupType.equals(Type.FALLBACK)) {
jsonFallbackSelectorGroups.put(jsonSelectorGroup);
}
}
if(selectorGroups.hasLabelElements()) {
String labelId = Long.toString(System.currentTimeMillis()) + Double.toString(Math.random());
jsonCondition.put("labelId", labelId);
jsonCondition.put("labelType", selectorGroups.getLabelType().toString().toLowerCase());
labelElements.put(labelId, selectorGroups.getLabelElements());
}
if(condition instanceof ElementCondition) {
jsonCondition.put("elementType", ((ElementCondition) condition).getElementType().toString().toLowerCase());
}
jsonCondition.put("type", condition.getType());
jsonCondition.put("selectorGroups", jsonSelectorGroups);
jsonCondition.put("labelSelectorGroups", jsonLabelSelectorGroups);
jsonCondition.put("fallbackSelectorGroups", jsonFallbackSelectorGroups);
if(condition instanceof ClosenessCondition) {
jsonCondition.put("isClosenessCondition", "true");
jsonClosenessConditions.put(jsonCondition);
} else if(condition instanceof ColorCondition) {
hasColorConditions = true;
jsonCondition.put("isColorCondition", "true");
jsonColorConditions.put(jsonCondition);
} else {
jsonConditions.put(jsonCondition);
}
}
}
// System.out.println(jsonConditions.toString(2));
// System.out.println(jsonClosenessConditions.toString(2));
// System.out.println(jsonColorConditions.toString(2));
} catch (JSONException e) {
e.printStackTrace();
}
// send screenshot if color queries requested
if(hasColorConditions) {
try {
byte[] pageAsPNGByteArray = ((TakesScreenshot) browser.getWebDriver()).getScreenshotAs(OutputType.BYTES);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(pageAsPNGByteArray));
// encode image as base64 string
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
baos.flush();
pageAsPNGByteArray = baos.toByteArray();
baos.close();
// TODO find way to take only one screenshot
// String pageAsBase64PNG = "data:image/png;base64," + Base64.encodeBase64URLSafeString(pageAsPNGByteArray);
String pageAsBase64PNG = "data:image/png;base64," + ((TakesScreenshot) browser.getWebDriver()).getScreenshotAs(OutputType.BASE64);
// TODO example base64 png
// pageAsBase64PNG = "data:image/png;base64," + "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";
if(pageAsBase64PNG.length() >= 500000) {
for (int i = 0; i < pageAsBase64PNG.length(); i += 500000) {
browser.javaScript("abmash.buildImageDataForPageScreenshot(arguments[0])", pageAsBase64PNG.substring(i, Math.min(i + 499999, pageAsBase64PNG.length())));
}
browser.javaScript("abmash.updatePageScreenshot(arguments[0], arguments[1])", image.getWidth(), image.getHeight());
} else {
browser.javaScript("abmash.buildImageDataForPageScreenshot(arguments[0]); abmash.updatePageScreenshot(arguments[1], arguments[2])", pageAsBase64PNG, image.getWidth(), image.getHeight());
}
// browser.javaScript("abmash.updatePageScreenshot(arguments[0], arguments[1], arguments[2])", pageAsBase64PNG, image.getWidth(), image.getHeight());
} catch (IOException e) {
e.printStackTrace();
}
}
if(!elementsToFilter.isEmpty()) {
System.out.println(elementsToFilter);
}
// sending queries to jquery executor
// String queryId = Long.toString(System.currentTimeMillis()) + Double.toString(Math.random());
String script = "return abmash.query(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);";
@SuppressWarnings("unchecked")
ArrayList<RemoteWebElement> seleniumElements = (ArrayList<RemoteWebElement>) browser.javaScript(
script,
// queryId,
jsonConditions.toString(),
jsonClosenessConditions.toString(),
jsonColorConditions.toString(),
rootElements,
elementsToFilter,
referenceElements,
labelElements,
limit
).getReturnValue();
// converting selenium elements to abmash elements
HtmlElements tempElements = new HtmlElements();
for(RemoteWebElement seleniumElement: seleniumElements) {
HtmlElement element = new HtmlElement(browser, seleniumElement);
// if filter elements are used, check if the elements match
if(elementsToFilter.isEmpty() || elementsToFilter.contains(element)) {
tempElements.add(new HtmlElement(browser, seleniumElement));
}
}
// System.out.println("tempElements: " + tempElements + " -- " + conditions);
// or queries
for(HtmlQuery orQuery: orQueries) {
orQuery.childOf(rootElements);
HtmlElements orElements = orQuery.find();
for(HtmlElement orElement: orElements) {
// if filter elements are used, check if the elements match
if(elementsToFilter.isEmpty() || elementsToFilter.contains(orElement)) {
tempElements.add(orElement);
}
}
}
// not queries
for(HtmlQuery notQuery: notQueries) {
// process not query if result is not empty already
if(!tempElements.isEmpty()) {
notQuery.childOf(rootElements);
HtmlElements notElements = notQuery.find();
HtmlElements subQueryElements = new HtmlElements();
for (HtmlElement tempElement: tempElements) {
// add only elements which are not returned by last not query
if(!notElements.contains(tempElement)) {
subQueryElements.add(tempElement);
}
}
// set temp elements to narrowed down results
tempElements = subQueryElements;
}
}
// filter out invalid matches
HtmlElements unsortedElements = new HtmlElements();
for (HtmlElement tempElement: tempElements) {
if(conditions.elementValid(tempElement)) {
tempElement.setWindowName(browser.window().getCurrentWindowName());
unsortedElements.add(tempElement);
}
}
// System.out.println("unsortedElements: " + unsortedElements + " -- " + conditions);
// fetch and store data for elements
// this needs to be done before sorting because of better performance
// unsortedElements.fetchDataForCache();
if(!elementTypes.isEmpty()) {
// set element types if specified by query
unsortedElements.setTypes(elementTypes);
}
// sort results
// TODO sorting completely in jquery
// TODO no sorting of OR'ed queries atm
HtmlElements sortedElements = unsortedElements;
for (Condition condition: conditions) {
// TODO what to do if there are several and/or different sorts?
if(condition instanceof ClosenessCondition) {
sortedElements = condition.sortElements(unsortedElements);
break;
}
}
// limit results
HtmlElements limitedElements = new HtmlElements();
for (HtmlElement sortedElement: sortedElements) {
if(limit == 0 || limitedElements.size() < limit) {
sortedElement.setWindowName(browser.window().getCurrentWindowName());
// eliminate duplicates
limitedElements.addAndIgnoreDuplicates(sortedElement);
}
}
// finalize results
resultElements = limitedElements;
// System.out.println(resultElements + " -- " + conditions);
return resultElements;
}
// public HtmlElements findOld() {
// if(!(resultElements instanceof HtmlElements)) {
// // find elements
// browser.log().debug("Query: Searching...");
// HtmlElements unsortedElements = doFind();
//
//// System.out.println("elements after ANDs: " + unsortedElements);
//
// // sort result
// HtmlElements sortedElements = sortElements(unsortedElements);
//
//// System.out.println("elements after filtering and sorting: " + sortedElements);
//
// if(unsortedElements.size() > 0) browser.log().debug("Query: Result found" +
// (sortedElements.size() > 1 ? " and sorted" : ""));
//
// // check OR queries to add them to the result set
// for (HtmlQuery orQuery: orQueries) {
// for (HtmlElement orElement: orQuery.find()) {
// // do not add the element if it is contained in the elements that need to be filtered out
// if(!notElements.contains(orElement)) sortedElements.addAndIgnoreDuplicates(orElement);
// }
// }
//
//// System.out.println("elements after ORs: " + unsortedElements);
//
// // limit result
// HtmlElements limitedElements = new HtmlElements();
// if(limit > 0 && sortedElements.size() > limit) {
// for (int i = 0; i < limit; i++) {
// limitedElements.add(sortedElements.get(i));
// }
// } else {
// limitedElements = sortedElements;
// }
// resultElements = limitedElements;
// }
//
// // log if result is empty
// if(!(resultElements instanceof HtmlElements) || resultElements.isEmpty()) {
// browser.log().debug("Query: No element found for " + this);
// resultElements = null;
// } else {
// resultElements.fetchDataForCache();
// }
//
// return resultElements;
// }
//
//
// /**
// * Searches for all matching elements
// *
// * @return
// */
// private HtmlElements doFind() {
// HtmlElements matchingElements = new HtmlElements();
//
// // if query specified without element type, tag or selector, search for any element
// boolean selectAnyElement = true;
// for (Condition condition: conditions) {
// // TODO tag() or select() with has() does not work properly
// if(queryStrings.isEmpty()) {
// if(condition instanceof ElementCondition || condition instanceof TagnameCondition || condition instanceof SelectorCondition) {
// selectAnyElement = false;
// break;
// }
// } else {
// if(condition instanceof ElementCondition) {
// selectAnyElement = false;
// break;
// }
// }
// }
//
// // if no condition specified search for any text element
// if(selectAnyElement || conditions.isEmpty()) {
// conditions.add(new ElementCondition(browser, ElementType.ALL));
// }
//
// // if no query specified search for anything
// if(queryStrings.isEmpty()) queryStrings.add("");
//
// // TODO when fallback conditions needed, use them one after another, or for all (inclusively the already processed conditions)
// for (Condition condition: conditions) {
// if(condition.isElementFinder()) {
// HtmlElements matchingElementsForCondition = new HtmlElements();
//
// // check all regular selectors
// for (String query: queryStrings) {
// // TODO queries for frame is framename
// if(condition instanceof ElementCondition) {
// ((ElementCondition) condition).addQuery(query);
// }
//
// // get selectors
// SelectorGroups selectors = condition.getSelectorGroups();
//
// // get matching elements
// HtmlElements matchingElementsForConditionQuery = matchingElementsForConditionQuery(condition, selectors);
// matchingElementsForCondition.addAll(matchingElementsForConditionQuery);
// matchingElements = matchingElements(matchingElements, matchingElementsForConditionQuery);
//// System.out.println("Checking condition: " + condition);
//// System.out.println(" with selectors: " + selectors);
//// System.out.println("All elements only for condition: " + matchingElementsForConditionQuery);
//// System.out.println("All ANDed elements after condition: " + matchingElements);
//
// // skip to fallback selectors if result is already empty
// if(matchingElements.isEmpty()) break;
// }
// }
//// browser.log().debug("Elements for " + condition + ": {}", matchingElements);
// }
//
// // set element types if specified by query
// if(!elementTypes.isEmpty()) {
// matchingElements.setTypes(elementTypes);
// }
//
//
// // return matching elements or empty list
// return matchingElements;
// }
//
// private HtmlElements matchingElementsForConditionQuery(Condition condition, SelectorGroups selectorGroups) {
// // selectors are processed OR-wise
// HtmlElements matchingElementsForConditionQuery = new HtmlElements();
// for (SelectorGroup selectorGroup: selectorGroups) {
// // process each group
// HtmlElements groupElements = new HtmlElements();
// for (Selector selector: selectorGroup) {
// // if elements found, do not use fallback selectors
// // TODO ensure correct order of non-fallback and fallback selectors
// if(matchingElementsForConditionQuery.size() > 0 && selectorGroup.getType() == Type.FALLBACK) break;
//
// HtmlElements selectorElements = selectAndFilterElements(selector, condition);
// groupElements.addAllAndIgnoreDuplicates(selectorElements); // selector matches
//
// // continue with next group if enough elements are matching already
// if(groupElements.size() >= selectorGroup.getLimit()) break;
// }
// matchingElementsForConditionQuery.addAllAndIgnoreDuplicates(groupElements); // selector matches
// }
//
//// System.out.println("Elements for condition: " + matchingElementsForConditionQuery);
// return matchingElementsForConditionQuery;
// }
//
// private HtmlElements matchingElements(HtmlElements matchingElements, HtmlElements matchingElementsForConditionQuery) {
// if(matchingElements.isEmpty()) {
// // first condition result is taken as reference
// matchingElements = matchingElementsForConditionQuery;
// } else {
// // next results are compared AND-wise to last result
// HtmlElements temporaryElements = new HtmlElements();
// for (HtmlElement matchingElementForCondition: matchingElementsForConditionQuery) {
// // throw away elements which are not returned in at least one condition
// // also ignore elements that need to be filtered out
// if(matchingElements.contains(matchingElementForCondition)) {
// temporaryElements.add(matchingElementForCondition);
// }
// }
// matchingElements = temporaryElements;
// }
// return matchingElements;
// }
//
// private HtmlElements selectAndFilterElements(Selector selector, Condition condition) {
// HtmlElements foundElements = new HtmlElements();
//
// HtmlElements conditionElements = new HtmlElements();
// if(!rootElements.isEmpty()) {
// for (HtmlElement rootElement: rootElements) {
// try {
// conditionElements.addAll(selector.find(browser, rootElement));
// } catch (Exception e) {
// System.err.println("Find error: skipped selector [" + selector + "] for condition [" + condition + "] on root element [" + rootElement + "]: " + e.getMessage());
//// e.printStackTrace();
//// System.exit(0);
// }
// }
// } else {
// try {
// conditionElements.addAll(selector.find(browser));
// } catch (RuntimeException e) {
// System.err.println("Find error: skipped selector [" + selector + "] for condition [" + condition + "]: " + e.getMessage());
//// e.printStackTrace();
//// System.exit(0);
// }
// }
//
// if(!conditionElements.isEmpty()) {
//// System.out.println(" ### FOUND ELEMENTS: " + conditionElements.size() + " for " + selector + " in " + condition);
// for (HtmlElement foundElement: conditionElements) {
// // TODO set label of typables
// // TODO make sure that the attribute is set in foundElement which is finally returned after filtering out duplicates
//// if(condition instanceof ElementCondition && ((ElementCondition) condition).getElementType() == ElementType.TYPABLE) foundElement.setLabel();
// if(condition.elementValid(foundElement) && !notElements.contains(foundElement)) {
// foundElement.setWindowName(browser.window().getCurrentWindowName());
// foundElements.add(foundElement);
// }
// }
// }
//
// return foundElements;
// }
//
// private HtmlElements sortElements(HtmlElements unsortedElements) {
// // if unsorted elements are empty fill it with all elements if no other conditions were specified
// boolean closenessWithAllElements = false;
// if(unsortedElements.isEmpty()) {
// closenessWithAllElements = true;
// for (Condition condition: conditions) {
// if(!(condition instanceof ClosenessCondition)) closenessWithAllElements = false;
// }
// }
//
// HtmlElements sortedElements = unsortedElements;
// for (Condition condition: conditions) {
//// System.out.println("unsorted elements: " + sortedElements + " (condition: " + condition + ")");
// if(condition instanceof ClosenessCondition) {
// if(closenessWithAllElements && sortedElements.isEmpty()) {
// sortedElements = browser.query().cssSelector("*").find();
// }
//
// // TODO what to do if there are several and/or different sorts?
// sortedElements = condition.sortElements(sortedElements);
// }
//// System.out.println("sorted elements: " + sortedElements + " (condition: " + condition + ")");
// }
// return sortedElements;
// }
}