All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.assertj.swing.driver.ComponentDriver Maven / Gradle / Ivy

There is a newer version: 3.17.1
Show newest version
/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Copyright 2012-2015 the original author or authors.
 */
package org.assertj.swing.driver;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.swing.core.MouseButton.LEFT_BUTTON;
import static org.assertj.swing.core.MouseButton.RIGHT_BUTTON;
import static org.assertj.swing.driver.ComponentEnabledCondition.untilIsEnabled;
import static org.assertj.swing.driver.ComponentPerformDefaultAccessibleActionTask.performDefaultAccessibleAction;
import static org.assertj.swing.driver.ComponentPreconditions.checkEnabledAndShowing;
import static org.assertj.swing.driver.ComponentPreconditions.checkShowing;
import static org.assertj.swing.edt.GuiActionRunner.execute;
import static org.assertj.swing.format.Formatting.format;
import static org.assertj.swing.query.ComponentEnabledQuery.isEnabled;
import static org.assertj.swing.query.ComponentHasFocusQuery.hasFocus;
import static org.assertj.swing.query.ComponentSizeQuery.sizeOf;
import static org.assertj.swing.query.ComponentVisibleQuery.isVisible;
import static org.assertj.swing.timing.Pause.pause;
import static org.assertj.swing.util.TimeoutWatch.startWatchWithTimeoutOf;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;

import javax.annotation.Nonnull;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;

import org.assertj.core.description.Description;
import org.assertj.swing.annotation.RunsInCurrentThread;
import org.assertj.swing.annotation.RunsInEDT;
import org.assertj.swing.core.ComponentDragAndDrop;
import org.assertj.swing.core.KeyPressInfo;
import org.assertj.swing.core.MouseButton;
import org.assertj.swing.core.MouseClickInfo;
import org.assertj.swing.core.Robot;
import org.assertj.swing.core.Settings;
import org.assertj.swing.edt.GuiLazyLoadingDescription;
import org.assertj.swing.internal.annotation.InternalApi;
import org.assertj.swing.timing.Timeout;
import org.assertj.swing.util.TimeoutWatch;

/**
 * 

* Supports functional testing of AWT or Swing {@code Component}s. *

* *

* Note: This class is intended for internal use only. Please use the classes in the package * {@link org.assertj.swing.fixture} in your tests. *

* * @author Alex Ruiz */ @InternalApi public class ComponentDriver { private static final String ENABLED_PROPERTY = "enabled"; private static final String SIZE_PROPERTY = "size"; private static final String VISIBLE_PROPERTY = "visible"; protected final Robot robot; private final ComponentDragAndDrop dragAndDrop; /** * Creates a new {@link ComponentDriver}. * * @param robot the robot to use to simulate user input. */ public ComponentDriver(@Nonnull Robot robot) { this.robot = robot; dragAndDrop = new ComponentDragAndDrop(robot); } /** * Simulates a user clicking once the given AWT or Swing {@code Component} using the left mouse button. * * @param c the {@code Component} to click on. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void click(@Nonnull Component c) { checkClickAllowed(c); robot.click(c); } /** * Simulates a user clicking once the given AWT or Swing {@code Component} using the given mouse button. * * @param c the {@code Component} to click on. * @param button the mouse button to use. * @throws NullPointerException if the given {@code MouseButton} is {@code null}. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void click(@Nonnull Component c, @Nonnull MouseButton button) { click(c, button, 1); } /** * Simulates a user clicking the given mouse button, the given times on the given AWT or Swing {@code Component}. * * @param c the {@code Component} to click on. * @param mouseClickInfo specifies the button to click and the times the button should be clicked. * @throws NullPointerException if the given {@code MouseClickInfo} is {@code null}. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void click(@Nonnull Component c, @Nonnull MouseClickInfo mouseClickInfo) { checkNotNull(mouseClickInfo); click(c, mouseClickInfo.button(), mouseClickInfo.times()); } /** * Simulates a user double-clicking the given AWT or Swing {@code Component}. * * @param c the {@code Component} to click on. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void doubleClick(@Nonnull Component c) { click(c, LEFT_BUTTON, 2); } /** * Simulates a user right-clicking the given AWT or Swing {@code Component}. * * @param c the {@code Component} to click on. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void rightClick(@Nonnull Component c) { click(c, RIGHT_BUTTON); } /** * Simulates a user clicking the given mouse button, the given times on the given AWT or Swing {@code Component}. * * @param c the {@code Component} to click on. * @param button the mouse button to click. * @param times the number of times to click the given mouse button. * @throws NullPointerException if the given {@code MouseButton} is {@code null}. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void click(@Nonnull Component c, @Nonnull MouseButton button, int times) { checkNotNull(button); checkClickAllowed(c); robot.click(c, button, times); } /** * Simulates a user clicking at the given position on the given AWT or Swing {@code Component}. * * @param c the {@code Component} to click on. * @param where the position where to click. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void click(@Nonnull Component c, @Nonnull Point where) { checkClickAllowed(c); robot.click(c, where); } protected @Nonnull Settings settings() { return robot.settings(); } /** * Asserts that the size of the AWT or Swing {@code Component} is equal to given one. * * @param c the target {@code Component}. * @param size the given size to match. * @throws AssertionError if the size of the {@code Component} is not equal to the given size. */ @RunsInEDT public void requireSize(@Nonnull Component c, @Nonnull Dimension size) { assertThat(sizeOf(c)).as(propertyName(c, SIZE_PROPERTY)).isEqualTo(size); } /** * Asserts that the AWT or Swing {@code Component} is visible. * * @param c the target {@code Component}. * @throws AssertionError if the {@code Component} is not visible. */ @RunsInEDT public void requireVisible(@Nonnull Component c) { assertThat(isVisible(c)).as(visibleProperty(c)).isTrue(); } /** * Asserts that the AWT or Swing {@code Component} is not visible. * * @param c the target {@code Component}. * @throws AssertionError if the {@code Component} is visible. */ @RunsInEDT public void requireNotVisible(@Nonnull Component c) { assertThat(isVisible(c)).as(visibleProperty(c)).isFalse(); } @RunsInEDT private static @Nonnull Description visibleProperty(@Nonnull Component c) { return propertyName(c, VISIBLE_PROPERTY); } /** * Asserts that the AWT or Swing {@code Component} has input focus. * * @param c the target {@code Component}. * @throws AssertionError if the {@code Component} does not have input focus. */ @RunsInEDT public void requireFocused(@Nonnull Component c) { assertThat(hasFocus(c)).as(requiredFocusedErrorMessage(c)).isTrue(); } private static @Nonnull Description requiredFocusedErrorMessage(final Component c) { return new GuiLazyLoadingDescription() { @Override protected @Nonnull String loadDescription() { return String.format("Expected component %s to have input focus", format(c)); } }; } /** * Asserts that the AWT or Swing {@code Component} is enabled. * * @param c the target {@code Component}. * @throws AssertionError if the {@code Component} is disabled. */ @RunsInEDT public void requireEnabled(@Nonnull Component c) { assertThat(isEnabled(c)).as(enabledProperty(c)).isTrue(); } /** * Asserts that the AWT or Swing {@code Component} is enabled. * * @param c the target {@code Component}. * @param timeout the time this fixture will wait for the {@code Component} to be enabled. * @throws org.assertj.swing.exception.WaitTimedOutError if the {@code Component} is never enabled. */ @RunsInEDT public void requireEnabled(@Nonnull Component c, @Nonnull Timeout timeout) { pause(untilIsEnabled(c), timeout); } /** * Asserts that the AWT or Swing {@code Component} is disabled. * * @param c the target {@code Component}. * @throws AssertionError if the {@code Component} is enabled. */ @RunsInEDT public void requireDisabled(@Nonnull Component c) { assertThat(isEnabled(c)).as(enabledProperty(c)).isFalse(); } @RunsInEDT private static @Nonnull Description enabledProperty(@Nonnull Component c) { return propertyName(c, ENABLED_PROPERTY); } /** * Simulates a user pressing and releasing the given keys on the AWT or Swing {@code Component}. * * @param c the target {@code Component}. * @param keyCodes one or more codes of the keys to press. * @throws NullPointerException if the given array of codes is {@code null}. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @throws IllegalArgumentException if the given code is not a valid key code. * @see java.awt.event.KeyEvent */ @RunsInEDT public void pressAndReleaseKeys(@Nonnull Component c, @Nonnull int... keyCodes) { checkNotNull(keyCodes); checkInEdtEnabledAndShowing(c); focusAndWaitForFocusGain(c); robot.pressAndReleaseKeys(keyCodes); } /** * Simulates a user pressing and releasing the given key on the AWT or Swing {@code Component}. Modifiers is a mask * from the available AWT {@code InputEvent} masks. * * @param c the target {@code Component}. * @param keyPressInfo specifies the key and modifiers to press. * @throws NullPointerException if the given {@code KeyPressInfo} is {@code null}. * @throws IllegalArgumentException if the given code is not a valid key code. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @see java.awt.event.KeyEvent * @see java.awt.event.InputEvent */ @RunsInEDT public void pressAndReleaseKey(@Nonnull Component c, @Nonnull KeyPressInfo keyPressInfo) { checkNotNull(keyPressInfo); pressAndReleaseKey(c, keyPressInfo.keyCode(), keyPressInfo.modifiers()); } /** * Simulates a user pressing and releasing the given key on the AWT or Swing {@code Component}. Modifiers is a mask * from the available AWT {@code InputEvent} masks. * * @param c the target {@code Component}. * @param keyCode the code of the key to press. * @param modifiers the given modifiers. * @throws IllegalArgumentException if the given code is not a valid key code. * * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @see java.awt.event.KeyEvent * @see java.awt.event.InputEvent */ @RunsInEDT public void pressAndReleaseKey(@Nonnull Component c, int keyCode, @Nonnull int[] modifiers) { focusAndWaitForFocusGain(c); robot.pressAndReleaseKey(keyCode, modifiers); } /** * Simulates a user pressing given key on the AWT or Swing {@code Component}. * * @param c the target {@code Component}. * @param keyCode the code of the key to press. * @throws IllegalArgumentException if the given code is not a valid key code. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @see #pressKeyWhileRunning(Component, int) * @see java.awt.event.KeyEvent */ @RunsInEDT public void pressKey(@Nonnull Component c, int keyCode) { focusAndWaitForFocusGain(c); robot.pressKey(keyCode); } /** * Simulates a user pressing given key on the AWT or Swing {@code Component}, running the given runnable and releasing * the key again. * * @param c the target {@code Component}. * @param keyCode the code of the key to press. * @throws IllegalArgumentException if the given code is not a valid key code. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @see #pressKey(Component, int) * @see java.awt.event.KeyEvent */ @RunsInEDT public void pressKeyWhileRunning(@Nonnull Component c, int keyCode, @Nonnull Runnable runnable) { focusAndWaitForFocusGain(c); robot.pressKeyWhileRunning(keyCode, runnable); } /** * Simulates a user releasing the given key on the AWT or Swing {@code Component}. * * @param c the target {@code Component}. * @param keyCode the code of the key to release. * @throws IllegalArgumentException if the given code is not a valid key code. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @see java.awt.event.KeyEvent */ @RunsInEDT public void releaseKey(@Nonnull Component c, int keyCode) { focusAndWaitForFocusGain(c); robot.releaseKey(keyCode); } /** * Gives input focus to the given AWT or Swing {@code Component} and waits until the {@code Component} has focus. * * @param c the {@code Component} to give focus to. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void focusAndWaitForFocusGain(@Nonnull Component c) { checkInEdtEnabledAndShowing(c); robot.focusAndWaitForFocusGain(c); } /** * Gives input focus to the given AWT or Swing {@code Component}. Note that the {@code Component} may not yet have * focus when this method returns. * * @param c the {@code Component} to give focus to. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT public void focus(@Nonnull Component c) { checkInEdtEnabledAndShowing(c); robot.focus(c); } /** * Performs a drag action at the given point. * * @param c the target {@code Component}. * @param where the point where to start the drag action. */ @RunsInEDT protected final void drag(@Nonnull Component c, @Nonnull Point where) { dragAndDrop.drag(c, where); } /** *

* Ends a drag operation, releasing the mouse button over the given target location. *

*

* This method is tuned for native drag/drop operations, so if you get odd behavior, you might try using a simple * {@link Robot#moveMouse(Component, int, int)} and {@link Robot#releaseMouseButtons()}. * * @param c the target {@code Component}. * @param where the point where the drag operation ends. * @throws org.assertj.swing.exception.ActionFailedException if there is no drag action in effect. */ @RunsInEDT protected final void drop(@Nonnull Component c, @Nonnull Point where) { dragAndDrop.drop(c, where); } /** * Move the mouse appropriately to get from the source to the destination. Enter/exit events will be generated where * appropriate. * * @param c the target {@code Component}. * @param where the point to drag over. */ protected final void dragOver(@Nonnull Component c, @Nonnull Point where) { dragAndDrop.dragOver(c, where); } /** *

* Performs the {@code AccessibleAction} in the given AWT or Swing {@code Component}'s event queue. *

* *

* Note: This method is accessed in the current executing thread. Such thread may or may not be the event * dispatch thread (EDT). Client code must call this method from the EDT. *

* * @param c the given {@code Component}. * @throws org.assertj.swing.exception.ActionFailedException if something goes wrong. */ @RunsInCurrentThread protected final void performAccessibleActionOf(@Nonnull Component c) { performDefaultAccessibleAction(c); robot.waitForIdle(); } /** *

* Wait the given number of milliseconds for the AWT or Swing {@code Component} to be showing and ready. Returns * {@code false} if the operation times out. *

* *

* Note: This method is accessed in the current executing thread. Such thread may or may not be the event * dispatch thread (EDT). Client code must call this method from the EDT. *

* * @param c the given {@code Component}. * @param timeout the time in milliseconds to wait for the {@code Component} to be showing and ready. * @return {@code true} if the {@code Component} is showing and ready, {@code false} otherwise. */ @RunsInCurrentThread protected final boolean waitForShowing(@Nonnull Component c, long timeout) { // TODO test if (robot.isReadyForInput(c)) { return true; } TimeoutWatch watch = startWatchWithTimeoutOf(timeout); while (!robot.isReadyForInput(c)) { if (c instanceof JPopupMenu) { // move the mouse over the parent menu item to ensure the sub-menu shows Component invoker = ((JPopupMenu) c).getInvoker(); if (invoker instanceof JMenu) { robot.jitter(invoker); } } if (watch.isTimeOut()) { return false; } pause(); } return true; } /** * Shows a pop-up menu using the given AWT or Swing {@code Component} as the invoker of the pop-up menu. * * @param c the invoker of the {@code JPopupMenu}. * @return the displayed pop-up menu. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @throws org.assertj.swing.exception.ComponentLookupException if a pop-up menu cannot be found. */ @RunsInEDT public @Nonnull JPopupMenu invokePopupMenu(@Nonnull Component c) { checkClickAllowed(c); return robot.showPopupMenu(c); } /** * Shows a pop-up menu at the given point using the given AWT or Swing {@code Component} as the invoker of the pop-up * menu. * * @param c the invoker of the {@code JPopupMenu}. * @param p the given point where to show the pop-up menu. * @return the displayed pop-up menu. * @throws NullPointerException if the given point is {@code null}. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. * @throws org.assertj.swing.exception.ComponentLookupException if a pop-up menu cannot be found. */ @RunsInEDT public @Nonnull JPopupMenu invokePopupMenu(@Nonnull Component c, @Nonnull Point p) { checkNotNull(p); checkClickAllowed(c); return robot.showPopupMenu(c, p); } /** * Verifies that the given AWT or Swing {@code Component} is enabled and showing on the screen. * * @param c the {@code Component} to check. * @throws IllegalStateException if the {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT protected static void checkInEdtEnabledAndShowing(final @Nonnull Component c) { execute(() -> checkEnabledAndShowing(c)); } /** * Verifies that the given AWT or Swing {@code Component} is showing on the screen. * * @param c the {@code Component} to check. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT protected static void checkInEdtShowing(final @Nonnull Component c) { execute(() -> checkShowing(c)); } /** * Verifies that the given AWT or Swing {@code Component} is enabled and showing on the screen. * * @param c the {@code Component} to check. * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and the * {@code Component} is disabled. * @throws IllegalStateException if the {@code Component} is not showing on the screen. */ @RunsInEDT protected void checkClickAllowed(final @Nonnull Component c) { if (robot.settings().clickOnDisabledComponentsAllowed()) { checkInEdtShowing(c); } else { checkInEdtEnabledAndShowing(c); } } /** * Formats the name of a property of the given AWT or Swing {@code Component} by concatenating the value obtained from * {@link org.assertj.swing.format.Formatting#format(Component)} with the given property name. * * @param c the given {@code Component}. * @param propertyName the name of the property. * @return the description of a property belonging to a {@code Component}. * @see org.assertj.swing.format.ComponentFormatter * @see org.assertj.swing.format.Formatting#format(Component) */ @RunsInEDT public static @Nonnull Description propertyName(final @Nonnull Component c, final @Nonnull String propertyName) { return new GuiLazyLoadingDescription() { @Override protected @Nonnull String loadDescription() { return String.format("%s - property:'%s'", format(c), propertyName); } }; } /** * Simulates a user moving the mouse pointer to the given coordinates relative to the given AWT or Swing * {@code Component}. This method will not throw any exceptions if the it was not possible to move the mouse * pointer. * * @param c the given {@code Component}. * @param p coordinates relative to the given {@code Component}. */ @RunsInEDT protected final void moveMouseIgnoringAnyError(@Nonnull Component c, @Nonnull Point p) { moveMouseIgnoringAnyError(c, p.x, p.y); } /** * Simulates a user moving the mouse pointer to the given coordinates relative to the given AWT or Swing * {@code Component}. This method will not throw any exceptions if the it was not possible to move the mouse * pointer. * * @param c the given {@code Component}. * @param x horizontal coordinate relative to the given {@code Component}. * @param y vertical coordinate relative to the given {@code Component}. */ @RunsInEDT protected final void moveMouseIgnoringAnyError(@Nonnull Component c, int x, int y) { try { robot.moveMouse(c, x, y); } catch (RuntimeException ignored) { } } /** * Returns the font of the given AWT or Swing {@code Component}. * * @param c the given {@code Component}. * @return the font of the given {@code Component}. */ @RunsInEDT public @Nonnull Font fontOf(final @Nonnull Component c) { Font result = execute(() -> c.getFont()); return checkNotNull(result); } /** * Returns the background color of the given AWT or Swing {@code Component}. * * @param c the given {@code Component}. * @return the background color of the given {@code Component}. */ @RunsInEDT public @Nonnull Color backgroundOf(final @Nonnull Component c) { Color result = execute(() -> c.getBackground()); return checkNotNull(result); } /** * Returns the foreground color of the given AWT or Swing {@code Component}. * * @param c the given {@code Component}. * @return the foreground color of the given {@code Component}. */ @RunsInEDT public @Nonnull Color foregroundOf(final @Nonnull Component c) { Color result = execute(() -> c.getForeground()); return checkNotNull(result); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy