001    /*
002     * Created on Jan 27, 2008
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with 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
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     *
014     * Copyright @2008-2010 the original author or authors.
015     */
016    package org.fest.swing.driver;
017    
018    import static org.fest.assertions.Assertions.assertThat;
019    import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing;
020    import static org.fest.swing.driver.JTabbedPaneSelectTabTask.setSelectedTab;
021    import static org.fest.swing.driver.JTabbedPaneTabTitlesQuery.tabTitlesOf;
022    import static org.fest.swing.driver.TextAssert.verifyThat;
023    import static org.fest.swing.edt.GuiActionRunner.execute;
024    
025    import java.awt.Component;
026    import java.awt.Point;
027    import java.util.ArrayList;
028    import java.util.List;
029    import java.util.regex.Pattern;
030    
031    import javax.swing.JTabbedPane;
032    
033    import org.fest.assertions.Description;
034    import org.fest.swing.annotation.RunsInEDT;
035    import org.fest.swing.core.Robot;
036    import org.fest.swing.data.Index;
037    import org.fest.swing.edt.GuiQuery;
038    import org.fest.swing.exception.ActionFailedException;
039    import org.fest.swing.exception.LocationUnavailableException;
040    import org.fest.swing.util.*;
041    import org.fest.util.VisibleForTesting;
042    
043    /**
044     * Understands functional testing of <code>{@link JTabbedPane}</code>s:
045     * <ul>
046     * <li>user input simulation</li>
047     * <li>state verification</li>
048     * <li>property value query</li>
049     * </ul>
050     * This class is intended for internal use only. Please use the classes in the package
051     * <code>{@link org.fest.swing.fixture}</code> in your tests.
052     *
053     * @author Alex Ruiz
054     * @author Yvonne Wang
055     */
056    public class JTabbedPaneDriver extends JComponentDriver {
057    
058      private final JTabbedPaneLocation location;
059    
060      /**
061       * Creates a new </code>{@link JTabbedPaneDriver}</code>.
062       * @param robot the robot to use to simulate user input.
063       */
064      public JTabbedPaneDriver(Robot robot) {
065        this(robot, new JTabbedPaneLocation());
066      }
067    
068      /**
069       * Creates a new </code>{@link JTabbedPaneDriver}</code>.
070       * @param robot the robot to use to simulate user input.
071       * @param location knows how to find the location of a tab.
072       */
073      @VisibleForTesting
074      JTabbedPaneDriver(Robot robot, JTabbedPaneLocation location) {
075        super(robot);
076        this.location = location;
077      }
078    
079      /**
080       * Returns the titles of all the tabs.
081       * @param tabbedPane the target <code>JTabbedPane</code>.
082       * @return the titles of all the tabs.
083       */
084      @RunsInEDT
085      public String[] tabTitles(JTabbedPane tabbedPane) {
086        return tabTitlesOf(tabbedPane);
087      }
088    
089      /**
090       * Simulates a user selecting the tab containing the given title.
091       * @param tabbedPane the target <code>JTabbedPane</code>.
092       * @param title the given text to match. It can be a regular expression.
093       * @throws IllegalStateException if the <code>JTabbedPane</code> is disabled.
094       * @throws IllegalStateException if the <code>JTabbedPane</code> is not showing on the screen.
095       * @throws LocationUnavailableException if a tab matching the given title could not be found.
096       */
097      @RunsInEDT
098      public void selectTab(JTabbedPane tabbedPane, String title) {
099        selectTab(tabbedPane, new StringTextMatcher(title));
100      }
101    
102    
103      /**
104       * Simulates a user selecting the tab whose title matches the given regular expression pattern.
105       * @param tabbedPane the target <code>JTabbedPane</code>.
106       * @param pattern the regular expression pattern to match.
107       * @throws IllegalStateException if the <code>JTabbedPane</code> is disabled.
108       * @throws IllegalStateException if the <code>JTabbedPane</code> is not showing on the screen.
109       * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
110       * @throws LocationUnavailableException if a tab matching the given regular expression pattern could not be found.
111       * @since 1.2
112       */
113      @RunsInEDT
114      public void selectTab(JTabbedPane tabbedPane, Pattern pattern) {
115        selectTab(tabbedPane, new PatternTextMatcher(pattern));
116      }
117    
118      @RunsInEDT
119      private void selectTab(JTabbedPane tabbedPane, TextMatcher matcher) {
120        Pair<Integer, Point> tabToSelectInfo = tabToSelectInfo(location, tabbedPane, matcher);
121        Point target = tabToSelectInfo.ii;
122        if (target != null) {
123          click(tabbedPane, target);
124          return;
125        }
126        setTabDirectly(tabbedPane, tabToSelectInfo.i);
127      }
128    
129      @RunsInEDT
130      private static Pair<Integer, Point> tabToSelectInfo(final JTabbedPaneLocation location,
131          final JTabbedPane tabbedPane, final TextMatcher matcher) {
132        return execute(new GuiQuery<Pair<Integer, Point>>() {
133          protected Pair<Integer, Point> executeInEDT() {
134            validateIsEnabledAndShowing(tabbedPane);
135            int index = location.indexOf(tabbedPane, matcher);
136            location.validateIndex(tabbedPane, index);
137            Point point = null;
138            try {
139              point = location.pointAt(tabbedPane, index);
140            } catch (LocationUnavailableException e) {}
141            return new Pair<Integer, Point>(index, point);
142          }
143        });
144      }
145    
146      /**
147       * Simulates a user selecting the tab located at the given index.
148       * @param tabbedPane the target <code>JTabbedPane</code>.
149       * @param index the index of the tab to select.
150       * @throws IllegalStateException if the <code>JTabbedPane</code> is disabled.
151       * @throws IllegalStateException if the <code>JTabbedPane</code> is not showing on the screen.
152       * @throws IndexOutOfBoundsException if the given index is not within the <code>JTabbedPane</code> bounds.
153       */
154      public void selectTab(JTabbedPane tabbedPane, int index) {
155        try {
156          Point p = validateAndGetPoint(location, tabbedPane, index);
157          click(tabbedPane, p);
158        } catch (LocationUnavailableException e) {
159          setTabDirectly(tabbedPane, index);
160        } catch (ActionFailedException e) {
161          setTabDirectly(tabbedPane, index);
162        }
163      }
164    
165      @RunsInEDT
166      private static Point validateAndGetPoint(final JTabbedPaneLocation location, final JTabbedPane tabbedPane, final int index) {
167        return execute(new GuiQuery<Point>() {
168          protected Point executeInEDT() {
169            location.validateIndex(tabbedPane, index);
170            validateIsEnabledAndShowing(tabbedPane);
171            return location.pointAt(tabbedPane, index);
172          }
173        });
174      }
175    
176      @VisibleForTesting
177      @RunsInEDT
178      void setTabDirectly(JTabbedPane tabbedPane, int index) {
179        setSelectedTab(tabbedPane, index);
180        robot.waitForIdle();
181        moveMouseToTab(tabbedPane, index);
182      }
183    
184      private void moveMouseToTab(JTabbedPane tabbedPane, int index) {
185        try {
186          Point p = pointAtTab(location, tabbedPane, index);
187          robot.moveMouse(tabbedPane, p);
188          robot.waitForIdle();
189        } catch (LocationUnavailableException ignored) {}
190      }
191    
192      @RunsInEDT
193      private static Point pointAtTab(final JTabbedPaneLocation location, final JTabbedPane tabbedPane, final int index) {
194        return execute(new GuiQuery<Point>() {
195          protected Point executeInEDT() {
196            return location.pointAt(tabbedPane, index);
197          }
198        });
199      }
200    
201      /**
202       * Returns the currently selected component for the given <code>{@link JTabbedPane}</code>.
203       * @param tabbedPane the target <code>JTabbedPane</code>.
204       * @return the currently selected component for the given <code>JTabbedPane</code>.
205       */
206      @RunsInEDT
207      public Component selectedComponentOf(JTabbedPane tabbedPane) {
208        return selectedComponent(tabbedPane);
209      }
210    
211      @RunsInEDT
212      private static Component selectedComponent(final JTabbedPane tabbedPane) {
213        return execute(new GuiQuery<Component>() {
214          protected Component executeInEDT() {
215            return tabbedPane.getSelectedComponent();
216          }
217        });
218      }
219    
220      /**
221       * Asserts that the title of the tab at the given index matches the given value.
222       * @param tabbedPane the target <code>JTabbedPane</code>.
223       * @param title the expected title. It can be a regular expression.
224       * @param index the index of the tab.
225       * @throws IndexOutOfBoundsException if the given index is not within the <code>JTabbedPane</code> bounds.
226       * @throws AssertionError if the title of the tab at the given index does not match the given one.
227       */
228      @RunsInEDT
229      public void requireTabTitle(JTabbedPane tabbedPane, String title, Index index) {
230        String actualTitle = titleAt(tabbedPane, index);
231        verifyThat(actualTitle).as(titleAtProperty(tabbedPane)).isEqualOrMatches(title);
232      }
233    
234      /**
235       * Asserts that the title of the tab at the given index matches the given regular expression pattern.
236       * @param tabbedPane the target <code>JTabbedPane</code>.
237       * @param pattern the regular expression pattern to match.
238       * @param index the index of the tab.
239       * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
240       * @throws IndexOutOfBoundsException if the given index is not within the <code>JTabbedPane</code> bounds.
241       * @throws AssertionError if the title of the tab at the given index does not match the given one.
242       * @since 1.2
243       */
244      @RunsInEDT
245      public void requireTabTitle(JTabbedPane tabbedPane, Pattern pattern, Index index) {
246        String actualTitle = titleAt(tabbedPane, index);
247        verifyThat(actualTitle).as(titleAtProperty(tabbedPane)).matches(pattern);
248      }
249    
250      @RunsInEDT
251      private Description titleAtProperty(JTabbedPane tabbedPane) {
252        return propertyName(tabbedPane, "titleAt");
253      }
254    
255      @RunsInEDT
256      private static String titleAt(final JTabbedPane tabbedPane, final Index index) {
257        return execute(new GuiQuery<String>() {
258          protected String executeInEDT() {
259            return tabbedPane.getTitleAt(index.value);
260          }
261        });
262      }
263    
264      /**
265       * Asserts that the tabs of the given <code>{@link JTabbedPane}</code> have the given titles. The tab titles are
266       * evaluated by index order, for example, the first tab is expected to have the first title in the given array, and so
267       * on.
268       * @param tabbedPane the target <code>JTabbedPane</code>.
269       * @param titles the expected titles.
270       * @throws AssertionError if the title of any of the tabs is not equal to the expected titles.
271       */
272      @RunsInEDT
273      public void requireTabTitles(JTabbedPane tabbedPane, String[] titles) {
274        String[] actualTitles = allTabTitlesIn(tabbedPane);
275        assertThat(actualTitles).as(propertyName(tabbedPane, "tabTitles")).isEqualTo(titles);
276      }
277    
278      @RunsInEDT
279      private static String[] allTabTitlesIn(final JTabbedPane tabbedPane) {
280        return execute(new GuiQuery<String[]>() {
281          protected String[] executeInEDT() {
282            List<String> allTitles = new ArrayList<String>();
283            int tabCount = tabbedPane.getTabCount();
284            for (int i = 0; i < tabCount; i++)
285              allTitles.add(tabbedPane.getTitleAt(i));
286            return allTitles.toArray(new String[allTitles.size()]);
287          }
288        });
289      }
290    }