You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GridElement.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.testbench.elements;
  17. import java.util.ArrayList;
  18. import java.util.Iterator;
  19. import java.util.List;
  20. import java.util.Optional;
  21. import org.openqa.selenium.NoSuchElementException;
  22. import org.openqa.selenium.WebElement;
  23. import com.vaadin.testbench.By;
  24. import com.vaadin.testbench.TestBenchElement;
  25. import com.vaadin.testbench.elementsbase.AbstractElement;
  26. import com.vaadin.testbench.elementsbase.ServerClass;
  27. /**
  28. * TestBench Element API for Grid
  29. *
  30. * @author Vaadin Ltd
  31. */
  32. @ServerClass("com.vaadin.ui.Grid")
  33. public class GridElement extends AbstractComponentElement {
  34. public static class GridCellElement extends AbstractElement {
  35. private static final String FOCUSED_CELL_CLASS_NAME = "-cell-focused";
  36. private static final String FROZEN_CLASS_NAME = "frozen";
  37. public boolean isFocused() {
  38. return getAttribute("class").contains(FOCUSED_CELL_CLASS_NAME);
  39. }
  40. public boolean isFrozen() {
  41. return getAttribute("class").contains(FROZEN_CLASS_NAME);
  42. }
  43. }
  44. public static class GridRowElement extends AbstractElement {
  45. private static final String FOCUSED_CLASS_NAME = "-row-focused";
  46. private static final String SELECTED_CLASS_NAME = "-row-selected";
  47. public boolean isFocused() {
  48. return getAttribute("class").contains(FOCUSED_CLASS_NAME);
  49. }
  50. @Override
  51. public boolean isSelected() {
  52. return getAttribute("class").contains(SELECTED_CLASS_NAME);
  53. }
  54. public GridCellElement getCell(int columnIndex) {
  55. TestBenchElement e = (TestBenchElement) findElement(
  56. By.xpath("./td[" + (columnIndex + 1) + "]"));
  57. return e.wrap(GridCellElement.class);
  58. }
  59. }
  60. public static class GridEditorElement extends AbstractElement {
  61. private GridElement grid;
  62. private GridEditorElement setGrid(GridElement grid) {
  63. this.grid = grid;
  64. return this;
  65. }
  66. /**
  67. * Gets the editor field for column in given index.
  68. *
  69. * @param colIndex
  70. * the column index
  71. * @return the editor field for given location
  72. *
  73. * @throws NoSuchElementException
  74. * if {@code isEditable(colIndex) == false}
  75. */
  76. public TestBenchElement getField(int colIndex) {
  77. return grid.getSubPart("#editor[" + colIndex + "]");
  78. }
  79. /**
  80. * Gets whether the column with the given index is editable, that is,
  81. * has an associated editor field.
  82. *
  83. * @param colIndex
  84. * the column index
  85. * @return {@code true} if the column has an editor field, {@code false}
  86. * otherwise
  87. */
  88. public boolean isEditable(int colIndex) {
  89. return grid
  90. .isElementPresent(By.vaadin("#editor[" + colIndex + "]"));
  91. }
  92. /**
  93. * Checks whether a field is marked with an error.
  94. *
  95. * @param colIndex
  96. * column index
  97. * @return <code>true</code> iff the field is marked with an error
  98. */
  99. public boolean isFieldErrorMarked(int colIndex) {
  100. return getField(colIndex).getAttribute("class").contains("error");
  101. }
  102. /**
  103. * Saves the fields of this editor.
  104. * <p>
  105. * <em>Note:</em> that this closes the editor making this element
  106. * useless.
  107. */
  108. public void save() {
  109. findElement(By.className("v-grid-editor-save")).click();
  110. }
  111. /**
  112. * Cancels this editor.
  113. * <p>
  114. * <em>Note:</em> that this closes the editor making this element
  115. * useless.
  116. */
  117. public void cancel() {
  118. findElement(By.className("v-grid-editor-cancel")).click();
  119. }
  120. /**
  121. * Gets the error message text, or <code>null</code> if no message is
  122. * present.
  123. */
  124. public String getErrorMessage() {
  125. WebElement messageWrapper = findElement(
  126. By.className("v-grid-editor-message"));
  127. List<WebElement> divs = messageWrapper
  128. .findElements(By.tagName("div"));
  129. if (divs.isEmpty()) {
  130. return null;
  131. } else {
  132. return divs.get(0).getText();
  133. }
  134. }
  135. }
  136. /**
  137. * Scrolls Grid element so that wanted row is displayed
  138. *
  139. * @param index
  140. * Target row
  141. */
  142. public void scrollToRow(int index) {
  143. try {
  144. getSubPart("#cell[" + index + "]");
  145. } catch (NoSuchElementException e) {
  146. // Expected, ignore it.
  147. }
  148. }
  149. /**
  150. * Gets cell element with given row and column index.
  151. *
  152. * @param rowIndex
  153. * Row index
  154. * @param colIndex
  155. * Column index
  156. * @return Cell element with given indices.
  157. */
  158. public GridCellElement getCell(int rowIndex, int colIndex) {
  159. scrollToRow(rowIndex);
  160. return getSubPart("#cell[" + rowIndex + "][" + colIndex + "]")
  161. .wrap(GridCellElement.class);
  162. }
  163. /**
  164. * Gets row element with given row index.
  165. *
  166. * @param index
  167. * Row index
  168. * @return Row element with given index.
  169. */
  170. public GridRowElement getRow(int index) {
  171. scrollToRow(index);
  172. return getSubPart("#cell[" + index + "]").wrap(GridRowElement.class);
  173. }
  174. /**
  175. * Gets header cell element with given row and column index.
  176. *
  177. * @param rowIndex
  178. * Row index
  179. * @param colIndex
  180. * Column index
  181. * @return Header cell element with given indices.
  182. */
  183. public GridCellElement getHeaderCell(int rowIndex, int colIndex) {
  184. return getSubPart("#header[" + rowIndex + "][" + colIndex + "]")
  185. .wrap(GridCellElement.class);
  186. }
  187. /**
  188. * Finds the header cell element with the given caption. If there are
  189. * multiple headers with the same name, the first one is returned.
  190. *
  191. * @param caption
  192. * The header caption
  193. * @return The first header cell element with a given caption.
  194. * @throws NoSuchElementException
  195. * if there is no header row or no header cell with the given
  196. * text.
  197. */
  198. public GridCellElement getHeaderCellByCaption(String caption) {
  199. List<WebElement> headerRows = findElement(By.vaadin("#header"))
  200. .findElements(By.xpath("./tr/th"));
  201. for (WebElement header : headerRows) {
  202. if (caption.equals(header.getText())) {
  203. return TestBenchElement
  204. .wrapElement(header, getCommandExecutor())
  205. .wrap(GridCellElement.class);
  206. }
  207. }
  208. String errorMessage = String
  209. .format("There is no header cell with %s caption. ", caption);
  210. throw new NoSuchElementException(errorMessage);
  211. }
  212. /**
  213. * Gets the header cell element with the given caption in the given header
  214. * row. If there are multiple headers with the same name, the first one is
  215. * returned.
  216. *
  217. * @param rowIndex
  218. * The index of the header row
  219. * @param caption
  220. * The header caption
  221. * @return The first header cell element with a given caption.
  222. * @throws NoSuchElementException
  223. * if there is no header row or no header cell with the given
  224. * text.
  225. */
  226. public GridCellElement getHeaderCellByCaption(int rowIndex,
  227. String caption) {
  228. List<GridCellElement> headerCells = getHeaderCells(rowIndex);
  229. for (GridCellElement cell : headerCells) {
  230. if (caption.equals(cell.getText())) {
  231. return cell;
  232. }
  233. }
  234. String errorMessage = String.format(
  235. "The row with index %d does not have header with %s caption. ",
  236. rowIndex, caption);
  237. throw new NoSuchElementException(errorMessage);
  238. }
  239. /**
  240. * Gets footer cell element with given row and column index.
  241. *
  242. * @param rowIndex
  243. * Row index
  244. * @param colIndex
  245. * Column index
  246. * @return Footer cell element with given indices.
  247. */
  248. public GridCellElement getFooterCell(int rowIndex, int colIndex) {
  249. return getSubPart("#footer[" + rowIndex + "][" + colIndex + "]")
  250. .wrap(GridCellElement.class);
  251. }
  252. /**
  253. * Gets list of header cell elements on given row.
  254. *
  255. * @param rowIndex
  256. * Row index
  257. * @return Header cell elements on given row.
  258. */
  259. public List<GridCellElement> getHeaderCells(int rowIndex) {
  260. List<GridCellElement> headers = new ArrayList<GridCellElement>();
  261. for (TestBenchElement e : TestBenchElement.wrapElements(
  262. getSubPart("#header[" + rowIndex + "]").findElements(
  263. By.xpath("./th")),
  264. getCommandExecutor())) {
  265. headers.add(e.wrap(GridCellElement.class));
  266. }
  267. return headers;
  268. }
  269. /**
  270. * Gets list of header cell elements on given row.
  271. *
  272. * @param rowIndex
  273. * Row index
  274. * @return Header cell elements on given row.
  275. */
  276. public List<GridCellElement> getFooterCells(int rowIndex) {
  277. List<GridCellElement> footers = new ArrayList<GridCellElement>();
  278. for (TestBenchElement e : TestBenchElement.wrapElements(
  279. getSubPart("#footer[" + rowIndex + "]").findElements(
  280. By.xpath("./td")),
  281. getCommandExecutor())) {
  282. footers.add(e.wrap(GridCellElement.class));
  283. }
  284. return footers;
  285. }
  286. /**
  287. * Get header row count
  288. *
  289. * @return Header row count
  290. */
  291. public int getHeaderCount() {
  292. return getSubPart("#header").findElements(By.xpath("./tr")).size();
  293. }
  294. /**
  295. * Get footer row count
  296. *
  297. * @return Footer row count
  298. */
  299. public int getFooterCount() {
  300. return getSubPart("#footer").findElements(By.xpath("./tr")).size();
  301. }
  302. /**
  303. * Get a header row by index
  304. *
  305. * @param rowIndex
  306. * Row index
  307. * @return The th element of the row
  308. */
  309. public TestBenchElement getHeaderRow(int rowIndex) {
  310. return getSubPart("#header[" + rowIndex + "]");
  311. }
  312. /**
  313. * Get a footer row by index
  314. *
  315. * @param rowIndex
  316. * Row index
  317. * @return The tr element of the row
  318. */
  319. public TestBenchElement getFooterRow(int rowIndex) {
  320. return getSubPart("#footer[" + rowIndex + "]");
  321. }
  322. /**
  323. * Get the vertical scroll element
  324. *
  325. * @return The element representing the vertical scrollbar
  326. */
  327. public TestBenchElement getVerticalScroller() {
  328. List<WebElement> rootElements = findElements(By.xpath("./div"));
  329. return (TestBenchElement) rootElements.get(0);
  330. }
  331. /**
  332. * Get the horizontal scroll element
  333. *
  334. * @return The element representing the horizontal scrollbar
  335. */
  336. public TestBenchElement getHorizontalScroller() {
  337. List<WebElement> rootElements = findElements(By.xpath("./div"));
  338. return (TestBenchElement) rootElements.get(1);
  339. }
  340. /**
  341. * Get the header element
  342. *
  343. * @return The thead element
  344. */
  345. public TestBenchElement getHeader() {
  346. return getSubPart("#header");
  347. }
  348. /**
  349. * Get the body element
  350. *
  351. * @return the tbody element
  352. */
  353. public TestBenchElement getBody() {
  354. return getSubPart("#cell");
  355. }
  356. /**
  357. * Get the footer element
  358. *
  359. * @return the tfoot element
  360. */
  361. public TestBenchElement getFooter() {
  362. return getSubPart("#footer");
  363. }
  364. /**
  365. * Get the element wrapping the table element
  366. *
  367. * @return The element that wraps the table element
  368. */
  369. public TestBenchElement getTableWrapper() {
  370. List<WebElement> rootElements = findElements(By.xpath("./div"));
  371. return (TestBenchElement) rootElements.get(2);
  372. }
  373. public GridEditorElement getEditor() {
  374. return getSubPart("#editor").wrap(GridEditorElement.class)
  375. .setGrid(this);
  376. }
  377. /**
  378. * Helper function to get Grid subparts wrapped correctly
  379. *
  380. * @param subPartSelector
  381. * SubPart to be used in ComponentLocator
  382. * @return SubPart element wrapped in TestBenchElement class
  383. */
  384. private TestBenchElement getSubPart(String subPartSelector) {
  385. return (TestBenchElement) findElement(By.vaadin(subPartSelector));
  386. }
  387. /**
  388. * Gets the element that contains the details of a row.
  389. *
  390. * @since 8.0
  391. * @param rowIndex
  392. * the index of the row for the details
  393. * @return the element that contains the details of a row. <code>null</code>
  394. * if no widget is defined for the details row
  395. * @throws NoSuchElementException
  396. * if the given details row is currently not open
  397. */
  398. public TestBenchElement getDetails(int rowIndex)
  399. throws NoSuchElementException {
  400. return getSubPart("#details[" + rowIndex + "]");
  401. }
  402. /**
  403. * Toggles the column visibility. Column is identified by its hiding toggle
  404. * caption.
  405. *
  406. * @param toggleCaption
  407. * @since 8.0.6
  408. */
  409. public void toggleColumnHidden(String toggleCaption) {
  410. if (!isElementPresent(By.className("v-grid-sidebar-content"))) {
  411. // Open sidebar menu
  412. WebElement sidebarButton = findElement(
  413. By.className("v-grid-sidebar"))
  414. .findElement(By.tagName("button"));
  415. sidebarButton.click();
  416. }
  417. Optional<WebElement> toggleButton = getDriver()
  418. .findElement(By.className("v-grid-sidebar-content"))
  419. .findElements(By.className("column-hiding-toggle")).stream()
  420. .filter(e -> e.getText().equals(toggleCaption)).findAny();
  421. if (toggleButton.isPresent()) {
  422. toggleButton.ifPresent(e -> e.click());
  423. } else {
  424. throw new IllegalArgumentException(
  425. "No column hiding toggle with caption '" + toggleCaption
  426. + "'");
  427. }
  428. }
  429. /**
  430. * Gets the total number of data rows in the grid.
  431. *
  432. * @return the number of data rows in the grid,
  433. */
  434. public long getRowCount() {
  435. Long res = (Long) getCommandExecutor()
  436. .executeScript("return arguments[0].getBodyRowCount()", this);
  437. if (res == null) {
  438. throw new IllegalStateException("getBodyRowCount returned null");
  439. }
  440. return res.longValue();
  441. }
  442. /**
  443. * Gets all the data rows in the grid.
  444. * <p>
  445. * Returns an iterable which will lazily scroll rows into views and lazy
  446. * load data as needed.
  447. *
  448. * @return an iterable of all the data rows in the grid.
  449. */
  450. public Iterable<GridRowElement> getRows() {
  451. return new Iterable<GridElement.GridRowElement>() {
  452. @Override
  453. public Iterator<GridRowElement> iterator() {
  454. return new Iterator<GridElement.GridRowElement>() {
  455. int nextIndex = 0;
  456. @Override
  457. public GridRowElement next() {
  458. return getRow(nextIndex++);
  459. }
  460. @Override
  461. public boolean hasNext() {
  462. try {
  463. getRow(nextIndex);
  464. return true;
  465. } catch (Exception e) {
  466. return false;
  467. }
  468. }
  469. @Override
  470. public void remove() {
  471. throw new UnsupportedOperationException(
  472. "remove not supported");
  473. }
  474. };
  475. }
  476. };
  477. }
  478. }