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

org.assertj.swing.fixture.AbstractComponentFixture 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.fixture;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.swing.driver.ComponentDriver.propertyName;
import static org.assertj.swing.format.Formatting.format;

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

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

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.driver.ComponentDriver;
import org.assertj.swing.query.ComponentEnabledQuery;
import org.assertj.swing.timing.Timeout;

/**
 * Supports functional testing of AWT and Swing {@code Component}s.
 *
 * @param  used to simulate "self types." For more information please read "Emulating 'self types' using Java Generics to simplify fluent API implementation."
 * @param  the type of {@code Component} that this fixture can manage.
 * @param  the type of {@link ComponentDriver} that this fixture uses internally.
 *
 * @author Alex Ruiz
 * @author Yvonne Wang
 */
public abstract class AbstractComponentFixture implements
    MouseInputSimulationFixture {
  /** Name of the property "font". */
  protected static final String FONT_PROPERTY = "font";

  /** Name of the property "background". */
  protected static final String BACKGROUND_PROPERTY = "background";

  /** Name of the property "foreground". */
  protected static final String FOREGROUND_PROPERTY = "foreground";

  /** Performs simulation of user events on {@link #target} */
  private final Robot robot;

  private final C target;
  private final S myself;

  private D driver;

  /**
   * Creates a new {@link AbstractComponentFixture}.
   *
   * @param selfType the "self type."
   * @param robot performs simulation of user events on a {@code Component}.
   * @param type the type of the {@code Component} to find using the given {@code Robot}.
   * @throws NullPointerException if {@code robot} is {@code null}.
   * @throws NullPointerException if {@code type} is {@code null}.
   * @throws org.assertj.swing.exception.ComponentLookupException if a matching component could not be found.
   * @throws org.assertj.swing.exception.ComponentLookupException if more than one matching component is found.
   */
  public AbstractComponentFixture(@Nonnull Class selfType, @Nonnull Robot robot, @Nonnull Class type) {
    this(selfType, robot, findTarget(robot, type));
  }

  private static @Nonnull  C findTarget(@Nonnull Robot robot, @Nonnull Class type) {
    checkNotNull(robot);
    checkNotNull(type);
    return robot.finder().findByType(type, requireShowing(robot));
  }

  /**
   * Creates a new {@link AbstractComponentFixture}.
   *
   * @param selfType the "self type."
   * @param robot performs simulation of user events on a {@code Component}.
   * @param name the name of the {@code Component} to find using the given {@code Robot}.
   * @param type the type of the {@code Component} to find using the given {@code Robot}.
   * @throws NullPointerException if {@code robot} is {@code null}.
   * @throws NullPointerException if {@code type} is {@code null}.
   * @throws org.assertj.swing.exception.ComponentLookupException if a matching component could not be found.
   * @throws org.assertj.swing.exception.ComponentLookupException if more than one matching component is found.
   */
  public AbstractComponentFixture(@Nonnull Class selfType, @Nonnull Robot robot, @Nullable String name,
                                  @Nonnull Class type) {
    this(selfType, robot, findTarget(robot, name, type));
  }

  private static @Nonnull  C findTarget(@Nonnull Robot robot, @Nullable String name,
                                                             @Nonnull Class type) {
    checkNotNull(robot);
    checkNotNull(type);
    return robot.finder().findByName(name, type, requireShowing(robot));
  }

  /**
   * Creates a new {@link AbstractComponentFixture}.
   *
   * @param selfType the "self type."
   * @param robot performs simulation of user events on the given {@code Component}.
   * @param target the {@code Component} to be managed by this fixture.
   * @throws NullPointerException if {@code selfType} is {@code null}.
   * @throws NullPointerException if {@code robot} is {@code null}.
   * @throws NullPointerException if {@code target} is {@code null}.
   */
  public AbstractComponentFixture(@Nonnull Class selfType, @Nonnull Robot robot, @Nonnull C target) {
    myself = checkNotNull(selfType).cast(this);
    this.robot = checkNotNull(robot);
    this.target = checkNotNull(target);
    replaceDriverWith(createDriver(robot));
  }

  protected abstract @Nonnull D createDriver(@Nonnull Robot robot);

  protected final @Nonnull D driver() {
    return driver;
  }

  public final void replaceDriverWith(@Nonnull D driver) {
    this.driver = checkNotNull(driver);
  }

  /**
   * Simulates a user clicking this fixture's {@code Component}.
   *
   * @return this fixture.
   * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and this
   *           fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  @Override
  public final @Nonnull S click() {
    driver.click(target());
    return myself();
  }

  /**
   * Simulates a user clicking this fixture's {@code Component}.
   *
   * @param button the button to click.
   * @return this fixture.
   * @throws NullPointerException if the given {@code MouseButton} is {@code null}.
   * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and this
   *           fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  @Override
  public final @Nonnull S click(@Nonnull MouseButton button) {
    driver.click(target(), button);
    return myself();
  }

  /**
   * Simulates a user clicking this fixture's {@code Component}.
   *
   * @param mouseClickInfo specifies the button to click and the times the button should be clicked.
   * @return this fixture.
   * @throws NullPointerException if the given {@code MouseClickInfo} is {@code null}.
   * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and this
   *           fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  @Override
  public final @Nonnull S click(@Nonnull MouseClickInfo mouseClickInfo) {
    driver.click(target(), mouseClickInfo);
    return myself();
  }

  /**
   * Simulates a user double-clicking this fixture's {@code Component}.
   *
   * @return this fixture.
   * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and this
   *           fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  @Override
  public final @Nonnull S doubleClick() {
    driver.doubleClick(target());
    return myself();
  }

  /**
   * Simulates a user right-clicking this fixture's {@code Component}.
   *
   * @return this fixture.
   * @throws IllegalStateException if {@link Settings#clickOnDisabledComponentsAllowed()} is false and this
   *           fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  @Override
  public final @Nonnull S rightClick() {
    driver.rightClick(target());
    return myself();
  }

  /**
   * Gives input focus to this fixture's {@code Component}.
   *
   * @return this fixture.
   * @throws IllegalStateException if this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   */
  public final @Nonnull S focus() {
    driver.focus(target());
    return myself();
  }

  /**
   * Simulates a user pressing given key with the given modifiers on this fixture's {@code Component}. Modifiers is a
   * mask from the available AWT {@code InputEvent} masks.
   *
   * @param keyPressInfo specifies the key and modifiers to press.
   * @return this fixture.
   * @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 this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   * @see KeyPressInfo
   */
  public final @Nonnull S pressAndReleaseKey(@Nonnull KeyPressInfo keyPressInfo) {
    driver.pressAndReleaseKey(target(), keyPressInfo);
    return myself();
  }

  /**
   * Simulates a user pressing and releasing the given keys on this fixture's {@code Component}.
   *
   * @param keyCodes one or more codes of the keys to press.
   * @return this fixture.
   * @throws NullPointerException if the given array of codes is {@code null}.
   * @throws IllegalArgumentException if any of the given code is not a valid key code.
   * @throws IllegalStateException if this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   * @see java.awt.event.KeyEvent
   */
  public final @Nonnull S pressAndReleaseKeys(@Nonnull int... keyCodes) {
    driver.pressAndReleaseKeys(target(), keyCodes);
    return myself();
  }

  /**
   * Simulates a user pressing the given key on this fixture's {@code Component}.
   *
   * @param keyCode the code of the key to press.
   * @return this fixture.
   * @throws IllegalArgumentException if any of the given code is not a valid key code.
   * @throws IllegalStateException if this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   * @see #pressKeyWhileRunning(int, Runnable)
   * @see java.awt.event.KeyEvent
   */
  public final @Nonnull S pressKey(int keyCode) {
    driver.pressKey(target(), keyCode);
    return myself();
  }

  /**
   * Simulates a user pressing the given key on this fixture's {@code Component}, running the given runnable and
   * releasing the key again.
   *
   * @param keyCode the code of the key to press.
   * @return this fixture.
   * @throws IllegalArgumentException if any of the given code is not a valid key code.
   * @throws IllegalStateException if this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   * @see #pressKey(int)
   * @see java.awt.event.KeyEvent
   */
  public final @Nonnull S pressKeyWhileRunning(int keyCode, @Nonnull Runnable runnable) {
    driver.pressKeyWhileRunning(target(), keyCode, runnable);
    return myself();
  }

  /**
   * Simulates a user releasing the given key on this fixture's {@code Component}.
   *
   * @param keyCode the code of the key to release.
   * @return this fixture.
   * @throws IllegalArgumentException if any of the given code is not a valid key code.
   * @throws IllegalStateException if this fixture's {@code Component} is disabled.
   * @throws IllegalStateException if this fixture's {@code Component} is not showing on the screen.
   * @see java.awt.event.KeyEvent
   */
  public final @Nonnull S releaseKey(int keyCode) {
    driver.releaseKey(target(), keyCode);
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} has input focus.
   *
   * @return this fixture.
   * @throws AssertionError if this fixture's {@code Component} does not have input focus.
   */
  public final @Nonnull S requireFocused() {
    driver.requireFocused(target());
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} is enabled.
   *
   * @return this fixture.
   * @throws AssertionError if this fixture's {@code Component} is disabled.
   */
  public final @Nonnull S requireEnabled() {
    driver.requireEnabled(target());
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} is enabled.
   *
   * @param timeout the time this fixture will wait for the component to be enabled.
   * @return this fixture.
   * @throws org.assertj.swing.exception.WaitTimedOutError if this fixture's {@code Component} is never enabled.
   */
  public final @Nonnull S requireEnabled(@Nonnull Timeout timeout) {
    driver.requireEnabled(target(), timeout);
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} is disabled.
   *
   * @return this fixture.
   * @throws AssertionError if this fixture's {@code Component} is enabled.
   */
  public final @Nonnull S requireDisabled() {
    driver.requireDisabled(target());
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} is visible.
   *
   * @return this fixture.
   * @throws AssertionError if this fixture's {@code Component} is not visible.
   */
  public final @Nonnull S requireVisible() {
    driver.requireVisible(target());
    return myself();
  }

  /**
   * Asserts that this fixture's {@code Component} is not visible.
   *
   * @return this fixture.
   * @throws AssertionError if this fixture's {@code Component} is visible.
   */
  public final @Nonnull S requireNotVisible() {
    driver.requireNotVisible(target());
    return myself();
  }

  /**
   * Returns whether showing components are the only ones participating in a component lookup. The returned value is
   * obtained from the {@link org.assertj.swing.core.Settings#componentLookupScope() component lookup scope} stored in
   * this fixture's {@link Robot}.
   *
   * @return {@code true} if only showing components can participate in a component lookup, {@code false} otherwise.
   */
  protected boolean requireShowing() {
    return requireShowing(robot());
  }

  private static boolean requireShowing(@Nonnull Robot robot) {
    return robot.settings().componentLookupScope().requireShowing();
  }

  /**
   * @return a fixture that checks properties of the font of this fixture's {@code Component}.
   */
  public final @Nonnull FontFixture font() {
    Font font = driver.fontOf(target);
    return new FontFixture(font, propertyName(target(), FONT_PROPERTY));
  }

  /**
   * @return a fixture that checks properties of the background color of this fixture's {@code Component}.
   */
  public final @Nonnull ColorFixture background() {
    Color background = driver.backgroundOf(target);
    return new ColorFixture(background, propertyName(target(), BACKGROUND_PROPERTY));
  }

  /**
   * @return a fixture that checks properties of the foreground color of this fixture's {@code Component}.
   */
  public final @Nonnull ColorFixture foreground() {
    Color foreground = driver.foregroundOf(target);
    return new ColorFixture(foreground, propertyName(target(), FOREGROUND_PROPERTY));
  }

  /**
   * Indicates whether this fixture's {@code Component} is enabled.
   *
   * @return {@code true} if this fixture's {@code Component} is enabled; {@code false} otherwise.
   */
  public final boolean isEnabled() {
    return ComponentEnabledQuery.isEnabled(target());
  }

  /**
   * Returns this fixture's {@code Component} casted to the given sub-type.
   *
   * @param  the type that the managed {@code Component} will be casted to.
   * @param type the class for the type of component.
   * @return this fixture's {@code Component} casted to the given sub-type.
   * @throws AssertionError if this fixture's {@code Component} is not an instance of the given type.
   */
  public final @Nonnull  T targetCastedTo(@Nonnull Class type) {
    assertThat(target).as(format(target)).isInstanceOf(type);
    return type.cast(target);
  }

  /**
   * 

* Returns the GUI component in this fixture. *

*

* Note: Access to the GUI component returned by this method must be executed in the event * dispatch thread (EDT). To do so, please execute a {@link org.assertj.swing.edt.GuiQuery GuiQuery} or * {@link org.assertj.swing.edt.GuiTask GuiTask} (depending on what you need to do), inside a * {@link org.assertj.swing.edt.GuiActionRunner}. To learn more about Swing threading, please read the Swing * Threading Policy. *

* * @return the GUI component in this fixture. */ public final @Nonnull C target() { return target; } /** @return the {@link Robot} that simulates user events on {@link #target()}. */ protected final @Nonnull Robot robot() { return robot; } /** * @return {@code this} casted to the "self type". */ protected final @Nonnull S myself() { return myself; } }