001    /*
002     * Created on Oct 20, 2006
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 @2006-2010 the original author or authors.
015     */
016    package org.fest.swing.fixture;
017    
018    import static org.fest.assertions.Assertions.assertThat;
019    import static org.fest.swing.driver.ComponentDriver.propertyName;
020    import static org.fest.swing.fixture.ComponentFixtureValidator.notNullRobot;
021    import static org.fest.swing.fixture.ComponentFixtureValidator.notNullTarget;
022    import static org.fest.swing.format.Formatting.format;
023    import static org.fest.swing.query.ComponentBackgroundQuery.backgroundOf;
024    import static org.fest.swing.query.ComponentFontQuery.fontOf;
025    import static org.fest.swing.query.ComponentForegroundQuery.foregroundOf;
026    
027    import java.awt.Component;
028    
029    import org.fest.swing.core.Robot;
030    import org.fest.swing.core.Settings;
031    import org.fest.swing.driver.ComponentDriver;
032    import org.fest.swing.edt.GuiActionRunner;
033    import org.fest.swing.exception.ComponentLookupException;
034    
035    /**
036     * Understands functional testing of <code>{@link Component}</code>s:
037     * <ul>
038     * <li>user input simulation</li>
039     * <li>state verification</li>
040     * <li>property value query</li>
041     * </ul>
042     *
043     * @param <T> the type of <code>Component</code> that this fixture can manage.
044     *
045     * @author Alex Ruiz
046     * @author Yvonne Wang
047     */
048    public abstract class ComponentFixture<T extends Component> {
049    
050      /** Name of the property "font". */
051      protected static final String FONT_PROPERTY = "font";
052    
053      /** Name of the property "background". */
054      protected static final String BACKGROUND_PROPERTY = "background";
055    
056      /** Name of the property "foreground". */
057      protected static final String FOREGROUND_PROPERTY = "foreground";
058    
059      /** Performs simulation of user events on <code>{@link #target}</code> */
060      public final Robot robot;
061    
062      /**
063       * This fixture's <code>{@link Component}</code>.
064       * <p>
065       * <strong>Note:</strong> Access to this GUI component <em>must</em> be executed in the event dispatch thread. To do
066       * so, please execute a <code>{@link org.fest.swing.edt.GuiQuery GuiQuery}</code> or
067       * <code>{@link org.fest.swing.edt.GuiTask GuiTask}</code> (depending on what you need to do,) inside a
068       * <code>{@link GuiActionRunner}</code>. To learn more about Swing threading, please read the
069       * <a href="http://java.sun.com/javase/6/docs/api/javax/swing/package-summary.html#threading" target="_blank">Swing Threading Policy</a>.
070       * </p>
071       */
072      public final T target;
073    
074      /**
075       * Creates a new <code>{@link ComponentFixture}</code>.
076       * @param robot performs simulation of user events on a <code>Component</code>.
077       * @param type the type of the <code>Component</code> to find using the given <code>RobotFixture</code>.
078       * @throws NullPointerException if <code>robot</code> is <code>null</code>.
079       * @throws NullPointerException if <code>type</code> is <code>null</code>.
080       * @throws ComponentLookupException if a matching component could not be found.
081       * @throws ComponentLookupException if more than one matching component is found.
082       */
083      public ComponentFixture(Robot robot, Class<? extends T> type) {
084        this(robot, findTarget(robot, type));
085      }
086    
087      private static <C extends Component> C findTarget(Robot robot, Class<? extends C> type) {
088        validate(robot, type);
089        return robot.finder().findByType(type, requireShowing(robot));
090      }
091    
092      /**
093       * Validates that the given <code>{@link ComponentDriver}</code> is not <code>null</code>.
094       * @param driver the {@code ComponentDriver} to validate.
095       * @throws NullPointerException if {@code driver} is <code>null</code>.
096       */
097      protected static void validateNotNull(ComponentDriver driver) {
098        if (driver == null) throw new NullPointerException("The driver should not be null");
099      }
100    
101      /**
102       * Creates a new <code>{@link ComponentFixture}</code>.
103       * @param robot performs simulation of user events on a <code>Component</code>.
104       * @param name the name of the <code>Component</code> to find using the given <code>RobotFixture</code>.
105       * @param type the type of the <code>Component</code> to find using the given <code>RobotFixture</code>.
106       * @throws NullPointerException if <code>robot</code> is <code>null</code>.
107       * @throws NullPointerException if <code>type</code> is <code>null</code>.
108       * @throws ComponentLookupException if a matching component could not be found.
109       * @throws ComponentLookupException if more than one matching component is found.
110       */
111      public ComponentFixture(Robot robot, String name, Class<? extends T> type) {
112        this(robot, findTarget(robot, name, type));
113      }
114    
115      private static <C extends Component> C findTarget(Robot robot, String name, Class<? extends C> type) {
116        validate(robot, type);
117        return robot.finder().findByName(name, type, requireShowing(robot));
118      }
119    
120      private static void validate(Robot robot, Class<?> type) {
121        notNullRobot(robot);
122        if (type == null) throw new NullPointerException("The type of component to look for should not be null");
123      }
124    
125      /**
126       * Returns whether showing components are the only ones participating in a component lookup. The returned value is
127       * obtained from the <code>{@link Settings#componentLookupScope() component lookup scope}</code> stored in this
128       * fixture's <code>{@link Robot}</code>.
129       * @return <code>true</code> if only showing components can participate in a component lookup, <code>false</code>
130       * otherwise.
131       */
132      protected boolean requireShowing() {
133        return requireShowing(robot);
134      }
135    
136      private static boolean requireShowing(Robot robot) {
137        return robot.settings().componentLookupScope().requireShowing();
138      }
139    
140      /**
141       * Creates a new <code>{@link ComponentFixture}</code>.
142       * @param robot performs simulation of user events on the given <code>Component</code>.
143       * @param target the <code>Component</code> to be managed by this fixture.
144       * @throws NullPointerException if <code>robot</code> is <code>null</code>.
145       * @throws NullPointerException if <code>target</code> is <code>null</code>.
146       */
147      public ComponentFixture(Robot robot, T target) {
148        this.robot = notNullRobot(robot);
149        this.target = notNullTarget(target);
150      }
151    
152      /**
153       * Returns a fixture that verifies the font of this fixture's <code>{@link Component}</code>.
154       * @return a fixture that verifies the font of this fixture's <code>Component</code>.
155       */
156      public final FontFixture font() {
157        return new FontFixture(fontOf(target), propertyName(target, FONT_PROPERTY));
158      }
159    
160      /**
161       * Returns a fixture that verifies the background color of this fixture's <code>{@link Component}</code>.
162       * @return a fixture that verifies the background color of this fixture's <code>Component</code>.
163       */
164      public final ColorFixture background() {
165        return new ColorFixture(backgroundOf(target), propertyName(target, BACKGROUND_PROPERTY));
166      }
167    
168      /**
169       * Returns a fixture that verifies the foreground color of this fixture's <code>{@link Component}</code>.
170       * @return a fixture that verifies the foreground color of this fixture's <code>Component</code>.
171       */
172      public final ColorFixture foreground() {
173        return new ColorFixture(foregroundOf(target), propertyName(target, FOREGROUND_PROPERTY));
174      }
175    
176      /**
177       * Returns this fixture's <code>{@link Component}</code> casted to the given sub-type.
178       * @param <C> enforces that the given type is a sub-type of the managed <code>Component</code>.
179       * @param type the type that the managed <code>Component</code> will be casted to.
180       * @return this fixture's <code>Component</code> casted to the given sub-type.
181       * @throws AssertionError if this fixture's <code>Component</code> is not an instance of the given type.
182       */
183      public final <C extends T> C targetCastedTo(Class<C> type) {
184        assertThat(target).as(format(target)).isInstanceOf(type);
185        return type.cast(target);
186      }
187    
188      /**
189       * Returns the GUI component in this fixture (same as <code>{@link #target}</code>.)
190       * <p>
191       * <strong>Note:</strong> Access to the GUI component returned by this method <em>must</em> be executed in the event
192       * dispatch thread. To do so, please execute a <code>{@link org.fest.swing.edt.GuiQuery GuiQuery}</code> or
193       * <code>{@link org.fest.swing.edt.GuiTask GuiTask}</code> (depending on what you need to do,) inside a
194       * <code>{@link GuiActionRunner}</code>. To learn more about Swing threading, please read the
195       * <a href="http://java.sun.com/javase/6/docs/api/javax/swing/package-summary.html#threading" target="_blank">Swing Threading Policy</a>.
196       * </p>
197       * @return the GUI component in this fixture.
198       */
199      public final T component() {
200        return target;
201      }
202    }