001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.tester;
018
019import static org.junit.jupiter.api.Assertions.assertEquals;
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023import static org.junit.jupiter.api.Assertions.fail;
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.List;
029import java.util.Locale;
030
031import javax.servlet.ServletContext;
032
033import org.apache.commons.collections4.CollectionUtils;
034import org.apache.wicket.Component;
035import org.apache.wicket.MarkupContainer;
036import org.apache.wicket.Page;
037import org.apache.wicket.behavior.AbstractAjaxBehavior;
038import org.apache.wicket.behavior.Behavior;
039import org.apache.wicket.feedback.ExactLevelFeedbackMessageFilter;
040import org.apache.wicket.feedback.FeedbackMessage;
041import org.apache.wicket.feedback.IFeedback;
042import org.apache.wicket.feedback.IFeedbackMessageFilter;
043import org.apache.wicket.markup.IMarkupFragment;
044import org.apache.wicket.markup.html.WebPage;
045import org.apache.wicket.markup.html.basic.Label;
046import org.apache.wicket.markup.html.form.ValidationErrorFeedback;
047import org.apache.wicket.markup.html.link.BookmarkablePageLink;
048import org.apache.wicket.markup.html.panel.FeedbackPanel;
049import org.apache.wicket.model.IModel;
050import org.apache.wicket.protocol.http.WebApplication;
051import org.apache.wicket.request.mapper.parameter.PageParameters;
052import org.apache.wicket.util.lang.Args;
053import org.apache.wicket.util.lang.Objects;
054import org.slf4j.Logger;
055import org.slf4j.LoggerFactory;
056
057/**
058 * A helper class to ease unit testing of Wicket applications without the need for a servlet
059 * container. To start a test, either use <code>startPage</code> or <code>startPanel</code>:
060 *
061 * <pre>
062 * // production page
063 * public class MyPage extends WebPage
064 * {
065 *      public MyPage()
066 *      {
067 *              add(new Label(&quot;myMessage&quot;, &quot;Hello!&quot;));
068 *              add(new Link(&quot;toYourPage&quot;)
069 *              {
070 *                      public void onClick()
071 *                      {
072 *                              setResponsePage(new YourPage(&quot;Hi!&quot;));
073 *                      }
074 *              });
075 *      }
076 * }
077 * </pre>
078 *
079 * <pre>
080 * // test code
081 * private WicketTester tester;
082 *
083 * public void setUp()
084 * {
085 *      tester = new WicketTester();
086 * }
087 *
088 * public void testRenderMyPage()
089 * {
090 *      // start and render the test page
091 *      tester.startPage(MyPage.class);
092 *      // assert rendered page class
093 *      tester.assertRenderedPage(MyPage.class);
094 *      // assert rendered label component
095 *      tester.assertLabel(&quot;myMessage&quot;, &quot;Hello!&quot;);
096 * }
097 * </pre>
098 *
099 * The above example is straight forward: start <code>MyPage.class</code> and assert
100 * <code>Label</code> it rendered. Next, we try to navigate through a <code>Link</code>:
101 *
102 * <pre>
103 * // production page
104 * public class YourPage extends WebPage
105 * {
106 *      public YourPage(String message)
107 *      {
108 *              add(new Label(&quot;yourMessage&quot;, message));
109 *              info(&quot;Wicket Rocks ;-)&quot;);
110 *      }
111 * }
112 *
113 * // test code
114 * public void testLinkToYourPage()
115 * {
116 *      tester.startPage(MyPage.class);
117 *      // click link and render
118 *      tester.clickLink(&quot;toYourPage&quot;);
119 *      tester.assertRenderedPage(YourPage.class);
120 *      tester.assertLabel(&quot;yourMessage&quot;, &quot;Hi!&quot;);
121 * }
122 * </pre>
123 *
124 * <code>tester.clickLink(path);</code> will simulate user click on the component (in this case,
125 * it's a <code>Link</code>) and render the response page <code>YourPage</code>. Ok, unit test of
126 * <code>MyPage</code> is completed. Now we test <code>YourPage</code> standalone:
127 *
128 * <pre>
129 * // test code
130 * public void testRenderYourPage()
131 * {
132 *      // provide page instance source for WicketTester
133 *      tester.startPage(new YourPage(&quot;mock message&quot;));
134 *      tester.assertRenderedPage(YourPage.class);
135 *      tester.assertLabel(&quot;yourMessage&quot;, &quot;mock message&quot;);
136 *      // assert feedback messages in INFO Level
137 *      tester.assertInfoMessages(new String[] { &quot;Wicket Rocks ;-)&quot; });
138 * }
139 * </pre>
140 *
141 * Many methods require a 'path' parameter. E.g. the page relative path can be obtained via
142 * {@link Component#getPageRelativePath()}. Since each Component has an ID/name, any Component can
143 * also be referenced by its ID {@link MarkupContainer#get(String)}. And since MarkupContainer's and
144 * its subclasses are containers which allow to add Components (in sync with the markup hierarchy),
145 * you may not only access direct children but also grandchildren like get("myPanel:myForm:myNameField")
146 * separating each ID with a ':'.
147 *
148 * <h2>Cookie handling</h2>
149 *
150 * There are some expectations about wicket tester cookie handling which should match as best as
151 * it can be with a real client server request response cycle:
152 * <ul>
153 *  <li> all valid cookies set before a request is made (tester.getRequest().addCookie()) should
154 *   appear in the page request</li>
155 *  <li> all cookies set in the response should appear in the last response (tester.getLastResponse())
156 *   after the request is made (expired cookies and others)</li>
157 *  <li> all cookies set in the response should appear even after a redirect response is made
158 *   until the final response (tester.getLastResponse()) is written to the client (wicket tester)</li>
159 *  <li> all valid cookies (maxAge!=0) from the last response should be added to
160 *   the next request cookies (tester.getRequest().getCookies())</li>
161 * </ul>
162 *
163 * @author Ingram Chen
164 * @author Juergen Donnerstag
165 * @author Frank Bille
166 * @since 1.2.6
167 */
168public class WicketTester extends BaseWicketTester
169{
170        private static final Logger log = LoggerFactory.getLogger(WicketTester.class);
171
172        /**
173         * Creates a <code>WicketTester</code> and automatically creates a <code>WebApplication</code>,
174         * but the tester will have no home page.
175         */
176        public WicketTester()
177        {
178        }
179
180        /**
181         * Creates a <code>WicketTester</code> and automatically creates a <code>WebApplication</code>.
182         *
183         * @param homePage
184         *            a home page <code>Class</code>
185         */
186        public WicketTester(final Class<? extends Page> homePage)
187        {
188                super(homePage);
189        }
190
191        /**
192         * Creates a <code>WicketTester</code>.
193         *
194         * @param application
195         *            a <code>WicketTester</code> <code>WebApplication</code> object
196         */
197        public WicketTester(final WebApplication application)
198        {
199                super(application);
200        }
201
202        /**
203         * Creates a <code>WicketTester</code> to help unit testing.
204         *
205         * @param application
206         *            a <code>WicketTester</code> <code>WebApplication</code> object
207         * @param path
208         *            the absolute path on disk to the web application's contents (e.g. war root) - may
209         *            be <code>null</code>
210         *
211         * @see org.apache.wicket.mock.MockApplication#MockApplication()
212         */
213        public WicketTester(final WebApplication application, final String path)
214        {
215                super(application, path);
216        }
217
218        /**
219         * Creates a <code>WicketTester</code> to help unit testing.
220         *
221         * @param application
222         *            a <code>WicketTester</code> <code>WebApplication</code> object
223         * @param servletCtx
224         *            the servlet context used as backend
225         */
226        public WicketTester(WebApplication application, ServletContext servletCtx)
227        {
228                super(application, servletCtx);
229        }
230
231        /**
232         * Creates a <code>WicketTester</code> to help unit testing.
233         *
234         * @param application
235         *            a <code>WicketTester</code> <code>WebApplication</code> object
236         * @param init
237         *            force the application to be initialized (default = true)
238         */
239        public WicketTester(WebApplication application, boolean init)
240        {
241                super(application, init);
242        }
243
244        /**
245         * Creates a <code>WicketTester</code> to help unit testing.
246         *
247         * @param application
248         *            a <code>WicketTester</code> <code>WebApplication</code> object
249         * @param servletCtx
250         *            the servlet context used as backend
251         * @param init
252         *            force the application to be initialized (default = true)
253         */
254        public WicketTester(WebApplication application, ServletContext servletCtx, boolean init)
255        {
256                super(application, servletCtx, init);
257        }
258
259        /**
260         * Returns the current Maven build directory taken from the <tt>basedir</tt> system property, or
261         * null if not set
262         *
263         * @return path with a trailing slash
264         */
265        public static String getBasedir()
266        {
267                String basedir = System.getProperty("basedir");
268                if (basedir != null)
269                {
270                        basedir = basedir + "/";
271                }
272                else
273                {
274                        basedir = "";
275                }
276                return basedir;
277        }
278
279        /**
280         * Asserts that the Ajax location header is present.
281         */
282        public void assertAjaxLocation()
283        {
284                if (null != getLastResponse().getHeader("Location"))
285                {
286                        fail("Location header should *not* be present when using Ajax");
287                }
288
289                String ajaxLocation = getLastResponse().getHeader("Ajax-Location");
290                if (null == ajaxLocation)
291                {
292                        fail("Ajax-Location header should be present when using Ajax");
293                }
294
295                int statusCode = getLastResponse().getStatus();
296                if (statusCode != 200)
297                {
298                        fail("Expected HTTP status code to be 200 (OK)");
299                }
300        }
301
302        /**
303         * Asserts a <code>Component</code> class.
304         *
305         * @param path
306         *            path to <code>Component</code>
307         * @param expectedComponentClass
308         *            expected <code>Component</code> class
309         */
310        public void assertComponent(String path, Class<? extends Component> expectedComponentClass)
311        {
312                assertResult(isComponent(path, expectedComponentClass));
313        }
314
315        /**
316         * Asserts that the <code>Component</code> a the given path has a behavior of the given type.
317         *
318         * @param path
319         *            path to <code>Component</code>
320         * @param expectedBehaviorClass
321         *            expected <code>Behavior</code> class
322         */
323        public void assertBehavior(String path, Class<? extends Behavior> expectedBehaviorClass)
324        {
325                Args.notNull(expectedBehaviorClass, "expectedBehaviorClass");
326
327                Component component = assertExists(path);
328                List<? extends Behavior> behaviors = component.getBehaviors(expectedBehaviorClass);
329                final String message = String.format("Component '%s' has no behaviors of type '%s'",
330                        component.getPageRelativePath(), expectedBehaviorClass);
331                assertResult(new Result(CollectionUtils.isEmpty(behaviors), message));
332        }
333
334        /**
335         * Tests that a <code>Component</code> has been added to a <code>AjaxRequestTarget</code>, using
336         * {@link org.apache.wicket.ajax.AjaxRequestTarget#add(Component...)}. This method actually
337         * tests that a <code>Component</code> is on the Ajax response sent back to the client.
338         * <p>
339         * PLEASE NOTE! This method doesn't actually insert the <code>Component</code> in the client DOM
340         * tree, using JavaScript. But it shouldn't be needed because you just have to trust that Wicket
341         * Ajax JavaScript works.
342         *
343         * @param component
344         *            a <code>Component</code> to be tested
345         */
346        public void assertComponentOnAjaxResponse(Component component)
347        {
348                Result result = isComponentOnAjaxResponse(component);
349                assertResult(result);
350        }
351
352        /**
353         * Tests that a <code>Component</code> has been added to a <code>AjaxRequestTarget</code>, using
354         * {@link org.apache.wicket.ajax.AjaxRequestTarget#add(Component...)}. This method actually
355         * tests that a <code>Component</code> is on the Ajax response sent back to the client.
356         * <p>
357         * PLEASE NOTE! This method doesn't actually insert the <code>Component</code> in the client DOM
358         * tree, using JavaScript. But it shouldn't be needed because you just have to trust that Wicket
359         * Ajax JavaScript works.
360         *
361         * @param componentPath
362         *            a <code>Component</code> path to test
363         */
364        public void assertComponentOnAjaxResponse(String componentPath)
365        {
366                Component component = getComponentFromLastRenderedPage(componentPath, false);
367                assertComponentOnAjaxResponse(component);
368        }
369
370        /**
371         * Asserts the content of last rendered page contains (matches) a given regex pattern.
372         *
373         * @param pattern
374         *            a regex pattern to match
375         */
376        public void assertContains(String pattern)
377        {
378                assertResult(ifContains(pattern));
379        }
380
381        /**
382         * The opposite of {@link #assertContains(String)}.
383         *
384         * @param pattern
385         *            pattern
386         */
387        public void assertContainsNot(String pattern)
388        {
389                assertResult(ifContainsNot(pattern));
390        }
391
392        /**
393         * Asserts that a component's markup has loaded with the given variation
394         *
395         * @param component
396         *            The component which markup to check
397         * @param expectedVariation
398         *            The expected variation of the component's markup
399         */
400        public void assertMarkupVariation(Component component, String expectedVariation)
401        {
402                Result result = Result.PASS;
403                IMarkupFragment markup = getMarkupFragment(component);
404
405                String actualVariation = markup.getMarkupResourceStream().getVariation();
406                if (Objects.equal(expectedVariation, actualVariation) == false)
407                {
408                        result = Result.fail(
409                                String.format("Wrong variation for component '%s'. Actual: '%s', expected: '%s'",
410                                        component.getPageRelativePath(), actualVariation, expectedVariation));
411                }
412
413                assertResult(result);
414        }
415
416        /**
417         * Asserts that a component's markup has loaded with the given style.
418         *
419         * @param component
420         *            The component which markup to check
421         * @param expectedStyle
422         *            The expected style of the component's markup. For example: <em>green</em> in
423         *            <code>MyPanel_green.html</code>
424         */
425        public void assertMarkupStyle(Component component, String expectedStyle)
426        {
427                Result result = Result.PASS;
428                IMarkupFragment markup = getMarkupFragment(component);
429
430                String actualStyle = markup.getMarkupResourceStream().getStyle();
431                if (Objects.equal(expectedStyle, actualStyle) == false)
432                {
433                        result = Result
434                                .fail(String.format("Wrong style for component '%s'. Actual: '%s', expected: '%s'",
435                                        component.getPageRelativePath(), actualStyle, expectedStyle));
436                }
437
438                assertResult(result);
439        }
440
441        /**
442         * Asserts that a component's markup has loaded with the given locale
443         *
444         * @param component
445         *            The component which markup to check
446         * @param expectedLocale
447         *            The expected locale of the component's markup
448         */
449        public void assertMarkupLocale(Component component, Locale expectedLocale)
450        {
451                Result result = Result.PASS;
452                IMarkupFragment markup = getMarkupFragment(component);
453
454                Locale actualLocale = markup.getMarkupResourceStream().getLocale();
455                if (Objects.equal(expectedLocale, actualLocale) == false)
456                {
457                        result = Result
458                                .fail(String.format("Wrong locale for component '%s'. Actual: '%s', expected: '%s'",
459                                        component.getPageRelativePath(), actualLocale, expectedLocale));
460                }
461
462                assertResult(result);
463        }
464
465        private IMarkupFragment getMarkupFragment(Component component)
466        {
467                IMarkupFragment markup = null;
468                if (component instanceof MarkupContainer)
469                {
470                        markup = ((MarkupContainer)component).getAssociatedMarkup();
471                }
472
473                if (markup == null)
474                {
475                        markup = component.getMarkup();
476                }
477
478                if (markup == null)
479                {
480                        fail(String.format("Cannot find the markup of component: %s",
481                                component.getPageRelativePath()));
482                }
483
484                return markup;
485        }
486
487        /**
488         * Asserts error-level feedback messages.
489         *
490         * @param expectedErrorMessages
491         *            expected error messages
492         */
493        public void assertErrorMessages(Serializable... expectedErrorMessages)
494        {
495                assertFeedbackMessages(new ExactLevelFeedbackMessageFilter(FeedbackMessage.ERROR),
496                        expectedErrorMessages);
497        }
498
499        /**
500         * Assert info-level feedback messages.
501         *
502         * @param expectedInfoMessages
503         *            expected info messages
504         */
505        public void assertInfoMessages(Serializable... expectedInfoMessages)
506        {
507                assertFeedbackMessages(new ExactLevelFeedbackMessageFilter(FeedbackMessage.INFO),
508                        expectedInfoMessages);
509        }
510
511        /**
512         * Assert there are feedback messages accepted by the provided filter.
513         *
514         * @param filter
515         *            the filter that will decide which messages to check
516         * @param expectedMessages
517         *            expected feedback messages
518         */
519        public void assertFeedbackMessages(IFeedbackMessageFilter filter,
520                Serializable... expectedMessages)
521        {
522                List<FeedbackMessage> feedbackMessages = getFeedbackMessages(filter);
523                List<Serializable> actualMessages = getActualFeedbackMessages(feedbackMessages);
524                WicketTesterHelper.assertEquals(Arrays.asList(expectedMessages), actualMessages);
525        }
526
527        /**
528         * Asserts that there is a feedback message provided by a given component
529         *
530         * @param component
531         *            the component that provided the expected feedback message. Optional.
532         * @param key
533         *            the resource key for the feedback message. Mandatory.
534         * @param model
535         *            the model used for interpolating the feedback message. Optional.
536         * @param filter
537         *            the filter that decides in which messages to look in. E.g. with a specific level,
538         *            rendered or not, etc.
539         */
540        public void assertComponentFeedbackMessage(Component component, String key, IModel<?> model,
541                IFeedbackMessageFilter filter)
542        {
543                Args.notNull(key, "key");
544
545                String expectedMessage = getApplication().getResourceSettings().getLocalizer().getString(
546                        key, component, model);
547
548                List<FeedbackMessage> feedbackMessages = getFeedbackMessages(filter);
549                List<Serializable> actualMessages = getActualFeedbackMessages(feedbackMessages);
550
551                assertTrue(actualMessages.contains(expectedMessage), String
552                        .format("Feedback message with key '%s' cannot be found in %s", key, actualMessages));
553        }
554
555        /**
556         * Extracts the actual messages from the passed feedback messages. Specially handles
557         * ValidationErrorFeedback messages by extracting their String message
558         *
559         * @param feedbackMessages
560         *            the feedback messages
561         * @return the FeedbackMessages' messages
562         */
563        private List<Serializable> getActualFeedbackMessages(List<FeedbackMessage> feedbackMessages)
564        {
565                List<Serializable> actualMessages = new ArrayList<>();
566                for (FeedbackMessage feedbackMessage : feedbackMessages)
567                {
568                        Serializable message = feedbackMessage.getMessage();
569                        if (message instanceof ValidationErrorFeedback)
570                        {
571                                actualMessages.add(message.toString());
572                        }
573                        else
574                        {
575                                actualMessages.add(message);
576                        }
577                }
578                return actualMessages;
579        }
580
581        /**
582         * Assert that a particular feedback panel is rendering certain messages.
583         *
584         * NOTE: this casts the component at the specified path to a {@link FeedbackPanel}, so it will
585         * not work with custom {@link IFeedback} implementations unless you are subclassing
586         * {@link FeedbackPanel}
587         *
588         * @param path
589         *            path to the feedback panel
590         * @param messages
591         *            messages expected to be rendered
592         */
593        public void assertFeedback(String path, Serializable... messages)
594        {
595                final FeedbackPanel fbp = (FeedbackPanel)getComponentFromLastRenderedPage(path);
596                final IModel<List<FeedbackMessage>> model = fbp.getFeedbackMessagesModel();
597                final List<FeedbackMessage> renderedMessages = model.getObject();
598                if (renderedMessages == null)
599                {
600                        fail(String.format("feedback panel at path [%s] returned null messages", path));
601                }
602
603                if (messages.length != renderedMessages.size())
604                {
605                        fail(String.format(
606                                "you expected '%d' messages for the feedback panel [%s], but there were actually '%d'",
607                                messages.length, path, renderedMessages.size()));
608                }
609
610                for (int i = 0; i < messages.length && i < renderedMessages.size(); i++)
611                {
612                        final Serializable expected = messages[i];
613                        boolean found = false;
614                        for (FeedbackMessage actual : renderedMessages)
615                        {
616                                if (Objects.equal(expected, actual.getMessage()))
617                                {
618                                        found = true;
619                                        break;
620                                }
621                        }
622                        if (!found)
623                        {
624                                assertResult(Result.fail("Missing expected feedback message: " + expected));
625                        }
626                }
627        }
628
629        /**
630         * Asserts that a <code>Component</code> is invisible.
631         *
632         * @param path
633         *            path to <code>Component</code>
634         */
635        public void assertInvisible(String path)
636        {
637                assertResult(isInvisible(path));
638        }
639
640        /**
641         * Asserts the text of a <code>Label</code> <code>Component</code>.
642         *
643         * @param path
644         *            path to <code>Label</code> <code>Component</code>
645         * @param expectedLabelText
646         *            expected text of the <code>Label</code>
647         */
648        public void assertLabel(String path, String expectedLabelText)
649        {
650                Label label = (Label)getComponentFromLastRenderedPage(path);
651                assertEquals(expectedLabelText, label.getDefaultModelObjectAsString());
652        }
653
654        /**
655         * Asserts the model value of a component.
656         *
657         * @param path
658         *            path to the component on the page
659         * @param expectedValue
660         *            expected value of the component's model
661         */
662        public void assertModelValue(String path, Object expectedValue)
663        {
664                Component component = getComponentFromLastRenderedPage(path);
665                assertEquals(expectedValue, component.getDefaultModelObject());
666        }
667
668        /**
669         * Asserts no error-level feedback messages.
670         */
671        public void assertNoErrorMessage()
672        {
673                assertNoFeedbackMessage(FeedbackMessage.ERROR);
674        }
675
676        /**
677         * Asserts no info-level feedback messages.
678         */
679        public void assertNoInfoMessage()
680        {
681                assertNoFeedbackMessage(FeedbackMessage.INFO);
682        }
683
684        /**
685         * Asserts there are no feedback messages with a certain level.
686         *
687         * @param level
688         *            the level to check for
689         */
690        public void assertNoFeedbackMessage(int level)
691        {
692                Result result = hasNoFeedbackMessage(level);
693                assertFalse(result.wasFailed(), result.getMessage());
694        }
695
696        /**
697         * Asserts a last-rendered <code>Page</code> class.
698         *
699         * @param expectedRenderedPageClass
700         *            expected class of last rendered <code>Page</code>
701         */
702        public void assertRenderedPage(Class<? extends Page> expectedRenderedPageClass)
703        {
704                assertResult(isRenderedPage(expectedRenderedPageClass));
705        }
706
707        /**
708         * Asserts last-rendered <code>Page</code> against an expected HTML document.
709         * <p>
710         * Use <code>-Dwicket.replace.expected.results=true</code> to automatically replace the expected
711         * output file.
712         *
713         * @param clazz
714         *            <code>Class</code> used to load the file (relative to <code>clazz</code> package)
715         * @param filename
716         *            expected output filename <code>String</code>
717         * @throws Exception
718         */
719        @Override
720        public void assertResultPage(final Class<?> clazz, final String filename) throws Exception
721        {
722                String document = getLastResponseAsString();
723                DiffUtil.validatePage(document, clazz, filename, true);
724        }
725
726        /**
727         * Asserts last-rendered <code>Page</code> against an expected HTML document as a
728         * <code>String</code>
729         *
730         * @param expectedDocument
731         *            expected output <code>String</code>
732         */
733        public void assertResultPage(final String expectedDocument)
734        {
735                // Validate the document
736                String document = getLastResponseAsString();
737                assertEquals(expectedDocument, document);
738        }
739
740        /**
741         * Asserts that a <code>Component</code> is visible.
742         *
743         * @param path
744         *            path to a <code>Component</code>
745         */
746        public void assertVisible(String path)
747        {
748                assertResult(isVisible(path));
749        }
750
751        /**
752         * assert component is enabled.
753         *
754         * @param path
755         *            path to component
756         *
757         */
758        public void assertEnabled(String path)
759        {
760                assertResult(isEnabled(path));
761        }
762
763        /**
764         * assert component is enabled.
765         *
766         * @param path
767         *            path to component
768         */
769        public void assertDisabled(String path)
770        {
771                assertResult(isDisabled(path));
772        }
773
774        /**
775         * assert form component is required.
776         *
777         * @param path
778         *            path to form component
779         */
780        public void assertRequired(String path)
781        {
782                assertResult(isRequired(path));
783        }
784
785        /**
786         * assert form component is required.
787         *
788         * @param path
789         *            path to form component
790         */
791        public void assertNotRequired(String path)
792        {
793                assertResult(isNotRequired(path));
794        }
795
796        /**
797         *
798         * @param result
799         */
800        private void assertResult(Result result)
801        {
802                if (result.wasFailed())
803                {
804                        fail(result.getMessage());
805                }
806        }
807
808        /**
809         * Checks whether a component is visible and/or enabled before usage
810         *
811         * @param component
812         */
813        public void assertUsability(final Component component)
814        {
815                checkUsability(component, true);
816        }
817
818        /**
819         *
820         * @param link
821         */
822        public void clickLink(Component link)
823        {
824                clickLink(link.getPageRelativePath());
825        }
826
827        /**
828         * Asserts that that the BookmarkablePageLink identified by "id" points to the page as
829         * expected - including parameters.
830         *
831         * @param id
832         * @param pageClass
833         * @param parameters
834         */
835        public void assertBookmarkablePageLink(final String id,
836                final Class<? extends WebPage> pageClass, final PageParameters parameters)
837        {
838                BookmarkablePageLink<?> pageLink;
839                try
840                {
841                        pageLink = (BookmarkablePageLink<?>)getComponentFromLastRenderedPage(id);
842                }
843                catch (ClassCastException e)
844                {
845                        throw new IllegalArgumentException(
846                                "Component with id:" + id + " is not a BookmarkablePageLink");
847                }
848
849                assertEquals(pageClass, pageLink.getPageClass(),
850                        "BookmarkablePageLink: " + id + " is pointing to the wrong page");
851
852                assertEquals(parameters, pageLink.getPageParameters(),
853                        "One or more of the parameters associated with the BookmarkablePageLink: " + id +
854                                " do not match");
855        }
856
857        /**
858         * Use <code>-Dwicket.replace.expected.results=true</code> to automatically replace the expected
859         * output file.
860         *
861         * @param <T>
862         * @param testClass
863         * @param pageClass
864         * @param filename
865         * @throws Exception
866         */
867        public <T extends Page> void executeTest(final Class<?> testClass, final Class<T> pageClass,
868                final String filename) throws Exception
869        {
870                log.info("=== " + pageClass.getName() + " ===");
871
872                startPage(pageClass);
873                assertRenderedPage(pageClass);
874                assertResultPage(testClass, filename);
875        }
876
877        /**
878         * Use <code>-Dwicket.replace.expected.results=true</code> to automatically replace the expected
879         * output file.
880         *
881         * @param testClass
882         * @param page
883         * @param filename
884         * @throws Exception
885         */
886        public void executeTest(final Class<?> testClass, final Page page, final String filename)
887                throws Exception
888        {
889                log.info("=== " + page.getClass().getName() + " ===");
890
891                startPage(page);
892                assertRenderedPage(page.getClass());
893                assertResultPage(testClass, filename);
894        }
895
896        /**
897         * Use <code>-Dwicket.replace.expected.results=true</code> to automatically replace the expected
898         * output file.
899         *
900         * @param testClass
901         * @param component
902         * @param filename
903         * @throws Exception
904         */
905        public void executeTest(final Class<?> testClass, final Component component,
906                final String filename) throws Exception
907        {
908                log.info("=== " + component.getClass().getName() + " ===");
909
910                startComponentInPage(component);
911                assertResultPage(testClass, filename);
912        }
913
914        /**
915         * Use <code>-Dwicket.replace.expected.results=true</code> to automatically replace the expected
916         * output file.
917         *
918         * @param <T>
919         * @param testClass
920         * @param pageClass
921         * @param parameters
922         * @param filename
923         * @throws Exception
924         */
925        public <T extends Page> void executeTest(final Class<?> testClass, final Class<T> pageClass,
926                PageParameters parameters, final String filename) throws Exception
927        {
928                log.info("=== " + pageClass.getName() + " ===");
929
930                startPage(pageClass, parameters);
931                assertRenderedPage(pageClass);
932                assertResultPage(testClass, filename);
933        }
934
935        /**
936         *
937         * @param testClass
938         * @param component
939         * @param filename
940         * @throws Exception
941         */
942        public void executeListener(final Class<?> testClass, final Component component,
943                final String filename) throws Exception
944        {
945                assertNotNull(component);
946
947                log.info("=== " + testClass.getName() + " : " + component.getPageRelativePath() + " ===");
948
949                executeListener(component);
950                assertResultPage(testClass, filename);
951        }
952
953        /**
954         *
955         * @param testClass
956         * @param behavior
957         * @param filename
958         * @throws Exception
959         */
960        public void executeBehavior(final Class<?> testClass, final AbstractAjaxBehavior behavior,
961                final String filename) throws Exception
962        {
963                assertNotNull(behavior);
964
965                log.info("=== " + testClass.getName() + " : " + behavior.toString() + " ===");
966
967                executeBehavior(behavior);
968                assertResultPage(testClass, filename);
969        }
970
971        /**
972         * Assert that the last request redirected to the given Url.
973         *
974         * @param expectedRedirectUrl
975         *            expected
976         */
977        public void assertRedirectUrl(String expectedRedirectUrl)
978        {
979                String actualRedirectUrl = getLastResponse().getRedirectLocation();
980                assertEquals(expectedRedirectUrl, actualRedirectUrl);
981        }
982}