1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javafx.scene.control.test.tableview; 26 27 import java.util.Collection; 28 import java.util.HashSet; 29 import javafx.collections.ObservableList; 30 import javafx.geometry.Orientation; 31 import javafx.scene.Node; 32 import javafx.scene.control.Control; 33 import javafx.scene.control.IndexedCell; 34 import javafx.scene.control.ScrollBar; 35 import javafx.scene.control.TableCell; 36 import javafx.scene.control.TablePosition; 37 import javafx.scene.control.TableRow; 38 import javafx.scene.control.TableSelectionModel; 39 import javafx.scene.control.TableView; 40 import javafx.scene.control.TableView.TableViewSelectionModel; 41 import javafx.scene.control.TreeTableCell; 42 import javafx.scene.control.TreeTablePosition; 43 import javafx.scene.control.TreeTableRow; 44 import javafx.scene.control.TreeTableView; 45 import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel; 46 import javafx.scene.control.test.util.MultipleSelectionHelper; 47 import javafx.scene.control.test.util.MultipleSelectionHelper.Range; 48 import javafx.scene.control.test.util.UtilTestFunctions; 49 import javafx.scene.text.Text; 50 import junit.framework.Assert; 51 import org.jemmy.Point; 52 import org.jemmy.Rectangle; 53 import org.jemmy.action.GetAction; 54 import org.jemmy.control.Wrap; 55 import org.jemmy.fx.ByStyleClass; 56 import org.jemmy.fx.NodeWrap; 57 import org.jemmy.fx.Root; 58 import org.jemmy.fx.control.TableViewWrap; 59 import org.jemmy.fx.control.TreeTableViewWrap; 60 import org.jemmy.input.AbstractScroll; 61 import org.jemmy.interfaces.Caret; 62 import org.jemmy.interfaces.Parent; 63 import org.jemmy.lookup.Lookup; 64 import org.jemmy.lookup.LookupCriteria; 65 import org.jemmy.timing.State; 66 67 /** 68 * @author Alexander Kirov 69 */ 70 public class TestBaseCommon extends UtilTestFunctions { 71 72 public static Wrap<? extends TableCell> getCellWrap(Wrap<? extends Control> testedControl, final int column, final int row) { 73 scrollTo(testedControl, column, row); 74 Lookup lookup = testedControl.as(Parent.class, Node.class).lookup(IndexedCell.class, new TableViewTest.ByPosition(column, row)); 75 if (lookup.size() == 0) { // TODO: what's that?!! 76 scrollTo(testedControl, column, row); 77 lookup.size(); 78 } 79 return lookup.wrap(); 80 } 81 82 public static void scrollTo(final Wrap<? extends Control> testedControl, final int column, final int row) { 83 AbstractScroll vScroll = getScroll(testedControl, true); 84 AbstractScroll hScroll = getScroll(testedControl, false); 85 86 if (vScroll != null) { 87 vScroll.caret().to(new Caret.Direction() { 88 public int to() { 89 int[] shown = shown(testedControl); 90 if (shown[1] > row) { 91 return -1; 92 } 93 if (shown[3] < row) { 94 return 1; 95 } 96 return 0; 97 } 98 }); 99 } 100 if (hScroll != null) { 101 hScroll.caret().to(new Caret.Direction() { 102 public int to() { 103 int[] shown = shown(testedControl); 104 if (shown[0] > column) { 105 return -1; 106 } 107 if (shown[2] < column) { 108 return 1; 109 } 110 return 0; 111 } 112 }); 113 } 114 } 115 116 private static AbstractScroll getScroll(final Wrap<? extends Control> testedControl, final boolean vertical) { 117 Lookup<ScrollBar> lookup = testedControl.as(Parent.class, Node.class).lookup(ScrollBar.class, 118 new LookupCriteria<ScrollBar>() { 119 @Override 120 public boolean check(ScrollBar control) { 121 return control.isVisible() && (control.getOrientation() == Orientation.VERTICAL) == vertical; 122 } 123 }); 124 int count = lookup.size(); 125 if (count == 0) { 126 return null; 127 } else if (count == 1) { 128 return lookup.as(AbstractScroll.class); 129 } else { 130 return null; 131 } 132 } 133 134 public static int[] shown(final Wrap<? extends Control> testedControl) { 135 final Rectangle viewArea = getContainerWrap(testedControl).getScreenBounds(); 136 final Rectangle clippedArea = getClippedContainerWrap(testedControl).getScreenBounds(); 137 138 final Rectangle actuallyVisibleArea = new Rectangle(viewArea.x, viewArea.y, clippedArea.width, clippedArea.height); 139 140 final boolean isTable = testedControl.getControl() instanceof TableView; 141 int[] res = new GetAction<int[]>() { 142 @Override 143 @SuppressWarnings("unchecked") 144 public void run(Object... parameters) { 145 final int[] res = new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1}; 146 testedControl.as(Parent.class, Node.class).lookup(new LookupCriteria<Node>() { 147 @Override 148 public boolean check(Node control) { 149 if (isTable) { 150 if (!TableCell.class.isAssignableFrom(control.getClass())) { 151 return false; 152 } 153 } else { 154 if (!TreeTableCell.class.isAssignableFrom(control.getClass())) { 155 return false; 156 } 157 } 158 159 if (control.isVisible() && control.getOpacity() == 1.0) { 160 Rectangle bounds = NodeWrap.getScreenBounds(testedControl.getEnvironment(), control); 161 int column; 162 if (isTable) { 163 column = getColumnIndex((TableCell) control); 164 } else { 165 column = getColumnIndex((TreeTableCell) control); 166 } 167 int row; 168 if (isTable) { 169 row = getRowIndex((TableCell) control); 170 } else { 171 row = getRowIndex((TreeTableCell) control); 172 } 173 174 //For cases, when we don't see cell fully, we will require only click point area. 175 final int xEpsilon = 2; 176 final int yEpsilon = 2; 177 final Point center = new Point(bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2); 178 Rectangle neededRec = new Rectangle(center.x - xEpsilon, center.y - yEpsilon, 2 * xEpsilon, 2 * yEpsilon); 179 if (actuallyVisibleArea.contains(neededRec) && row >= 0) { 180 res[0] = Math.min(res[0], column); 181 res[1] = Math.min(res[1], row); 182 res[2] = Math.max(res[2], column); 183 res[3] = Math.max(res[3], row); 184 } 185 } 186 return false; 187 } 188 }).size(); 189 190 setResult(res); 191 } 192 }.dispatch(testedControl.getEnvironment()); 193 194 return res; 195 } 196 197 /** 198 * @return wrap of parent container that contains Cells 199 */ 200 static Wrap<? extends javafx.scene.Parent> getContainerWrap(Wrap<? extends Control> parent) { 201 return getParentWrap(parent.as(Parent.class, Node.class), VIRTIAL_FLOW_CLASS_NAME); 202 } 203 204 static Wrap<? extends javafx.scene.Parent> getClippedContainerWrap(Wrap<? extends Control> parent) { 205 return getParentWrap(parent.as(Parent.class, Node.class), CLIPPED_CONTAINER_CLASS_NAME); 206 } 207 208 static private Wrap<? extends javafx.scene.Parent> getParentWrap(Parent<Node> parent, final String className) { 209 return parent.lookup(javafx.scene.Parent.class, new LookupCriteria<javafx.scene.Parent>() { 210 @Override 211 public boolean check(javafx.scene.Parent control) { 212 return control.getClass().getName().endsWith(className); 213 } 214 }).wrap(); 215 } 216 217 public static Wrap<Text> getCellWrap(Wrap<? extends TableView> testedControl, final String item) { 218 return testedControl.as(Parent.class, String.class).lookup( 219 new LookupCriteria<String>() { 220 @Override 221 public boolean check(String cell_item) { 222 return cell_item.equals(item); 223 } 224 }).wrap(); 225 } 226 227 public static int getRowIndex(final TableCell tableCell) { 228 return new GetAction<Integer>() { 229 @Override 230 public void run(Object... os) throws Exception { 231 setResult(tableCell.getTableRow().getIndex()); 232 } 233 }.dispatch(Root.ROOT.getEnvironment()); 234 } 235 236 public static int getRowIndex(final TreeTableCell tableCell) { 237 return new GetAction<Integer>() { 238 @Override 239 public void run(Object... os) throws Exception { 240 setResult(tableCell.getTreeTableRow().getIndex()); 241 } 242 }.dispatch(Root.ROOT.getEnvironment()); 243 } 244 245 public static int getColumnIndex(final TableCell tableCell) { 246 return new GetAction<Integer>() { 247 @Override 248 public void run(Object... os) throws Exception { 249 setResult(tableCell.getTableView().getVisibleLeafIndex(tableCell.getTableColumn())); 250 } 251 }.dispatch(Root.ROOT.getEnvironment()); 252 } 253 254 public static int getColumnIndex(final TreeTableCell tableCell) { 255 return new GetAction<Integer>() { 256 @Override 257 public void run(Object... os) throws Exception { 258 setResult(tableCell.getTreeTableView().getVisibleLeafIndex(tableCell.getTableColumn())); 259 } 260 }.dispatch(Root.ROOT.getEnvironment()); 261 } 262 263 protected static Range getVisibleRange(final Wrap<? extends Control> testedControl) { 264 int[] visibleIndices; 265 266 if (testedControl.getControl() instanceof TableView) { 267 visibleIndices = org.jemmy.fx.control.TableUtils.shown( 268 testedControl.getEnvironment(), 269 testedControl, 270 new org.jemmy.fx.control.TableUtils.TableViewIndexInfoProvider((TableViewWrap) testedControl), TableCell.class); 271 } else { 272 visibleIndices = org.jemmy.fx.control.TableUtils.shown( 273 testedControl.getEnvironment(), 274 testedControl, 275 new org.jemmy.fx.control.TableUtils.TreeTableViewIndexInfoProvider((TreeTableViewWrap) testedControl), TreeTableCell.class); 276 } 277 278 return new Range(visibleIndices[1], visibleIndices[3]); 279 } 280 281 protected static void checkSelection(final Wrap<? extends Control> testedControl, final MultipleSelectionHelper selectionHelper) { 282 testedControl.waitState(new State() { 283 public Object reached() { 284 Collection<Point> helperSelected = selectionHelper.getSelected(); 285 Collection<Point> selected = getSelected(testedControl); 286 Point helperFocus = selectionHelper.focus; 287 Point focus = getSelectedItem(testedControl); 288 289 System.out.println("Helper selection : " + helperSelected); 290 System.out.println("Selection : " + selected); 291 System.out.println("Helper focus : " + helperFocus); 292 System.out.println("Focus : " + focus); 293 System.out.println("Anchor : " + selectionHelper.anchor + "\n\n"); 294 295 if (helperSelected.size() == selected.size() 296 && helperSelected.containsAll(selected) 297 && (focus.equals(helperFocus) || selectionHelper.ctrlA)) { 298 return true; 299 } else { 300 return null; 301 } 302 } 303 }); 304 } 305 306 protected static HashSet<Point> getSelected(final Wrap<? extends Control> testedControl) { 307 return new GetAction<HashSet<Point>>() { 308 @Override 309 public void run(Object... parameters) throws Exception { 310 HashSet<Point> selected = new HashSet<Point>(); 311 if (testedControl.getControl() instanceof TableView) { 312 TableViewSelectionModel model = ((TableView) testedControl.getControl()).getSelectionModel(); 313 for (Object obj : model.getSelectedCells()) { 314 TablePosition tablePos = (TablePosition) obj; 315 if (model.isCellSelectionEnabled()) { 316 selected.add(new Point(tablePos.getColumn(), tablePos.getRow())); 317 } else { 318 selected.add(new Point(-1, tablePos.getRow())); 319 } 320 } 321 setResult(selected); 322 } else { 323 TreeTableViewSelectionModel model = ((TreeTableView) testedControl.getControl()).getSelectionModel(); 324 for (Object obj : model.getSelectedCells()) { 325 TreeTablePosition treeTablePos = (TreeTablePosition) obj; 326 if (model.isCellSelectionEnabled()) { 327 selected.add(new Point(treeTablePos.getColumn(), treeTablePos.getRow())); 328 } else { 329 selected.add(new Point(-1, treeTablePos.getRow())); 330 } 331 } 332 setResult(selected); 333 } 334 } 335 }.dispatch(Root.ROOT.getEnvironment()); 336 } 337 338 protected static Point getSelectedItem(final Wrap<? extends Control> testedControl) { 339 return new GetAction<Point>() { 340 @Override 341 public void run(Object... parameters) throws Exception { 342 TableSelectionModel model; 343 if (testedControl.getControl() instanceof TableView) { 344 model = ((TableView) testedControl.getControl()).getSelectionModel(); 345 } else { 346 model = ((TreeTableView) testedControl.getControl()).getSelectionModel(); 347 } 348 if (model.isCellSelectionEnabled()) { 349 Lookup lookup = testedControl.as(Parent.class, Node.class).lookup(new LookupCriteria<Node>() { 350 public boolean check(Node row) { 351 // if (IndexedCell.class.isAssignableFrom(row.getClass()) ) { 352 // System.out.println("row.getClass() = " + row.getClass()); 353 // System.out.println("IndexedCell.class.isAssignableFrom(row.getClass()) = " + IndexedCell.class.isAssignableFrom(row.getClass())); 354 // System.out.println("((IndexedCell) row).getText() = " + ((IndexedCell) row).getText()); 355 // System.out.println("((IndexedCell) row).isFocused() = " + ((IndexedCell) row).isFocused()); 356 // } 357 return IndexedCell.class.isAssignableFrom(row.getClass()) && ((IndexedCell) row).isFocused(); 358 } 359 }); 360 if (lookup.size() > 0) { 361 if (testedControl.getControl() instanceof TableView) { 362 TableCell cell = (TableCell) lookup.get(); 363 setResult(new Point(getColumnIndex(cell), cell.getTableRow().getIndex())); 364 } else { 365 TreeTableCell cell = (TreeTableCell) lookup.lookup(TreeTableCell.class).get(); 366 setResult(new Point(getColumnIndex(cell), cell.getTreeTableRow().getIndex())); 367 } 368 return; 369 } 370 } else { 371 Lookup lookup = testedControl.as(Parent.class, Node.class).lookup(new LookupCriteria<Node>() { 372 public boolean check(Node row) { 373 if (testedControl.getControl() instanceof TableView) { 374 return TableRow.class.isAssignableFrom(row.getClass()) 375 && ((TableRow) row).isVisible() 376 && ((TableRow) row).isFocused(); 377 } else { 378 return TreeTableRow.class.isAssignableFrom(row.getClass()) 379 && ((TreeTableRow) row).isVisible() 380 && ((TreeTableRow) row).isFocused(); 381 } 382 } 383 }); 384 if (lookup.size() > 0) { 385 if (lookup.size() > 1) { 386 throw new IllegalStateException("Too many focused rows."); 387 } 388 if (testedControl.getControl() instanceof TableView) { 389 setResult(new Point(-1, ((TableRow) lookup.get()).getIndex())); 390 } else { 391 setResult(new Point(-1, ((TreeTableRow) lookup.get()).getIndex())); 392 } 393 return; 394 } 395 } 396 setResult(new Point(-1, -1)); 397 } 398 }.dispatch(Root.ROOT.getEnvironment()); 399 } 400 401 protected static Wrap<? extends TableCell> getCellWrapByContent(final Wrap<? extends Control> testedControl, final int column, final int row) { 402 return testedControl.as(Parent.class, TableCell.class).lookup(new LookupCriteria<TableCell<TableViewApp.Data, String>>() { 403 public boolean check(TableCell<TableViewApp.Data, String> control) { 404 String item = control.getItem(); 405 return item != null && item.startsWith(String.format("item %02d field %d", row, column)); 406 } 407 }).wrap(); 408 } 409 410 // protected static Integer getColumn(TableCell cell) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { 411 // Field columnIndex = TableCell.class.getDeclaredField("columnIndex"); 412 // columnIndex.setAccessible(true); 413 // return (Integer) columnIndex.get(cell); 414 // } 415 protected static void clickOnFirstCell(final Wrap<? extends Control> tableViewWrap) { 416 getCellWrap(tableViewWrap, 0, 0).mouse().click(); 417 } 418 419 protected static Wrap getHeaderWrap(Wrap<? extends Control> tableViewWrap) { 420 return tableViewWrap.as(Parent.class, Node.class).lookup(Node.class, new ByStyleClass("column-header-background")).wrap(); 421 } 422 423 public static boolean isSelectedCellVisible(final Wrap<? extends Control> wrap) { 424 Lookup focusedCells; 425 426 if (!(wrap.getControl() instanceof TableView || wrap.getControl() instanceof TreeTableView)) { 427 return false; 428 } 429 430 focusedCells = wrap.as(Parent.class, Node.class).lookup(IndexedCell.class, 431 new LookupCriteria<IndexedCell>() { 432 public boolean check(IndexedCell cell) { 433 return (TreeTableCell.class.isAssignableFrom(cell.getClass()) || TableCell.class.isAssignableFrom(cell.getClass())) && cell.isFocused(); 434 } 435 }); 436 437 Assert.assertEquals("Must be only one focused cell", 1, focusedCells.size()); 438 439 final Wrap cell = focusedCells.wrap(); 440 441 Rectangle cellBounds = cell.getScreenBounds(); 442 Rectangle controlBounds = wrap.getScreenBounds(); 443 444 boolean isCellVisuallyVisible = controlBounds.contains(cellBounds); 445 boolean isCellProgrammlyVisible = new GetAction<Boolean>() { 446 @Override 447 public void run(Object... parameters) throws Exception { 448 setResult(((IndexedCell) cell.getControl()).isVisible()); 449 } 450 }.dispatch(wrap.getEnvironment()).booleanValue(); 451 452 if (!isCellVisuallyVisible) { 453 System.out.println("Focused cell is outside of control bounds."); 454 System.out.println("cell bounds = " + cellBounds); 455 System.out.println("control bounds = " + controlBounds); 456 457 System.out.println("wrap.getScreenBounds() = " + wrap.getScreenBounds()); 458 System.out.println("cell.getScreenBounds() = " + cell.getScreenBounds()); 459 } 460 461 if (!isCellProgrammlyVisible) { 462 System.out.println("Focused cell is invisible"); 463 } 464 465 return isCellVisuallyVisible && isCellProgrammlyVisible; 466 } 467 private static final String VIRTIAL_FLOW_CLASS_NAME = "VirtualFlow"; 468 private static final String CLIPPED_CONTAINER_CLASS_NAME = "VirtualFlow$ClippedContainer"; 469 }