Package org.fest.swing.driver

Source Code of org.fest.swing.driver.JComboBoxDriver

/*
* Created on Jan 21, 2008
*
* 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 @2008-2013 the original author or authors.
*/
package org.fest.swing.driver;

import static javax.swing.text.DefaultEditorKit.selectAllAction;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.fest.swing.driver.ComponentPreconditions.checkEnabledAndShowing;
import static org.fest.swing.driver.JComboBoxContentQuery.contents;
import static org.fest.swing.driver.JComboBoxEditableQuery.isEditable;
import static org.fest.swing.driver.JComboBoxItemCountQuery.itemCountIn;
import static org.fest.swing.driver.JComboBoxItemIndexPreconditions.checkItemIndexInBounds;
import static org.fest.swing.driver.JComboBoxMatchingItemQuery.matchingItemIndex;
import static org.fest.swing.driver.JComboBoxSelectedIndexQuery.selectedIndexOf;
import static org.fest.swing.driver.JComboBoxSelectionValueQuery.selection;
import static org.fest.swing.driver.JComboBoxSetSelectedIndexTask.setSelectedIndex;
import static org.fest.swing.driver.TextAssert.verifyThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.exception.ActionFailedException.actionFailure;
import static org.fest.swing.format.Formatting.format;
import static org.fest.util.Arrays.format;
import static org.fest.util.Preconditions.checkNotNull;
import static org.fest.util.Strings.quote;

import java.awt.Component;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.ComboBoxEditor;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;

import org.fest.assertions.Description;
import org.fest.swing.annotation.RunsInCurrentThread;
import org.fest.swing.annotation.RunsInEDT;
import org.fest.swing.cell.JComboBoxCellReader;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.exception.ActionFailedException;
import org.fest.swing.exception.ComponentLookupException;
import org.fest.swing.exception.LocationUnavailableException;
import org.fest.swing.util.Pair;
import org.fest.swing.util.PatternTextMatcher;
import org.fest.swing.util.StringTextMatcher;
import org.fest.swing.util.TextMatcher;
import org.fest.util.InternalApi;
import org.fest.util.VisibleForTesting;

/**
* <p>
* Supports functional testing of {@code JComboBox}es.
* </p>
*
* <p>
* <b>Note:</b> This class is intended for internal use only. Please use the classes in the package
* {@link org.fest.swing.fixture} in your tests.
* </p>
*
* @author Alex Ruiz
* @author Yvonne Wang
*/
@InternalApi
public class JComboBoxDriver extends JComponentDriver {
  private static final String EDITABLE_PROPERTY = "editable";
  private static final String SELECTED_INDEX_PROPERTY = "selectedIndex";

  private final JListDriver listDriver;
  private final JComboBoxDropDownListFinder dropDownListFinder;

  private JComboBoxCellReader cellReader;

  /**
   * Creates a new {@link JComboBoxDriver}.
   *
   * @param robot the robot to use to simulate user input.
   */
  public JComboBoxDriver(@Nonnull Robot robot) {
    super(robot);
    listDriver = new JListDriver(robot);
    dropDownListFinder = new JComboBoxDropDownListFinder(robot);
    replaceCellReader(new BasicJComboBoxCellReader());
  }

  /**
   * Returns an array of {@code String}s that represents the contents of the given {@code JComboBox} list. The
   * {@code String} representation of each element is performed using this driver's {@link JComboBoxCellReader}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @return an array of {@code String}s that represent the contents of the given {@code JComboBox} list.
   * @see #value(JComboBox, int)
   * @see #replaceCellReader(JComboBoxCellReader)
   */
  @RunsInEDT
  public @Nonnull String[] contentsOf(@Nonnull JComboBox comboBox) {
    return contents(comboBox, cellReader());
  }

  /**
   * Selects the first item matching the given text in the {@code JComboBox}. The text of the {@code JComboBox} items is
   * obtained by this fixture's {@link JComboBoxCellReader}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param value the value to match. It can be a regular expression.
   * @throws LocationUnavailableException if an element matching the given value cannot be found.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @see #replaceCellReader(JComboBoxCellReader)
   */
  @RunsInEDT
  public void selectItem(@Nonnull JComboBox comboBox, @Nullable String value) {
    selectItem(comboBox, new StringTextMatcher(value));
  }

  /**
   * Selects the first item matching the given regular expression pattern in the {@code JComboBox}. The text of the
   * {@code JComboBox} items is obtained by this fixture's {@link JComboBoxCellReader}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param pattern the regular expression pattern to match.
   * @throws LocationUnavailableException if an element matching the given pattern cannot be found.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @throws NullPointerException if the given regular expression pattern is {@code null}.
   * @see #replaceCellReader(JComboBoxCellReader)
   * @since 1.2
   */
  @RunsInEDT
  public void selectItem(@Nonnull JComboBox comboBox, @Nonnull Pattern pattern) {
    selectItem(comboBox, new PatternTextMatcher(pattern));
  }

  @RunsInEDT
  private void selectItem(@Nonnull JComboBox comboBox, @Nonnull TextMatcher matcher) {
    int index = matchingItemIndex(comboBox, matcher, cellReader());
    if (index < 0) {
      String format = "Unable to find item matching %s among the JComboBox contents: ";
      String msg = String.format(format, matcher.description(), format(contentsOf(comboBox)));
      throw new LocationUnavailableException(msg);
    }
    selectItem(comboBox, index);
  }

  /**
   * Verifies that the {@code String} representation of the selected item in the {@code JComboBox} matches the given
   * text.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param value the text to match. It can be a regular expression.
   * @throws AssertionError if the selected item does not match the given value.
   * @see #replaceCellReader(JComboBoxCellReader)
   */
  @RunsInEDT
  public void requireSelection(@Nonnull JComboBox comboBox, @Nullable String value) {
    String selection = requiredSelectionOf(comboBox);
    verifyThat(selection).as(selectedIndexProperty(comboBox)).isEqualOrMatches(value);
  }

  /**
   * Verifies that the {@code String} representation of the selected item in the {@code JComboBox} matches the given
   * regular expression pattern.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param pattern the regular expression pattern to match.
   * @throws AssertionError if the selected item does not match the given regular expression pattern.
   * @throws NullPointerException if the given regular expression pattern is {@code null}.
   * @see #replaceCellReader(JComboBoxCellReader)
   * @since 1.2
   */
  @RunsInEDT
  public void requireSelection(@Nonnull JComboBox comboBox, @Nonnull Pattern pattern) {
    String selection = requiredSelectionOf(comboBox);
    verifyThat(selection).as(selectedIndexProperty(comboBox)).matches(pattern);
  }

  @RunsInEDT
  private @Nullable String requiredSelectionOf(@Nonnull JComboBox comboBox) throws AssertionError {
    Pair<Boolean, String> selection = selection(comboBox, cellReader());
    boolean hasSelection = selection.first;
    if (!hasSelection) {
      throw failNoSelection(comboBox);
    }
    return selection.second;
  }

  /**
   * Verifies that the index of the selected item in the {@code JComboBox} is equal to the given value.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param index the expected selection index.
   * @throws AssertionError if the selection index is not equal to the given value.
   * @since 1.2
   */
  @RunsInEDT
  public void requireSelection(@Nonnull JComboBox comboBox, int index) {
    int selectedIndex = selectedIndexOf(comboBox);
    if (selectedIndex == -1) {
      failNoSelection(comboBox);
    }
    assertThat(selectedIndex).as(selectedIndexProperty(comboBox)).isEqualTo(index);
  }

  private @Nonnull AssertionError failNoSelection(@Nonnull JComboBox comboBox) {
    throw fail(String.format("[%s] No selection", selectedIndexProperty(comboBox).value()));
  }

  /**
   * Verifies that the {@code JComboBox} does not have any selection.
   *
   * @param comboBox the target {@code JComboBox}.
   * @throws AssertionError if the {@code JComboBox} has a selection.
   */
  @RunsInEDT
  public void requireNoSelection(@Nonnull JComboBox comboBox) {
    Pair<Boolean, String> selection = selection(comboBox, cellReader());
    boolean hasSelection = selection.first;
    if (!hasSelection) {
      return;
    }
    String format = "[%s] Expecting no selection, but found:<%s>";
    fail(String.format(format, selectedIndexProperty(comboBox).value(), quote(selection.second)));
  }

  /**
   * Returns the {@code String} representation of the element under the given index, using this driver's
   * {@link JComboBoxCellReader}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param index the given index.
   * @return the value of the element under the given index.
   * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
   *           {@code JComboBox}.
   * @see #replaceCellReader(JComboBoxCellReader)
   */
  public @Nullable String value(@Nonnull JComboBox comboBox, int index) {
    return valueAsText(comboBox, index, cellReader());
  }

  @RunsInEDT
  private static @Nullable String valueAsText(final @Nonnull JComboBox comboBox, final int index,
      final @Nonnull JComboBoxCellReader cellReader) {
    return execute(new GuiQuery<String>() {
      @Override
      protected @Nullable String executeInEDT() {
        checkItemIndexInBounds(comboBox, index);
        return cellReader.valueAt(comboBox, index);
      }
    });
  }

  private @Nonnull Description selectedIndexProperty(@Nonnull JComboBox comboBox) {
    return propertyName(comboBox, SELECTED_INDEX_PROPERTY);
  }

  /**
   * Clears the selection in the given {@code JComboBox}. Since this method does not simulate user input, it does not
   * verifies that the {@code JComboBox} is enabled and showing.
   *
   * @param comboBox the target {@code JComboBox}.
   * @since 1.2
   */
  public void clearSelection(@Nonnull JComboBox comboBox) {
    setSelectedIndex(comboBox, -1);
    robot.waitForIdle();
  }

  /**
   * Selects the item under the given index in the {@code JComboBox}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param index the given index.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @throws IndexOutOfBoundsException if the given index is negative or greater than the index of the last item in the
   *           {@code JComboBox}.
   */
  @RunsInEDT
  public void selectItem(final @Nonnull JComboBox comboBox, int index) {
    validateCanSelectItem(comboBox, index);
    showDropDownList(comboBox);
    selectItemAtIndex(comboBox, index);
    hideDropDownListIfVisible(comboBox);
  }

  @RunsInEDT
  private static void validateCanSelectItem(final @Nonnull JComboBox comboBox, final int index) {
    execute(new GuiTask() {
      @Override
      protected void executeInEDT() {
        checkEnabledAndShowing(comboBox);
        checkItemIndexInBounds(comboBox, index);
      }
    });
  }

  @VisibleForTesting
  @RunsInEDT
  void showDropDownList(@Nonnull JComboBox comboBox) {
    // Location of pop-up button activator is LAF-dependent
    dropDownVisibleThroughUIDelegate(comboBox, true);
  }

  @RunsInEDT
  private void selectItemAtIndex(@Nonnull final JComboBox comboBox, final int index) {
    JList dropDownList = dropDownListFinder.findDropDownList();
    if (dropDownList != null) {
      listDriver.selectItem(dropDownList, index);
      return;
    }
    setSelectedIndex(comboBox, index);
    robot.waitForIdle();
  }

  @RunsInEDT
  private void hideDropDownListIfVisible(@Nonnull JComboBox comboBox) {
    dropDownVisibleThroughUIDelegate(comboBox, false);
  }

  @RunsInEDT
  private void dropDownVisibleThroughUIDelegate(@Nonnull final JComboBox comboBox, final boolean visible) {
    execute(new GuiTask() {
      @Override protected void executeInEDT() {
        comboBox.setPopupVisible(visible);
      }
    });
    robot.waitForIdle();
  }

  /**
   * Simulates a user entering the specified text in the {@code JComboBox}, replacing any text. This action is executed
   * only if the {@code JComboBox} is editable.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param text the text to enter.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @throws IllegalStateException if the {@code JComboBox} is not editable.
   */
  @RunsInEDT
  public void replaceText(@Nonnull JComboBox comboBox, @Nonnull String text) {
    selectAllText(comboBox);
    enterText(comboBox, text);
  }

  /**
   * Simulates a user selecting the text in the {@code JComboBox}. This action is executed only if the {@code JComboBox}
   * is editable.
   *
   * @param comboBox the target {@code JComboBox}.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @throws IllegalStateException if the {@code JComboBox} is not editable.
   */
  @RunsInEDT
  public void selectAllText(@Nonnull JComboBox comboBox) {
    Component editor = accessibleEditorOf(comboBox);
    if (!(editor instanceof JComponent)) {
      return;
    }
    focus(editor);
    invokeAction((JComponent) editor, selectAllAction);
  }

  @RunsInEDT
  private static @Nullable Component accessibleEditorOf(final @Nonnull JComboBox comboBox) {
    return execute(new GuiQuery<Component>() {
      @Override
      protected @Nullable Component executeInEDT() {
        checkAccessibleEditor(comboBox);
        return editorComponentOf(comboBox);
      }
    });
  }

  /**
   * Simulates a user entering the specified text in the {@code JComboBox}. This action is executed only if the
   * {@code JComboBox} is editable.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param text the text to enter.
   * @throws IllegalStateException if the {@code JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} is not showing on the screen.
   * @throws IllegalStateException if the {@code JComboBox} is not editable.
   * @throws ActionFailedException if the {@code JComboBox} does not have an editor.
   */
  @RunsInEDT
  public void enterText(final @Nonnull JComboBox comboBox, @Nonnull String text) {
    execute(new GuiTask() {
      @Override
      protected void executeInEDT() {
        checkAccessibleEditor(comboBox);
      }
    });
    Component editor = editorComponentOf(comboBox);
    // this will never happen...at least in Sun's JVM
    if (editor == null) {
      throw actionFailure("JComboBox does not have an editor");
    }
    focusAndWaitForFocusGain(editor);
    robot.enterText(text);
  }

  /**
   * Simulates a user pressing and releasing the given keys on the {@code JComboBox}.
   *
   * @param comboBox the target {@code JComboBox}.
   * @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 JComboBox} is disabled.
   * @throws IllegalStateException if the {@code JComboBox} 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 JComboBox comboBox, @Nonnull int... keyCodes) {
    checkNotNull(keyCodes);
    checkInEdtEnabledAndShowing(comboBox);
    Component target = editorIfEditable(comboBox);
    if (target == null) {
      target = comboBox;
    }
    focusAndWaitForFocusGain(target);
    robot.pressAndReleaseKeys(keyCodes);
  }

  @RunsInEDT
  private static Component editorIfEditable(final JComboBox comboBox) {
    return execute(new GuiQuery<Component>() {
      @Override
      protected Component executeInEDT() {
        if (!comboBox.isEditable()) {
          return null;
        }
        return editorComponent(comboBox);
      }
    });
  }

  @RunsInCurrentThread
  private static void checkAccessibleEditor(@Nonnull JComboBox comboBox) {
    checkEnabledAndShowing(comboBox);
    if (!comboBox.isEditable()) {
      String msg = String.format("Expecting component %s to be editable", format(comboBox));
      throw new IllegalStateException(msg);
    }
  }

  @RunsInEDT
  private static @Nullable Component editorComponentOf(final @Nonnull JComboBox comboBox) {
    return execute(new GuiQuery<Component>() {
      @Override
      protected @Nullable Component executeInEDT() {
        return editorComponent(comboBox);
      }
    });
  }

  @RunsInCurrentThread
  private static @Nullable Component editorComponent(@Nonnull JComboBox comboBox) {
    ComboBoxEditor editor = comboBox.getEditor();
    if (editor == null) {
      return null;
    }
    return editor.getEditorComponent();
  }

  /**
   * Find the {@code JList} in the pop-up raised by the {@code JComboBox}, if the LAF actually uses one.
   *
   * @return the found {@code JList}.
   * @throws ComponentLookupException if the {@code JList} in the pop-up could not be found.
   */
  @RunsInEDT
  public @Nonnull JList dropDownList() {
    JList list = dropDownListFinder.findDropDownList();
    if (list == null) {
      throw new ComponentLookupException("Unable to find the pop-up list for the JComboBox");
    }
    return list;
  }

  /**
   * Asserts that the given {@code JComboBox} is editable.
   *
   * @param comboBox the target {@code JComboBox}.
   * @throws AssertionError if the {@code JComboBox} is not editable.
   */
  @RunsInEDT
  public void requireEditable(final @Nonnull JComboBox comboBox) {
    checkEditable(comboBox, true);
  }

  /**
   * Asserts that the given {@code JComboBox} is not editable.
   *
   * @param comboBox the given {@code JComboBox}.
   * @throws AssertionError if the {@code JComboBox} is editable.
   */
  @RunsInEDT
  public void requireNotEditable(@Nonnull JComboBox comboBox) {
    checkEditable(comboBox, false);
  }

  @RunsInEDT
  private void checkEditable(@Nonnull JComboBox comboBox, boolean expected) {
    assertThat(isEditable(comboBox)).as(editableProperty(comboBox)).isEqualTo(expected);
  }

  @RunsInEDT
  private static Description editableProperty(@Nonnull JComboBox comboBox) {
    return propertyName(comboBox, EDITABLE_PROPERTY);
  }

  /**
   * Updates the implementation of {@link JComboBoxCellReader} to use when comparing internal values of a
   * {@code JComboBox} and the values expected in a test.
   *
   * @param newCellReader the new {@code JComboBoxCellValueReader} to use.
   * @throws NullPointerException if {@code newCellReader} is {@code null}.
   */
  public void replaceCellReader(@Nonnull JComboBoxCellReader newCellReader) {
    cellReader = checkNotNull(newCellReader);
  }

  /**
   * Verifies that number of items in the given {@code JComboBox} is equal to the expected one.
   *
   * @param comboBox the target {@code JComboBox}.
   * @param expected the expected number of items.
   * @throws AssertionError if the number of items in the given {@code JComboBox} is not equal to the expected one.
   * @since 1.2
   */
  @RunsInEDT
  public void requireItemCount(@Nonnull JComboBox comboBox, int expected) {
    int actual = itemCountIn(comboBox);
    assertThat(actual).as(propertyName(comboBox, "itemCount")).isEqualTo(expected);
  }

  /**
   * Returns the selected value of the given {@code JComboBox} as plain text. This method returns {@code null} if the
   * {code JComboBox} does not have any selection.
   *
   * @param comboBox the target {@code JComboBox}.
   * @return the selected value of the given {code JComboBox} as plain text, or {@code null} if the {code JComboBox}
   *         does not have any selection.
   * @since 1.3
   */
  public @Nullable String selectedItemOf(@Nonnull JComboBox comboBox) {
    return selection(comboBox, cellReader()).second;
  }

  private @Nonnull JComboBoxCellReader cellReader() {
    return cellReader;
  }
}
TOP

Related Classes of org.fest.swing.driver.JComboBoxDriver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.