/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package jdave.wicket;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jdave.IContainment;
import jdave.Specification;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.markup.IMarkupResourceStreamProvider;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.border.Border;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.StringResourceStream;
import org.apache.wicket.util.tester.BaseWicketTester;
import org.apache.wicket.util.tester.ITestPageSource;
import org.apache.wicket.util.tester.TestPanelSource;
import org.apache.wicket.util.tester.BaseWicketTester.DummyWebApplication;
/**
* A base class for Wicket's Component specifications.
*
* @author Joni Freeman
* @author Timo Rantalaiho
*/
public abstract class ComponentSpecification extends Specification {
protected BaseWicketTester wicket;
protected C specifiedComponent;
@Override
public final void create() {
wicket = newWicketTester();
onCreate();
}
/**
* Called after create(). No need to call super.onCreate().
*/
protected void onCreate() {
}
/**
* Start component for context.
*/
public C startComponent() {
return startComponent(null);
}
/**
* Start component for context.
*
* @param model The model passed to component that is used for context.
*/
public C startComponent(final IModel model) {
final Class> type = extractComponentType();
if (Page.class.isAssignableFrom(type)) {
startPage(model);
} else if (Panel.class.isAssignableFrom(type)) {
startPanel(model);
} else if (Border.class.isAssignableFrom(type)) {
startBorder(model);
} else {
startComponentWithoutMarkup(model);
}
return specifiedComponent;
}
private Class> extractComponentType() {
final ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
Class> type;
if (superclass.getActualTypeArguments()[0] instanceof Class>) {
type = (Class>) superclass.getActualTypeArguments()[0];
} else {
type = (Class>) ((ParameterizedType) superclass.getActualTypeArguments()[0])
.getRawType();
}
return type;
}
/**
* Start component for context, using page with given markup.
*
* @param model The model passed to component that is used for context.
* @param pageMarkup Markup (as java.lang.CharSequence) of
* the page created to host the specified component.
* @param rootComponentId Wicket id of the root component in the component
* markup hierarchy given by the pageMarkup
* parameter. This is the id of the component returned from
* newComponent method and used as context, often
* a MarkupContainer or Form.
*/
public C startComponent(final IModel model, final CharSequence pageMarkup,
final String rootComponentId) {
return startComponent(model, new StringResourceStream(pageMarkup), rootComponentId);
}
/**
* Start component for context, using page with given markup.
*
* @param model The model passed to component that is used for context.
* @param pageMarkup Markup (as
* org.apache.wicket.util.resource.IResourceStream
* ) of the page created to host the specified component.
* @param rootComponentId Wicket id of the root component in the component
* markup hierarchy given by the pageMarkup
* parameter. This is the id of the component returned from
* newComponent method and used as context, often
* a MarkupContainer or Form.
*/
public C startComponent(final IModel model, final IResourceStream pageMarkup,
final String rootComponentId) {
final WebPage page = new TestPage(pageMarkup);
specifiedComponent = newComponent(rootComponentId, model);
page.add(specifiedComponent);
wicket.startPage(page);
return specifiedComponent;
}
/**
* Start form for context, using given markup as form markup.
*
* @param model The model passed to newComponent method.
* @param formMarkup Markup (as java.lang.CharSequence) of
* the form returned from newComponent method,
* excluding the <form> tag.
*/
public C startForm(final IModel model, final CharSequence formMarkup) {
ensureComponentIsAForm();
return startComponent(model, new StringBuilder().append(
"").toString(), "form");
}
private void ensureComponentIsAForm() {
if (!Form.class.isAssignableFrom(extractComponentType())) {
throw new IllegalArgumentException(
"Your ComponentSpecification must be typed as extends Form>> if you want to start a form. Instead you are using "
+ extractComponentType().getName());
}
}
/**
* Start component for context.
*
* The markup file of a component is not needed.
*
* @param model The model passed to component that is used for context.
*/
public C startComponentWithoutMarkup(final IModel model) {
specifiedComponent = newComponent("component", model);
wicket.startComponent(specifiedComponent);
return specifiedComponent;
}
protected void startBorder(final IModel model) {
wicket.startPanel(new TestPanelSource() {
public Panel getTestPanel(final String panelId) {
final Panel panel = new Container(panelId);
specifiedComponent = newComponent("component", model);
panel.add(specifiedComponent);
return panel;
}
});
}
protected void startPanel(final IModel model) {
wicket.startPanel(new TestPanelSource() {
public Panel getTestPanel(final String panelId) {
specifiedComponent = newComponent(panelId, model);
return (Panel) specifiedComponent;
}
});
}
protected void startPage(final IModel model) {
specifiedComponent = newComponent(null, model);
final TestPageSource testPageSource = new TestPageSource((Page) specifiedComponent);
wicket.startPage(testPageSource);
}
@SuppressWarnings( { "unchecked" })
protected IModel cast(final IModel> model) {
return (IModel) model;
}
private static class TestPageSource implements ITestPageSource {
private final Page page;
public TestPageSource(final Page page) {
this.page = page;
}
public Page getTestPage() {
return page;
}
}
private static class TestPage extends WebPage implements IMarkupResourceStreamProvider {
private final IResourceStream markup;
public TestPage(final IResourceStream markup) {
this.markup = markup;
}
public IResourceStream getMarkupResourceStream(final MarkupContainer container,
final Class> containerClass) {
return markup;
}
}
/**
* Specify that given container contains given model objects.
*
* This is most often used with RefreshingViews and
* ListViews.
*
*
*
* @param actual the container of Wicket components
* @param containment any containment, see:
* http://www.jdave.org/documentation.html#containments
*/
public void specify(final MarkupContainer actual, final IContainment containment) {
super.specify(modelObjects(actual.iterator()), containment);
}
/**
* Select an item from a RepeatingView.
*/
@SuppressWarnings("unchecked")
public Item itemAt(final RepeatingView view, final int index) {
final Iterator extends Component> items = view.iterator();
for (int i = 0; i < index; i++) {
items.next();
}
return (Item) items.next();
}
/**
* Select an item from a ListView.
*/
public ListItem itemAt(final ListView view, final int index) {
final Iterator extends ListItem> items = view.iterator();
for (int i = 0; i < index; i++) {
items.next();
}
return items.next();
}
/**
* Collect model objects from given components.
*/
public List> modelObjects(final Iterator extends Component> components) {
final List