package com.gfk.senbot.framework.services.selenium; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import com.gfk.senbot.framework.BaseServiceHub; import com.gfk.senbot.framework.context.SenBotContext; import cucumber.api.DataTable; /** * A util class for all HTML table related selenium actions * * @author joostschouten * */ public class TableService extends BaseServiceHub { private final ElementService seleniumElementService; /** * Constructor * * @param seleniumElementService A ElementService object */ public TableService(ElementService seleniumElementService) { this.seleniumElementService = seleniumElementService; } /** * Compare method for asserting an expected cucumber {@link DataTable} to a found HTML table on a page * * When dealing with colspan or rowspan, mark the cells which are missing in the HTML in your expected result with <code><colspan></code> * and <code><rowspan></code> to indicate they need substituting * * @param expectedContent a {@link ExpectedTableDefinition} wrapper around the {@link DataTable} constructed by Cucumber which allows some more * filter settings defining how to match the expected to the found * @param table the {@link WebElement} as found on the HTML page * @throws Throwable */ public void compareTable(ExpectedTableDefinition expectedContent, WebElement table) throws Throwable { try { SenBotContext.getSeleniumDriver().manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS); List<List<String>> expectedRows = expectedContent.getExpected().raw(); //only get the tr's directly under the table or with one parent between the table and the tr (eg. THEAD, TBODY) List<WebElement> foundRows = table.findElements(By.xpath("tr | */tr")); List<WebElement> filteredRows = new ArrayList<WebElement>(); expectedContent.cacheIncludeAndIgnore(table); for (WebElement row : foundRows) { if(!row.isDisplayed()) { //always ignore hidden rows continue; } if ((expectedContent.getIncludeByMatches().isEmpty() || expectedContent.getIncludeByMatches().contains(row)) && !expectedContent.getIgnoreByMatches().contains(row)) { filteredRows.add(row); } } assertEquals("The found table should have the expected number of rows", expectedRows.size(), filteredRows.size()); LinkedHashMap<String, Integer> columnHeaders = new LinkedHashMap<String, Integer>(); for (int rowCount = 0; rowCount < expectedRows.size(); rowCount++) { WebElement foundRow = filteredRows.get(rowCount); List<WebElement> foundCells = foundRow.findElements(By.xpath("td")); foundCells.addAll(foundRow.findElements(By.xpath("th"))); List<String> expectedRow = expectedRows.get(rowCount); if (rowCount == 0) { //capture potential column headers //a map to maintain which header WebElement represents which index List<WebElement> registeredColumnHeaders = new ArrayList<WebElement>(); for (int c = 0; c < expectedRow.size(); c++) { String expectedHeader = expectedRow.get(c); String expectedHeaderKey = expectedHeader + "_" + c; Integer foundHeaderIndex = 0; for (WebElement foundHeaderCell : foundCells) { String foundHeaderText = foundHeaderCell.getText().trim(); //replace line breaks with spaces foundHeaderText = foundHeaderText.replaceAll("\n", " "); if (foundHeaderText.equals(expectedHeader) && !registeredColumnHeaders.contains(foundHeaderCell)) { if (columnHeaders.get(expectedHeaderKey) != null && expectedContent.isMatchOnlyPassedInColumns()) { fail("The expected column header \"" + expectedHeader + "\" is not unique in the HTML table"); } columnHeaders.put(expectedHeaderKey, foundHeaderIndex); registeredColumnHeaders.add(foundHeaderCell); break; } foundHeaderIndex++; } if (columnHeaders.get(expectedHeaderKey) == null) { fail("The expected column header \"" + expectedHeaderKey + "\" was not found in the HTML table"); } } } int cellCount = 0; int expectedSpanCells = 0; for (String columnHeaderKey : columnHeaders.keySet()) { String expectedValue = getReferenceService().namespaceString(expectedRow.get(cellCount).trim()); if ("<rowspan>".equals(expectedValue) || "<colspan>".equals(expectedValue)) { //a missing cell is expected here that exists due to a rowspan or colspan in an earlier row/column. ignore the cell expectedSpanCells++; } else { Integer foundColumnIndex = columnHeaders.get(columnHeaderKey); String foundValue = foundCells.get(foundColumnIndex - expectedSpanCells).getText(); foundValue = foundValue.trim().replaceAll("\n", " "); assertEquals("Cell " + (cellCount + 1) + " (column: " + columnHeaderKey + ") of row " + (rowCount + 1) + " should be as expected ", expectedValue, foundValue); } cellCount++; } if (expectedContent.isMatchOnlyPassedInColumns()) { assertEquals("Found row " + rowCount + " should have the expected number of cells", expectedRow.size(), columnHeaders.size()); } else { assertEquals("Found row " + rowCount + " should have the expected number of cells", expectedRow.size(), foundCells.size() + expectedSpanCells); } } } finally { SenBotContext.getSeleniumDriver().manage().timeouts().implicitlyWait(SenBotContext.getSenBotContext().getSeleniumManager().getTimeout(), TimeUnit.MILLISECONDS); } } }