001    /*
002     * Created on Jul 1, 2007
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 @2007-2010 the original author or authors.
015     */
016    package org.fest.swing.fixture;
017    
018    import java.awt.Point;
019    import java.util.regex.Pattern;
020    
021    import javax.swing.JSpinner;
022    import javax.swing.text.JTextComponent;
023    
024    import org.fest.swing.core.*;
025    import org.fest.swing.driver.JSpinnerDriver;
026    import org.fest.swing.exception.*;
027    import org.fest.swing.timing.Timeout;
028    
029    /**
030     * Understands functional testing of <code>{@link JSpinner}</code>s:
031     * <ul>
032     * <li>user input simulation</li>
033     * <li>state verification</li>
034     * <li>property value query</li>
035     * </ul>
036     *
037     * @author Yvonne Wang
038     * @author Alex Ruiz
039     */
040    public class JSpinnerFixture extends ComponentFixture<JSpinner> implements CommonComponentFixture,
041        JComponentFixture, JPopupMenuInvokerFixture {
042    
043      private JSpinnerDriver driver;
044    
045      /**
046       * Creates a new <code>{@link JSpinnerFixture}</code>.
047       * @param robot performs simulation of user events on a <code>JSpinner</code>.
048       * @param spinnerName the name of the <code>JSpinner</code> to find using the given <code>Robot</code>.
049       * @throws NullPointerException if <code>robot</code> is <code>null</code>.
050       * @throws ComponentLookupException if a matching <code>JSpinner</code> could not be found.
051       * @throws ComponentLookupException if more than one matching <code>JSpinner</code> is found.
052       */
053      public JSpinnerFixture(Robot robot, String spinnerName) {
054        super(robot, spinnerName, JSpinner.class);
055        createDriver();
056      }
057    
058      /**
059       * Creates a new <code>{@link JSpinnerFixture}</code>.
060       * @param robot performs simulation of user events on the given <code>JSpinner</code>.
061       * @param target the <code>JSpinner</code> to be managed by this fixture.
062       * @throws NullPointerException if <code>robot</code> is <code>null</code>.
063       * @throws NullPointerException if <code>target</code> is <code>null</code>.
064       */
065      public JSpinnerFixture(Robot robot, JSpinner target) {
066        super(robot, target);
067        createDriver();
068      }
069    
070      private void createDriver() {
071        driver(new JSpinnerDriver(robot));
072      }
073    
074      /**
075       * Sets the <code>{@link JSpinnerDriver}</code> to be used by this fixture.
076       * @param newDriver the new <code>JSpinnerDriver</code>.
077       * @throws NullPointerException if the given driver is <code>null</code>.
078       */
079      protected final void driver(JSpinnerDriver newDriver) {
080        validateNotNull(newDriver);
081        driver = newDriver;
082      }
083    
084      /**
085       * Simulates a user incrementing the value of this fixture's <code>{@link JSpinner}</code> the given number of times.
086       * @param times how many times the value of this fixture's <code>JSpinner</code> should be incremented.
087       * @return this fixture.
088       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
089       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
090       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
091       */
092      public JSpinnerFixture increment(int times) {
093        driver.increment(target, times);
094        return this;
095      }
096    
097      /**
098       * Simulates a user incrementing the value of this fixture's <code>{@link JSpinner}</code> one time.
099       * @return this fixture.
100       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
101       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
102       */
103      public JSpinnerFixture increment() {
104        driver.increment(target);
105        return this;
106      }
107    
108      /**
109       * Simulates a user decrementing the value of this fixture's <code>{@link JSpinner}</code> the given number of times.
110       * @param times how many times the value of this fixture's <code>JSpinner</code> should be decremented.
111       * @return this fixture.
112       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
113       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
114       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
115       */
116      public JSpinnerFixture decrement(int times) {
117        driver.decrement(target, times);
118        return this;
119      }
120    
121      /**
122       * Simulates a user decrementing the value of this fixture's <code>{@link JSpinner}</code> one time.
123       * @return this fixture.
124       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
125       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
126       */
127      public JSpinnerFixture decrement() {
128        driver.decrement(target);
129        return this;
130      }
131    
132      /**
133       * Simulates a user entering the given text in this fixture's <code>{@link JSpinner}</code> (assuming its editor has a
134       * <code>{@link JTextComponent}</code> under it.) This method does not commit the value to the <code>JSpinner</code>.
135       * @param text the text to enter.
136       * @return this fixture.
137       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
138       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
139       * @throws ActionFailedException if the editor of the <code>JSpinner</code> is not a <code>JTextComponent</code> or
140       * cannot be found.
141       * @throws UnexpectedException if the entering the text in the <code>JSpinner</code>'s editor fails.
142       */
143      public JSpinnerFixture enterText(String text) {
144        driver.enterText(target, text);
145        return this;
146      }
147    
148      /**
149       * Simulates a user entering and committing the given text in this fixture's <code>{@link JSpinner}</code> (assuming
150       * its editor has a <code>{@link JTextComponent}</code> under it.)
151       * @param text the text to enter.
152       * @return this fixture.
153       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
154       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
155       * @throws ActionFailedException if the editor of the <code>JSpinner</code> is not a <code>JTextComponent</code> or
156       * cannot be found.
157       * @throws UnexpectedException if the entering the text in the <code>JSpinner</code>'s editor fails.
158       */
159      public JSpinnerFixture enterTextAndCommit(String text) {
160        driver.enterTextAndCommit(target, text);
161        return this;
162      }
163    
164      /**
165       * Selects the given value in this fixture's <code>{@link JSpinner}</code>.
166       * @param value the value to select.
167       * @return this fixture.
168       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
169       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
170       * @throws IllegalArgumentException if the <code>JSpinner</code> does not support the specified <code>value</code>.
171       */
172      public Object select(Object value) {
173        driver.selectValue(target, value);
174        return this;
175      }
176    
177      /**
178       * Simulates a user clicking this fixture's <code>{@link JSpinner}</code>.
179       * @return this fixture.
180       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
181       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
182       */
183      public JSpinnerFixture click() {
184        driver.click(target);
185        return this;
186      }
187    
188      /**
189       * Simulates a user clicking this fixture's <code>{@link JSpinner}</code>.
190       * @param button the button to click.
191       * @return this fixture.
192       * @throws NullPointerException if the given <code>MouseButton</code> is <code>null</code>.
193       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
194       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
195       */
196      public JSpinnerFixture click(MouseButton button) {
197        driver.click(target, button);
198        return this;
199      }
200    
201      /**
202       * Simulates a user clicking this fixture's <code>{@link JSpinner}</code>.
203       * @param mouseClickInfo specifies the button to click and the times the button should be clicked.
204       * @return this fixture.
205       * @throws NullPointerException if the given <code>MouseClickInfo</code> is <code>null</code>.
206       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
207       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
208       */
209      public JSpinnerFixture click(MouseClickInfo mouseClickInfo) {
210        driver.click(target, mouseClickInfo);
211        return this;
212      }
213    
214      /**
215       * Simulates a user right-clicking this fixture's <code>{@link JSpinner}</code>.
216       * @return this fixture.
217       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
218       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
219       */
220      public JSpinnerFixture rightClick() {
221        driver.rightClick(target);
222        return this;
223      }
224    
225      /**
226       * Simulates a user double-clicking this fixture's <code>{@link JSpinner}</code>.
227       * @return this fixture.
228       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
229       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
230       */
231      public JSpinnerFixture doubleClick() {
232        driver.doubleClick(target);
233        return this;
234      }
235    
236      /**
237       * Gives input focus to this fixture's <code>{@link JSpinner}</code>.
238       * @return this fixture.
239       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
240       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
241       */
242      public JSpinnerFixture focus() {
243        driver.focus(target);
244        return this;
245      }
246    
247      /**
248       * Simulates a user pressing given key with the given modifiers on this fixture's <code>{@link JSpinner}</code>.
249       * Modifiers is a mask from the available <code>{@link java.awt.event.InputEvent}</code> masks.
250       * @param keyPressInfo specifies the key and modifiers to press.
251       * @return this fixture.
252       * @throws NullPointerException if the given <code>KeyPressInfo</code> is <code>null</code>.
253       * @throws IllegalArgumentException if the given code is not a valid key code.
254       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
255       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
256       * @see KeyPressInfo
257       */
258      public JSpinnerFixture pressAndReleaseKey(KeyPressInfo keyPressInfo) {
259        driver.pressAndReleaseKey(target, keyPressInfo);
260        return this;
261      }
262    
263      /**
264       * Simulates a user pressing and releasing the given keys on this fixture's <code>{@link JSpinner}</code>. This method
265       * does not affect the current focus.
266       * @param keyCodes one or more codes of the keys to press.
267       * @return this fixture.
268       * @throws NullPointerException if the given array of codes is <code>null</code>.
269       * @throws IllegalArgumentException if any of the given code is not a valid key code.
270       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
271       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
272       * @see java.awt.event.KeyEvent
273       */
274      public JSpinnerFixture pressAndReleaseKeys(int... keyCodes) {
275        driver.pressAndReleaseKeys(target, keyCodes);
276        return this;
277      }
278    
279      /**
280       * Simulates a user pressing the given key on this fixture's <code>{@link JSpinner}</code>.
281       * @param keyCode the code of the key to press.
282       * @return this fixture.
283       * @throws IllegalArgumentException if any of the given code is not a valid key code.
284       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
285       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
286       * @see java.awt.event.KeyEvent
287       */
288      public JSpinnerFixture pressKey(int keyCode) {
289        driver.pressKey(target, keyCode);
290        return this;
291      }
292    
293      /**
294       * Simulates a user releasing the given key on this fixture's <code>{@link JSpinner}</code>.
295       * @param keyCode the code of the key to release.
296       * @return this fixture.
297       * @throws IllegalArgumentException if any of the given code is not a valid key code.
298       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
299       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
300       * @see java.awt.event.KeyEvent
301       */
302      public JSpinnerFixture releaseKey(int keyCode) {
303        driver.releaseKey(target, keyCode);
304        return this;
305      }
306    
307      /**
308       * Asserts that this fixture's <code>{@link JSpinner}</code> has input focus.
309       * @return this fixture.
310       * @throws AssertionError if this fixture's <code>JSpinner</code> does not have input focus.
311       */
312      public JSpinnerFixture requireFocused() {
313        driver.requireFocused(target);
314        return this;
315      }
316    
317      /**
318       * Asserts that this fixture's <code>{@link JSpinner}</code> is enabled.
319       * @return this fixture.
320       * @throws AssertionError if this fixture's <code>JSpinner</code> is disabled.
321       */
322      public JSpinnerFixture requireEnabled() {
323        driver.requireEnabled(target);
324        return this;
325      }
326    
327      /**
328       * Asserts that this fixture's <code>{@link JSpinner}</code> is enabled.
329       * @param timeout the time this fixture will wait for the component to be enabled.
330       * @return this fixture.
331       * @throws org.fest.swing.exception.WaitTimedOutError if this fixture's <code>JSpinner</code> is never enabled.
332       */
333      public JSpinnerFixture requireEnabled(Timeout timeout) {
334        driver.requireEnabled(target, timeout);
335        return this;
336      }
337    
338      /**
339       * Asserts that this fixture's <code>{@link JSpinner}</code> is disabled.
340       * @return this fixture.
341       * @throws AssertionError if this fixture's <code>JSpinner</code> is enabled.
342       */
343      public JSpinnerFixture requireDisabled() {
344        driver.requireDisabled(target);
345        return this;
346      }
347    
348      /**
349       * Asserts that this fixture's <code>{@link JSpinner}</code> is visible.
350       * @return this fixture.
351       * @throws AssertionError if this fixture's <code>JSpinner</code> is not visible.
352       */
353      public JSpinnerFixture requireVisible() {
354        driver.requireVisible(target);
355        return this;
356      }
357    
358      /**
359       * Asserts that this fixture's <code>{@link JSpinner}</code> is not visible.
360       * @return this fixture.
361       * @throws AssertionError if this fixture's <code>JSpinner</code> is visible.
362       */
363      public JSpinnerFixture requireNotVisible() {
364        driver.requireNotVisible(target);
365        return this;
366      }
367    
368      /**
369       * Verifies that the value of this fixture's <code>{@link JSpinner}</code> is equal to the given one.
370       * @param value the expected value of this fixture's <code>JSpinner</code>.
371       * @return this fixture.
372       * @throws AssertionError if the value of this fixture's <code>JSpinner</code> is not equal to the given one.
373       */
374      public JSpinnerFixture requireValue(Object value) {
375        driver.requireValue(target, value);
376        return this;
377      }
378    
379      /**
380       * Returns the text displayed by this fixture's <code>{@link JSpinner}</code>. This method first tries to get the text
381       * displayed in the <code>JSpinner</code>'s editor, assuming it is a <code>{@link JTextComponent}</code>. If the
382       * text from the editor cannot be retrieved, it will return the <code>String</code> representation of the value
383       * in the <code>JSpinner</code>'s model.
384       * @return the text displayed by this fixture's <code>JSpinner</code>.
385       * @since 1.2
386       */
387      public String text() {
388        return driver.textOf(target);
389      }
390    
391    
392      /**
393       * Asserts that the toolTip in this fixture's <code>{@link JSpinner}</code> matches the given value.
394       * @param expected the given value. It can be a regular expression.
395       * @return this fixture.
396       * @throws AssertionError if the toolTip in this fixture's <code>JSpinner</code> does not match the given value.
397       * @since 1.2
398       */
399      public JSpinnerFixture requireToolTip(String expected) {
400        driver.requireToolTip(target, expected);
401        return this;
402      }
403    
404      /**
405       * Asserts that the toolTip in this fixture's <code>{@link JSpinner}</code> matches the given regular expression
406       * pattern.
407       * @param pattern the regular expression pattern to match.
408       * @return this fixture.
409       * @throws NullPointerException if the given regular expression pattern is <code>null</code>.
410       * @throws AssertionError if the toolTip in this fixture's <code>JSpinner</code> does not match the given regular
411       * expression.
412       * @since 1.2
413       */
414      public JSpinnerFixture requireToolTip(Pattern pattern) {
415        driver.requireToolTip(target, pattern);
416        return this;
417      }
418    
419      /**
420       * Returns the client property stored in this fixture's <code>{@link JSpinner}</code>, under the given key.
421       * @param key the key to use to retrieve the client property.
422       * @return the value of the client property stored under the given key, or <code>null</code> if the property was
423       * not found.
424       * @throws NullPointerException if the given key is <code>null</code>.
425       * @since 1.2
426       */
427      public Object clientProperty(Object key) {
428        return driver.clientProperty(target, key);
429      }
430    
431      /**
432       * Shows a pop-up menu using this fixture's <code>{@link JSpinner}</code> as the invoker of the pop-up menu.
433       * @return a fixture that manages the displayed pop-up menu.
434       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
435       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
436       * @throws ComponentLookupException if a pop-up menu cannot be found.
437       */
438      public JPopupMenuFixture showPopupMenu() {
439        return new JPopupMenuFixture(robot, driver.invokePopupMenu(target));
440      }
441    
442      /**
443       * Shows a pop-up menu at the given point using this fixture's <code>{@link JSpinner}</code> as the invoker of the
444       * pop-up menu.
445       * @param p the given point where to show the pop-up menu.
446       * @return a fixture that manages the displayed pop-up menu.
447       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is disabled.
448       * @throws IllegalStateException if this fixture's <code>JSpinner</code> is not showing on the screen.
449       * @throws ComponentLookupException if a pop-up menu cannot be found.
450       */
451      public JPopupMenuFixture showPopupMenuAt(Point p) {
452        return new JPopupMenuFixture(robot, driver.invokePopupMenu(target, p));
453      }
454    }