/*******************************************************************************
* Copyright (c) 2014 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.rc.javafx.tester;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.eclipse.jubula.rc.common.exception.StepExecutionException;
import org.eclipse.jubula.rc.common.listener.EventLock;
import org.eclipse.jubula.rc.common.tester.AbstractMenuTester;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IMenuComponent;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IMenuItemComponent;
import org.eclipse.jubula.rc.javafx.driver.EventThreadQueuerJavaFXImpl;
import org.eclipse.jubula.rc.javafx.listener.ComponentHandler;
import org.eclipse.jubula.rc.javafx.tester.adapter.MenuAdapter;
import org.eclipse.jubula.rc.javafx.tester.adapter.MenuItemAdapter;
import org.eclipse.jubula.tools.internal.constants.TimeoutConstants;
import org.eclipse.jubula.tools.internal.objects.event.EventFactory;
import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.stage.WindowEvent;
/**
* Toolkit specific commands for the <code>Menu</code> and <code>MenuBar</code>.
*
* @author BREDEX GmbH
* @created 10.2.2014
*/
public class MenuTester extends AbstractMenuTester {
@Override
public String[] getTextArrayFromComponent() {
return null;
}
@Override
protected IMenuItemComponent newMenuItemAdapter(Object component) {
return new MenuItemAdapter<MenuItem>((MenuItem) component);
}
@Override
protected void closeMenu(IMenuComponent menu, String[] textPath,
String operator) {
IMenuComponent currMenu = menu;
List<IMenuComponent> openMenus = new ArrayList<>();
for (String name : textPath) {
IMenuItemComponent[] items = currMenu.getItems();
int index = getIndexForName(currMenu, name, operator);
IMenuItemComponent item = null;
// Because the index returned by getIndexForname only counts real
// menu items not things
// like separators or disable menu items, we have to find the right
// item for the index
for (int j = index; j < items.length; j++) {
item = items[j];
if ((!item.isSeparator()) && item.isEnabled()
&& item.isShowing()) {
break;
}
}
if (item != null && item.hasSubMenu()) {
currMenu = new MenuAdapter((Menu) item.getRealComponent());
openMenus.add(currMenu);
}
}
for (int i = openMenus.size() - 1; i >= 0; i--) {
boolean successful = waitForMenuToClose((Menu) openMenus.get(i)
.getRealComponent());
if (!successful) {
throw new StepExecutionException("Popup could not be closed", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
}
if (menu.getRealComponent() instanceof ContextMenu) {
boolean successful = closeContextMenu(
(ContextMenu) menu.getRealComponent());
if (!successful) {
throw new StepExecutionException("Popup could not be closed", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
}
}
@Override
protected void closeMenu(IMenuComponent menu, int[] path) {
IMenuComponent currMenu = menu;
List<IMenuComponent> openMenus = new ArrayList<>();
for (int index : path) {
IMenuItemComponent[] items = currMenu.getItems();
// Because the index in path only counts real menu items not things
// like separators or disable menu items, we have to find the right
// item for the index
IMenuItemComponent item = null;
for (int j = index; j < items.length; j++) {
item = items[j];
if ((!item.isSeparator()) && item.isEnabled()
&& item.isShowing()) {
break;
}
}
if (!(item == null || (item.isSeparator()) || (!item.isEnabled())
|| (!item.isShowing())) && item.hasSubMenu()) {
currMenu = new MenuAdapter((Menu) item.getRealComponent());
openMenus.add(currMenu);
}
}
for (int i = openMenus.size() - 1; i >= 0; i--) {
boolean successful = waitForMenuToClose((Menu) openMenus.get(i)
.getRealComponent());
if (!successful) {
throw new StepExecutionException("Popup could not be closed", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
}
if (menu.getRealComponent() instanceof ContextMenu) {
boolean successful = closeContextMenu(
(ContextMenu) menu.getRealComponent());
if (!successful) {
throw new StepExecutionException("Popup could not be closed", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
POPUP_NOT_FOUND));
}
}
}
/**
* Closes a ContextMenu and waits for it to be closed
*
* @param m
* the menu
* @return true if menu was closed successfully, false if not;
*/
private boolean closeContextMenu(final ContextMenu m) {
final EventLock eventLock = new EventLock();
final EventHandler<Event> shownHandler = new EventHandler<Event>() {
@Override
public void handle(Event event) {
synchronized (eventLock) {
eventLock.notifyAll();
}
}
};
EventThreadQueuerJavaFXImpl.invokeAndWait(
"addCloseHandler", new Callable<Void>() { //$NON-NLS-1$
@Override
public Void call() throws Exception {
m.addEventHandler(
WindowEvent.WINDOW_HIDDEN, shownHandler);
return null;
}
});
boolean successful = false;
getRobot().keyType(null, KeyEvent.VK_ESCAPE);
try {
if (m.isShowing()) {
synchronized (eventLock) {
eventLock
.wait(TimeoutConstants.
SERVER_TIMEOUT_WAIT_FOR_POPUP);
}
}
} catch (InterruptedException e) {
// ignore
} finally {
successful = EventThreadQueuerJavaFXImpl.invokeAndWait("closeMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
m.removeEventHandler(
WindowEvent.WINDOW_HIDDEN, shownHandler);
return !m.isShowing();
}
});
}
return successful;
}
/**
* Closes a menu and waits for it to be closed
*
* @param m
* the menu
* @return true if menu was closed successfully, false if not;
*/
private boolean waitForMenuToClose(final Menu m) {
final EventLock eventLock = new EventLock();
final EventHandler<Event> shownHandler = new EventHandler<Event>() {
@Override
public void handle(Event event) {
synchronized (eventLock) {
eventLock.notifyAll();
}
}
};
EventThreadQueuerJavaFXImpl.invokeAndWait(
"addCloseHandler", new Callable<Void>() { //$NON-NLS-1$
@Override
public Void call() throws Exception {
m.addEventHandler(Menu.ON_HIDDEN, shownHandler);
return null;
}
});
boolean successful = false;
getRobot().keyType(null, KeyEvent.VK_ESCAPE);
try {
if (m.isShowing()) {
synchronized (eventLock) {
eventLock
.wait(TimeoutConstants.
SERVER_TIMEOUT_WAIT_FOR_POPUP);
}
}
} catch (InterruptedException e) {
// ignore
} finally {
successful = EventThreadQueuerJavaFXImpl.invokeAndWait("closeMenu", //$NON-NLS-1$
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
m.removeEventHandler(Menu.ON_HIDDEN, shownHandler);
return !m.isShowing();
}
});
}
return successful;
}
@Override
public IComponent getComponent() {
IComponent adapt = super.getComponent();
if (adapt != null
&& (adapt.getRealComponent() instanceof ContextMenu)) {
return adapt;
}
List<? extends MenuBar> bars = ComponentHandler
.getAssignableFrom(MenuBar.class);
switch (bars.size()) {
case 1:
setComponent(bars.get(0));
break;
case 0:
setComponent(null);
break;
default:
throw new StepExecutionException("Multiple MenuBars found", //$NON-NLS-1$
EventFactory
.createActionError(TestErrorEvent.
UNSUPPORTED_OPERATION_IN_TOOLKIT_ERROR));
}
return super.getComponent();
}
}