001 /* 002 * Created on Jan 30, 2008 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 005 * the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 011 * specific language governing permissions and limitations under the License. 012 * 013 * Copyright @2008-2010 the original author or authors. 014 */ 015 package org.fest.swing.driver; 016 017 import static java.lang.Boolean.getBoolean; 018 import static org.fest.swing.core.WindowAncestorFinder.windowAncestorOf; 019 import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing; 020 import static org.fest.swing.driver.JMenuPopupMenuQuery.popupMenuOf; 021 import static org.fest.swing.driver.WindowMoveToFrontTask.toFront; 022 import static org.fest.swing.edt.GuiActionRunner.execute; 023 import static org.fest.swing.exception.ActionFailedException.actionFailure; 024 import static org.fest.swing.format.Formatting.format; 025 import static org.fest.swing.timing.Pause.pause; 026 import static org.fest.swing.util.Platform.isOSX; 027 import static org.fest.util.Strings.concat; 028 029 import java.awt.Window; 030 031 import javax.swing.*; 032 033 import org.fest.swing.annotation.RunsInEDT; 034 import org.fest.swing.core.Robot; 035 import org.fest.swing.edt.GuiQuery; 036 import org.fest.swing.edt.GuiTask; 037 import org.fest.swing.exception.ActionFailedException; 038 039 /** 040 * Understands functional testing of <code>{@link JMenuItem}</code>s: 041 * <ul> 042 * <li>user input simulation</li> 043 * <li>state verification</li> 044 * <li>property value query</li> 045 * </ul> 046 * This class is intended for internal use only. Please use the classes in the package 047 * <code>{@link org.fest.swing.fixture}</code> in your tests. 048 * 049 * @author Alex Ruiz 050 * @author Yvonne Wang 051 */ 052 public class JMenuItemDriver extends JComponentDriver { 053 054 /** 055 * Creates a new </code>{@link JMenuItemDriver}</code>. 056 * @param robot the robot to use to simulate user input. 057 */ 058 public JMenuItemDriver(Robot robot) { 059 super(robot); 060 } 061 062 /** 063 * Finds and selects the given <code>{@link JMenuItem}</code>. 064 * @param menuItem the <code>JMenuItem</code> to select. 065 * @throws IllegalStateException if the menu to select is disabled. 066 * @throws IllegalStateException if the menu to select is not showing on the screen. 067 * @throws ActionFailedException if the menu has a pop-up and it fails to show up. 068 */ 069 @RunsInEDT 070 public void click(JMenuItem menuItem) { 071 show(menuItem); 072 doClick(menuItem); 073 ensurePopupIsShowing(menuItem); 074 } 075 076 @RunsInEDT 077 private void show(JMenuItem menuItem) { 078 JMenuItemLocation location = locationOf(menuItem); 079 activateParentIfIsAMenu(location); 080 moveParentWindowToFront(location); 081 if (menuItem instanceof JMenu && !location.inMenuBar()) waitForSubMenuToShow(); 082 } 083 084 @RunsInEDT 085 private static JMenuItemLocation locationOf(final JMenuItem menuItem) { 086 return execute(new GuiQuery<JMenuItemLocation>() { 087 protected JMenuItemLocation executeInEDT() { 088 return new JMenuItemLocation(menuItem); 089 } 090 091 }); 092 } 093 094 @RunsInEDT 095 private void activateParentIfIsAMenu(JMenuItemLocation location) { 096 if (!location.isParentAMenu()) return; 097 click((JMenuItem)location.parentOrInvoker()); 098 } 099 100 @RunsInEDT 101 private void moveParentWindowToFront(JMenuItemLocation location) { 102 if (!location.inMenuBar()) return; 103 // TODO windowAncestorOf is not being called in EDT 104 moveToFront(windowAncestorOf(location.parentOrInvoker())); 105 } 106 107 @RunsInEDT 108 private void doClick(JMenuItem menuItem) { 109 if (isMacOSMenuBar()) { 110 validateAndDoClick(menuItem); 111 return; 112 } 113 super.click(menuItem); 114 robot.waitForIdle(); 115 } 116 117 private boolean isMacOSMenuBar() { 118 return isOSX() && (getBoolean("apple.laf.useScreenMenuBar") || getBoolean("com.apple.macos.useScreenMenuBar")); 119 } 120 121 @RunsInEDT 122 private static void validateAndDoClick(final JMenuItem menuItem) { 123 execute(new GuiTask() { 124 protected void executeInEDT() { 125 validateIsEnabledAndShowing(menuItem); 126 menuItem.doClick(); 127 } 128 }); 129 } 130 131 @RunsInEDT 132 private void ensurePopupIsShowing(JMenuItem menuItem) { 133 if (!(menuItem instanceof JMenu)) return; 134 JPopupMenu popup = popupMenuOf((JMenu)menuItem); 135 // TODO review EDT access 136 if (!waitForShowing(popup, robot.settings().timeoutToFindPopup())) 137 throw actionFailure(concat("Clicking on menu item <", format(menuItem), "> never showed a pop-up menu")); 138 waitForSubMenuToShow(); 139 } 140 141 private void waitForSubMenuToShow() { 142 pause(robot.settings().timeoutToFindSubMenu()); 143 } 144 145 @RunsInEDT 146 private void moveToFront(Window w) { 147 if (w == null) return; 148 // Make sure the window is in front, or its menus may be obscured by another window. 149 toFront(w); 150 robot.waitForIdle(); 151 robot.moveMouse(w); 152 } 153 }