Class | Line # | Actions | |||||
---|---|---|---|---|---|---|---|
LiveTableElement | 40 | 59 | 0% | 26 | 35 |
1 | /* | |
2 | * See the NOTICE file distributed with this work for additional | |
3 | * information regarding copyright ownership. | |
4 | * | |
5 | * This is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU Lesser General Public License as | |
7 | * published by the Free Software Foundation; either version 2.1 of | |
8 | * the License, or (at your option) any later version. | |
9 | * | |
10 | * This software is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this software; if not, write to the Free | |
17 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |
19 | */ | |
20 | package org.xwiki.test.ui.po; | |
21 | ||
22 | import java.util.List; | |
23 | ||
24 | import org.apache.commons.lang3.StringEscapeUtils; | |
25 | import org.openqa.selenium.By; | |
26 | import org.openqa.selenium.JavascriptExecutor; | |
27 | import org.openqa.selenium.TimeoutException; | |
28 | import org.openqa.selenium.WebDriver; | |
29 | import org.openqa.selenium.WebElement; | |
30 | import org.openqa.selenium.interactions.Actions; | |
31 | import org.openqa.selenium.support.ui.ExpectedCondition; | |
32 | import org.openqa.selenium.support.ui.Select; | |
33 | ||
34 | /** | |
35 | * Represents the actions possible on a livetable. | |
36 | * | |
37 | * @version $Id: 6ba9a98cb28200dadaa1bb0dfc4947cf04070032 $ | |
38 | * @since 3.2M3 | |
39 | */ | |
40 | public class LiveTableElement extends BaseElement | |
41 | { | |
42 | private String livetableId; | |
43 | ||
44 | 113 | public LiveTableElement(String livetableId) |
45 | { | |
46 | 113 | this.livetableId = livetableId; |
47 | } | |
48 | ||
49 | /** | |
50 | * @return if the livetable has finished displaying and is ready for service | |
51 | */ | |
52 | 341 | public boolean isReady() |
53 | { | |
54 | 341 | Object result = getDriver().executeJavascript("return Element.hasClassName('" |
55 | + StringEscapeUtils.escapeEcmaScript(livetableId) + "-ajax-loader','hidden')"); | |
56 | 341 | return result instanceof Boolean ? (Boolean) result : false; |
57 | } | |
58 | ||
59 | /** | |
60 | * Wait till the livetable has finished displaying all its rows (so that we can then assert the livetable content | |
61 | * without running the risk that the rows have not been updated yet). | |
62 | */ | |
63 | 119 | public void waitUntilReady() |
64 | { | |
65 | 119 | long t1 = System.currentTimeMillis(); |
66 | 341 | while ((System.currentTimeMillis() - t1 < getDriver().getTimeout() * 1000L)) { |
67 | 341 | if (isReady()) { |
68 | 119 | return; |
69 | } | |
70 | 222 | try { |
71 | 222 | Thread.sleep(50); |
72 | } catch (InterruptedException e) { | |
73 | // Do nothing just break out | |
74 | 0 | break; |
75 | } | |
76 | } | |
77 | 0 | throw new TimeoutException("Livetable isn't ready after the timeout has expired."); |
78 | } | |
79 | ||
80 | 14 | public boolean hasColumn(String columnTitle) |
81 | { | |
82 | 14 | List<WebElement> elements = getDriver().findElementsWithoutWaiting( |
83 | By.xpath("//th[contains(@class, 'xwiki-livetable-display-header-text') and normalize-space(.) = '" | |
84 | + columnTitle + "']")); | |
85 | 14 | return elements.size() > 0; |
86 | } | |
87 | ||
88 | 22 | public void filterColumn(String inputId, String filterValue) |
89 | { | |
90 | // Make extra sure Selenium can't go quicker than the live table status by forcing it before filtering. | |
91 | 22 | getDriver().executeJavascript("return $('" + StringEscapeUtils.escapeEcmaScript(livetableId) |
92 | + "-ajax-loader').removeClassName('hidden')"); | |
93 | ||
94 | 22 | WebElement element = getDriver().findElement(By.id(inputId)); |
95 | 22 | if ("select".equals(element.getTagName())) { |
96 | 3 | new Select(element).selectByVisibleText(filterValue); |
97 | } else { | |
98 | 19 | element.clear(); |
99 | 19 | element.sendKeys(filterValue); |
100 | } | |
101 | 22 | waitUntilReady(); |
102 | } | |
103 | ||
104 | /** | |
105 | * @param inputId the filter input identifier | |
106 | * @return the value of the filter input for the specified column | |
107 | * @see #filterColumn(String, String) | |
108 | */ | |
109 | 2 | public String getFilterValue(String inputId) |
110 | { | |
111 | 2 | return getDriver().findElement(By.id(inputId)).getAttribute("value"); |
112 | } | |
113 | ||
114 | /** | |
115 | * @param columnTitle the title of live table column | |
116 | * @return the 0-based index of the specified column | |
117 | */ | |
118 | 34 | public int getColumnIndex(String columnTitle) |
119 | { | |
120 | 34 | WebElement liveTable = getDriver().findElement(By.id(livetableId)); |
121 | ||
122 | 34 | String escapedColumnTitle = columnTitle.replace("\\", "\\\\").replace("'", "\\'"); |
123 | 34 | String columnXPath = "//thead[@class = 'xwiki-livetable-display-header']//th[normalize-space(.) = '%s']"; |
124 | 34 | WebElement column = liveTable.findElement(By.xpath(String.format(columnXPath, escapedColumnTitle))); |
125 | ||
126 | 34 | return ((Long) ((JavascriptExecutor) getDriver()).executeScript("return arguments[0].cellIndex;", column)) |
127 | .intValue(); | |
128 | } | |
129 | ||
130 | /** | |
131 | * Checks if there is a row that has the given value for the specified column. | |
132 | * | |
133 | * @param columnTitle the title of live table column | |
134 | * @param columnValue the value to match rows against | |
135 | * @return {@code true} if there is a row that matches the given value for the specified column, {@code false} | |
136 | * otherwise | |
137 | */ | |
138 | 31 | public boolean hasRow(String columnTitle, String columnValue) |
139 | { | |
140 | 31 | List<WebElement> elements = getRows(columnTitle); |
141 | ||
142 | 31 | boolean result = elements.size() > 0; |
143 | 31 | boolean match = false; |
144 | 31 | if (result) { |
145 | 30 | for (WebElement element : elements) { |
146 | 54 | match = element.getText().equals(columnValue); |
147 | 54 | if (match) { |
148 | 29 | break; |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | 31 | return result && match; |
154 | } | |
155 | ||
156 | /** | |
157 | * Checks if there are as many rows as there are passed values and check that the values match. | |
158 | * | |
159 | * @since 7.2M2 | |
160 | */ | |
161 | 0 | public boolean hasExactRows(String columnTitle, List<String> columnValues) |
162 | { | |
163 | 0 | List<WebElement> elements = getRows(columnTitle); |
164 | ||
165 | 0 | boolean result = elements.size() == columnValues.size(); |
166 | 0 | if (result) { |
167 | 0 | for (int i = 0; i < elements.size(); i++) { |
168 | 0 | result = result && elements.get(i).getText().equals(columnValues.get(i)); |
169 | 0 | if (!result) { |
170 | 0 | break; |
171 | } | |
172 | } | |
173 | } | |
174 | ||
175 | 0 | return result; |
176 | } | |
177 | ||
178 | 31 | private List<WebElement> getRows(String columnTitle) |
179 | { | |
180 | 31 | String cellXPath = String.format(".//tr/td[position() = %s]", getColumnIndex(columnTitle) + 1); |
181 | 31 | WebElement liveTableBody = getDriver().findElement(By.id(this.livetableId + "-display")); |
182 | 31 | return liveTableBody.findElements(By.xpath(cellXPath)); |
183 | } | |
184 | ||
185 | /** | |
186 | * @return the number of rows in the live table | |
187 | */ | |
188 | 24 | public int getRowCount() |
189 | { | |
190 | 24 | WebElement liveTableBody = getDriver().findElementWithoutWaiting(By.id(this.livetableId + "-display")); |
191 | // We use XPath because we're interested only in the direct children. | |
192 | 24 | return getDriver().findElementsWithoutWaiting(liveTableBody, By.xpath("tr")).size(); |
193 | } | |
194 | ||
195 | /** | |
196 | * @since 5.3RC1 | |
197 | */ | |
198 | 0 | public WebElement getRow(int rowNumber) |
199 | { | |
200 | 0 | WebElement liveTableBody = getDriver().findElementWithoutWaiting(By.id(this.livetableId + "-display")); |
201 | 0 | return getDriver().findElementWithoutWaiting(liveTableBody, By.xpath("tr[" + rowNumber + "]")); |
202 | } | |
203 | ||
204 | /** | |
205 | * @since 5.3RC1 | |
206 | */ | |
207 | 0 | public WebElement getCell(WebElement rowElement, int columnNumber) |
208 | { | |
209 | 0 | return getDriver().findElementWithoutWaiting(rowElement, By.xpath("td[" + columnNumber + "]")); |
210 | } | |
211 | ||
212 | /** | |
213 | * @since 5.3RC1 | |
214 | */ | |
215 | 0 | public ViewPage clickCell(int rowNumber, int columnNumber) |
216 | { | |
217 | 0 | WebElement tdElement = getCell(getRow(rowNumber), columnNumber); |
218 | // First scroll the element into view, if needed, by moving the mouse to the top left corner of the element. | |
219 | 0 | new Actions(getDriver()).moveToElement(tdElement, 0, 0).perform(); |
220 | // Find the first A element and click it! | |
221 | 0 | tdElement.findElement(By.tagName("a")).click(); |
222 | 0 | return new ViewPage(); |
223 | } | |
224 | ||
225 | /** | |
226 | * @since 3.2M3 | |
227 | */ | |
228 | 0 | public void waitUntilRowCountGreaterThan(final int minimalExpectedRowCount) |
229 | { | |
230 | 0 | final By by = By.xpath("//tbody[@id = '" + this.livetableId + "-display']//tr"); |
231 | 0 | getDriver().waitUntilCondition(new ExpectedCondition<Boolean>() |
232 | { | |
233 | 0 | @Override |
234 | public Boolean apply(WebDriver driver) | |
235 | { | |
236 | // Refresh the current page since we need the livetable to fetch the JSON again | |
237 | 0 | driver.navigate().refresh(); |
238 | 0 | return driver.findElements(by).size() >= minimalExpectedRowCount; |
239 | } | |
240 | }); | |
241 | } | |
242 | } |