org.assertj.swing.driver.JComboBoxDriver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of assertj-swing Show documentation
Show all versions of assertj-swing Show documentation
Fluent interface for functional GUI testing
/*
* 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.assertj.swing.driver;
import static javax.swing.text.DefaultEditorKit.selectAllAction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.fail;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.core.util.Strings.quote;
import static org.assertj.swing.driver.ComponentPreconditions.checkEnabledAndShowing;
import static org.assertj.swing.driver.JComboBoxContentQuery.contents;
import static org.assertj.swing.driver.JComboBoxEditableQuery.isEditable;
import static org.assertj.swing.driver.JComboBoxItemCountQuery.itemCountIn;
import static org.assertj.swing.driver.JComboBoxItemIndexPreconditions.checkItemIndexInBounds;
import static org.assertj.swing.driver.JComboBoxMatchingItemQuery.matchingItemIndex;
import static org.assertj.swing.driver.JComboBoxSelectedIndexQuery.selectedIndexOf;
import static org.assertj.swing.driver.JComboBoxSelectionValueQuery.selection;
import static org.assertj.swing.driver.JComboBoxSetSelectedIndexTask.setSelectedIndex;
import static org.assertj.swing.driver.TextAssert.verifyThat;
import static org.assertj.swing.edt.GuiActionRunner.execute;
import static org.assertj.swing.exception.ActionFailedException.actionFailure;
import static org.assertj.swing.format.Formatting.format;
import static org.assertj.swing.util.Arrays.format;
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.assertj.core.description.Description;
import org.assertj.core.util.VisibleForTesting;
import org.assertj.swing.annotation.RunsInCurrentThread;
import org.assertj.swing.annotation.RunsInEDT;
import org.assertj.swing.cell.JComboBoxCellReader;
import org.assertj.swing.core.Robot;
import org.assertj.swing.edt.GuiQuery;
import org.assertj.swing.edt.GuiTask;
import org.assertj.swing.exception.ActionFailedException;
import org.assertj.swing.exception.ComponentLookupException;
import org.assertj.swing.exception.LocationUnavailableException;
import org.assertj.swing.internal.annotation.InternalApi;
import org.assertj.swing.util.Pair;
import org.assertj.swing.util.PatternTextMatcher;
import org.assertj.swing.util.StringTextMatcher;
import org.assertj.swing.util.TextMatcher;
/**
*
* Supports functional testing of {@code JComboBox}es.
*
*
*
* 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
* @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)
*/
@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)
*/
@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 selection = selection(comboBox, cellReader());
boolean hasSelection = selection.first;
if (!hasSelection) {
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.
*/
@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 void failNoSelection(@Nonnull JComboBox comboBox) {
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 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() {
@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}.
*/
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() {
@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() {
@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() {
@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.
*/
@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.
*/
public @Nullable String selectedItemOf(@Nonnull JComboBox comboBox) {
return selection(comboBox, cellReader()).second;
}
private @Nonnull JComboBoxCellReader cellReader() {
return cellReader;
}
}