package com.ggasoftware.uitest.control.new_controls.complex.table;
import com.ggasoftware.uitest.control.Button;
import com.ggasoftware.uitest.control.base.map.MapArray;
import com.ggasoftware.uitest.control.base.pairs.Pair;
import com.ggasoftware.uitest.control.interfaces.base.IBaseElement;
import com.ggasoftware.uitest.control.interfaces.base.IClickableText;
import com.ggasoftware.uitest.control.interfaces.complex.ITable;
import com.ggasoftware.uitest.control.new_controls.common.Text;
import com.ggasoftware.uitest.utils.linqInterfaces.JFuncT;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static com.ggasoftware.uitest.control.base.apiInteract.ContextType.Locator;
import static com.ggasoftware.uitest.control.base.asserter.testNG.Assert.exception;
import static com.ggasoftware.uitest.utils.LinqUtils.*;
import static com.ggasoftware.uitest.utils.PrintUtils.print;
import static com.ggasoftware.uitest.utils.StringUtils.LineBreak;
import static com.ggasoftware.uitest.utils.Timer.waitCondition;
import static com.ggasoftware.uitest.utils.WebDriverByUtils.fillByTemplateSilent;
import static com.ggasoftware.uitest.utils.WebDriverWrapper.TIMEOUT;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* Created by Roman_Iovlev on 6/2/2015.
*/
public class Table<T extends IClickableText, P> extends Text<P> implements ITable<T, P> {
public JFuncT<T> cellCreateTemplate;
protected String[] _footer;
private List<Cell<T, P>> _allCells = new ArrayList<>();
// ------------------------------------------ //
private Columns<T, P> _columns = new Columns<>();
private Rows<T, P> _rows = new Rows<>();
private Class<Button> clazz;
private By _cellLocatorTemplate = By.xpath(".//tr[%s]/td[%s]");
public Table() {
this(null);
//GetFooterFunc = t => t.FindElements(By.xpath("//tfoot/tr/td")).Select(el => el.Text).ToArray();
}
public Table(By tableLocator) {
super(tableLocator);
columns().table = this;
rows().table = this;
clazz = Button.class;
}
public Table(By tableLocator, By cellLocatorTemplate) {
this(tableLocator);
_cellLocatorTemplate = cellLocatorTemplate;
}
public List<Cell<T, P>> getCells() {
for (String columnName : columns().headers())
for (String rowName : rows().headers())
_allCells.add(cell(new Column(columnName), new Row(rowName)));
return _allCells;
}
public Columns<T, P> columns() {
return _columns;
}
public MapArray<String, Cell<T, P>> column(int colNum) {
return rows().getColumn(colNum);
}
public MapArray<String, Cell<T, P>> column(String colName) {
return rows().getColumn(colName);
}
private MapArray<String, Cell<T, P>> column(Column column) {
return column.get(this::column, this::column);
}
public void setColumns(Columns<T, P> value) {
_columns.update(value);
}
public Rows<T, P> rows() {
return _rows;
}
public MapArray<String, Cell<T, P>> row(int rowNum) {
return columns().getRow(rowNum);
}
public MapArray<String, Cell<T, P>> row(String rowName) {
return columns().getRow(rowName);
}
private MapArray<String, Cell<T, P>> row(Row row) {
return row.get(this::row, this::row);
}
public void setRows(Rows<T, P> value) {
_rows.update(value);
}
public void setColumnHeaders(String[] value) {
columns().setHeaders(value);
}
public void setRowHeaders(String[] value) {
rows().setHeaders(value);
}
public void setColCount(int value) {
columns().setCount(value);
}
public void setRowCount(int value) {
rows().setCount(value);
}
protected String[] getFooterAction() {
return select(getWebElement().findElements(By.xpath("//tfoot/tr/td[1]")), WebElement::getText)
.toArray(new String[1]);
}
public void setFooter(String[] value) {
_footer = value;
}
public String[] header() {
return columns().headers();
}
public String[] footer() {
if (_footer != null)
return _footer;
_footer = doJActionResult("Get Footer", this::getFooterAction);
if (_footer == null || _footer.length == 0)
return null;
columns().setCount(_footer.length);
return _footer;
}
public Cell<T, P> cell(Column column, Row row) {
int colIndex = column.get(this::getColumnIndex, num -> num + columns().startIndex - 1);
int rowIndex = row.get(this::getRowIndex, num -> num + rows().startIndex - 1);
return addCell(colIndex, rowIndex,
column.get(name -> asList(columns().headers()).indexOf(name) + 1, num -> num),
row.get(name -> asList(rows().headers()).indexOf(name) + 1, num -> num),
column.get(name -> name, num -> ""),
row.get(name -> name, num -> ""));
}
private List<Cell<T, P>> matches(Collection<Cell<T, P>> list, String regex) {
return new ArrayList<>(where(list, cell -> cell.getValue().matches(regex)));
}
public List<Cell<T, P>> cells(String value) {
return new ArrayList<>(where(getCells(), cell -> cell.getValue().equals(value)));
}
public List<Cell<T, P>> cellsMatch(String regex) {
return matches(getCells(), regex);
}
public Cell<T, P> cell(String value) {
for (int colIndex = 1; colIndex <= columns().count(); colIndex++)
for (int rowIndex = 1; rowIndex <= rows().count(); rowIndex++) {
Cell<T, P> cell = cell(new Column(colIndex), new Row(rowIndex));
if (cell.getValue().equals(value)) return cell;
}
return null;
}
public Cell<T, P> cellMatch(String regex) {
for (int colIndex = 1; colIndex <= columns().count(); colIndex++)
for (int rowIndex = 1; rowIndex <= rows().count(); rowIndex++) {
Cell<T, P> cell = cell(new Column(colIndex), new Row(rowIndex));
if (cell.getValue().matches(regex)) return cell;
}
return null;
}
/**
* Finds Rows in table matches specified criteria
*
* @param colNameValues - list of search criteria in format columnName=columnValue
* (e.g. rows("Name=Roman", "Profession=QA") )
* @return List of Rows (dictionary: rowName:row)
* Each Row is dictionary: columnName:cell)
*/
public MapArray<String, MapArray<String, Cell<T, P>>> rows(String... colNameValues) {
MapArray<String, MapArray<String, Cell<T, P>>> result = new MapArray<>();
for (Pair<String, MapArray<String, Cell<T, P>>> row : rows().get()) {
boolean matches = true;
for (String colNameValue : colNameValues) {
if (!colNameValue.matches("[^=]+=[^=]*"))
throw exception("Wrong searchCriteria for Cells: " + colNameValue);
String[] splited = colNameValue.split("=");
String colName = splited[0];
String colValue = splited[1];
if (!row.value.get(colName).getValue().equals(colValue)) {
matches = false;
break;
}
}
if (matches) result.add(row);
}
return result;
}
/**
* Finds Columns in table matches specified criteria
*
* @param rowNameValues - list of search criteria in format rowName=rowValue
* (e.g. columns("Name=Roman", "Profession=QA") )
* @return List of Columns (dictionary: columnName:column)
* Each Column is dictionary: rowName:cell)
*/
public MapArray<String, MapArray<String, Cell<T, P>>> columns(String... rowNameValues) {
MapArray<String, MapArray<String, Cell<T, P>>> result = new MapArray<>();
for (Pair<String, MapArray<String, Cell<T, P>>> column : columns().get()) {
boolean matches = true;
for (String rowNameValue : rowNameValues) {
if (!rowNameValue.matches("[^=]+=[^=]*"))
throw exception("Wrong searchCritaria for Cells: " + rowNameValue);
String[] splitted = rowNameValue.split("=");
String rowName = splitted[0];
String rowValue = splitted[1];
if (!column.value.get(rowName).getValue().equals(rowValue)) {
matches = false;
break;
}
}
if (matches) result.add(column);
}
return result;
}
public boolean waitValue(String value, Row row) {
return waitCondition(() -> column(value, row) != null);
}
public boolean waitValue(String value, Column column) {
return waitCondition(() -> row(value, column) != null);
}
public boolean isEmpty() {
getDriver().manage().timeouts().implicitlyWait(0, MILLISECONDS);
int rowsCount = rows().count();
getDriver().manage().timeouts().implicitlyWait(TIMEOUT, SECONDS);
return rowsCount == 0;
}
public boolean waitHaveRows() {
return waitRows(1);
}
public boolean waitRows(int count) {
return waitCondition(() -> rows().count() > count);
}
public Cell<T, P> cell(String value, Row row) {
int rowIndex = (row.haveName())
? asList(rows().headers()).indexOf(row.getName()) + 1
: row.getNum();
for (int colIndex = 1; colIndex <= columns().count(); colIndex++) {
Cell<T, P> cell = cell(new Column(colIndex), new Row(rowIndex));
if (cell.getValue().equals(value)) return cell;
}
return null;
}
public Cell<T, P> cell(String value, Column column) {
int colIndex = column.get(
name -> asList(columns().headers()).indexOf(name) + 1,
num -> num);
for (int rowIndex = 1; rowIndex <= rows().count(); rowIndex++) {
Cell<T, P> cell = cell(new Column(colIndex), new Row(rowIndex));
if (cell.getValue().equals(value)) return cell;
}
return null;
}
public List<Cell<T, P>> cellsMatch(String regex, Column column) {
MapArray<String, Cell<T, P>> columnLine = column(column);
return matches(columnLine.values(), regex);
}
public List<Cell<T, P>> cellsMatch(String regex, Row row) {
MapArray<String, Cell<T, P>> rowLine = row(row);
return matches(rowLine.values(), regex);
}
public MapArray<String, Cell<T, P>> column(String value, Row row) {
Cell<T, P> columnCell = cell(value, row);
return columnCell != null ? columns().getRow(columnCell.columnNum) : null;
}
public MapArray<String, Cell<T, P>> row(String value, Column column) {
Cell<T, P> rowCell = cell(value, column);
return rowCell != null ? rows().getColumn(rowCell.rowNum) : null;
}
private int getColumnIndex(String name) {
int nameIndex = -1;
String[] headers = columns().headers();
if (headers != null && asList(headers).contains(name))
nameIndex = asList(headers).indexOf(name);
else exception("Can't Get Column: '" + name + "'. " + ((headers == null)
? "ColumnHeaders is Null"
: ("Available ColumnHeaders: " + print(headers, ", ", "'{0}'") + ")")));
return nameIndex + columns().startIndex;
}
private int getRowIndex(String name) {
int nameIndex = -1;
String[] headers = rows().headers();
if (headers != null && asList(headers).contains(name))
nameIndex = asList(headers).indexOf(name);
else exception("Can't Get Row: '" + name + "'. " + ((headers == null)
? "RowHeaders is Null"
: ("Available RowHeaders: " + print(headers, ", ", "'{0}'") + ")")));
return nameIndex + rows().startIndex;
}
@Override
protected String getValueAction() {
return "||X|" + print(columns().headers(), "|") + "||" + LineBreak +
print(new ArrayList<>(select(rows().headers(),
rowName -> "||" + rowName + "||" +
print(new ArrayList<>(select(where(getCells(),
cell -> cell.rowName.equals(rowName)),
Cell::getValue)), "|") + "||")), LineBreak);
}
private Cell<T, P> addCell(int colIndex, int rowIndex, int colNum, int rowNum, String colName, String rowName) {
Cell<T, P> cell = first(_allCells, c -> c.columnNum == colNum && c.rowNum == rowNum);
if (cell != null)
return cell.updateData(colName, rowName);
cell = createCell(colIndex, rowIndex, colNum, rowNum, colName, rowName);
_allCells.add(cell);
return cell;
}
private Cell<T, P> createCell(int colIndex, int rowIndex, int colNum, int rowNum, String colName, String rowName) {
T cell = createCell(colIndex, rowIndex);
return new Cell<>(cell, colNum, rowNum, colName, rowName);
}
private T createCell(int colIndex, int rowIndex) {
T cell;
try {
if (cellCreateTemplate != null) {
cell = cellCreateTemplate.invoke();
cell.getAvatar().byLocator = fillByTemplateSilent(cell.getLocator(), rowIndex, colIndex);
cell.getAvatar().context.add(Locator, getLocator());
} else
cell = (T) createCellInstance(clazz, fillByTemplateSilent(_cellLocatorTemplate, rowIndex, colIndex));
} catch (Exception | AssertionError ex) {
throw exception("Can't init Cell");
}
if (cell == null)
throw exception("Can't init Cell");
return cell;
}
public <TChild extends IBaseElement> TChild createCellInstance(Class<TChild> childClass, By newLocator) {
TChild element;
try {
element = childClass.newInstance();
} catch (Exception | AssertionError ignore) {
throw exception(
format("Can't create child for parent '%s' with type '%s' and new locator '%s'",
toString(), childClass.getName(), newLocator));
}
element.getAvatar().byLocator = newLocator;
element.getAvatar().context.add(Locator, getLocator());
return element;
}
}