Package org.apache.tapestry.test

Source Code of org.apache.tapestry.test.PageTester

// Copyright 2006, 2007 The Apache Software Foundation
//
// 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 org.apache.tapestry.test;

import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
import static org.apache.tapestry.ioc.internal.util.Defense.notNull;

import java.util.Locale;
import java.util.Map;

import org.apache.tapestry.dom.Document;
import org.apache.tapestry.dom.Element;
import org.apache.tapestry.dom.Node;
import org.apache.tapestry.internal.InternalConstants;
import org.apache.tapestry.internal.SingleKeySymbolProvider;
import org.apache.tapestry.internal.TapestryAppInitializer;
import org.apache.tapestry.internal.services.ActionLinkTarget;
import org.apache.tapestry.internal.services.ComponentInvocation;
import org.apache.tapestry.internal.services.ComponentInvocationMap;
import org.apache.tapestry.internal.services.LocalizationSetter;
import org.apache.tapestry.internal.services.PageLinkTarget;
import org.apache.tapestry.internal.test.ActionLinkInvoker;
import org.apache.tapestry.internal.test.ComponentInvoker;
import org.apache.tapestry.internal.test.PageLinkInvoker;
import org.apache.tapestry.internal.test.PageTesterContext;
import org.apache.tapestry.internal.test.PageTesterModule;
import org.apache.tapestry.internal.test.TestableRequest;
import org.apache.tapestry.ioc.Registry;
import org.apache.tapestry.ioc.services.SymbolProvider;
import org.apache.tapestry.ioc.util.StrategyRegistry;
import org.apache.tapestry.services.ApplicationGlobals;

/**
* This class is used to run a Tapestry app in an in-process testing environment. You can ask it to
* render a certain page and check the DOM object created. You can also ask it to click on a link
* element in the DOM object to get the next page. Because no servlet container is required, it is
* very fast and you can directly debug into your code in your IDE.
*/
public class PageTester implements ComponentInvoker
{
    private final Registry _registry;

    private final ComponentInvocationMap _invocationMap;

    private final TestableRequest _request;

    private final StrategyRegistry<ComponentInvoker> _invokerRegistry;

    private Locale _preferedLanguage;

    private final LocalizationSetter _localizationSetter;

    public static final String DEFAULT_CONTEXT_PATH = "src/main/webapp";

    private final String _contextPath;

    private static final String DEFAULT_SUBMIT_VALUE_ATTRIBUTE = "Submit Query";

    /**
     * Initializes a PageTester without overriding any services and assuming that the context root
     * is in src/main/webapp.
     *
     * @see #PageTester(String, String, String, Map)
     */
    public PageTester(String appPackage, String appName)
    {
        this(appPackage, appName, DEFAULT_CONTEXT_PATH);
    }

    /**
     * Initializes a PageTester that acts as a browser and a servlet container to test drive your
     * Tapestry pages.
     *
     * @param appPackage
     *            The same value you would specify using the tapestry.app-package context parameter.
     *            As this testing environment is not run in a servlet container, you need to specify
     *            it.
     * @param appName
     *            The same value you would specify as the filter name. It is used to form the name
     *            of the module builder for your app. If you don't have one, pass an empty string.
     * @param contextPath
     *            The path to the context root so that Tapestry can find the templates (if they're
     *            put there).
     * @param modulesClasses
     *            Classes of additional modules to load
     */
    public PageTester(String appPackage, String appName, String contextPath, Class... moduleClasses)
    {
        _preferedLanguage = Locale.ENGLISH;
        _contextPath = contextPath;

        SymbolProvider provider = new SingleKeySymbolProvider(
                InternalConstants.TAPESTRY_APP_PACKAGE_PARAM, appPackage);

        TapestryAppInitializer initializer = new TapestryAppInitializer(provider, appName,
                PageTesterModule.TEST_MODE);

        initializer.addModules(PageTesterModule.class);
        initializer.addModules(moduleClasses);
       
        _registry = initializer.getRegistry();

        _request = _registry.getObject(TestableRequest.class, null);

        _localizationSetter = _registry.getService("LocalizationSetter", LocalizationSetter.class);

        _invocationMap = _registry.getObject(ComponentInvocationMap.class, null);

        ApplicationGlobals globals = _registry.getObject(ApplicationGlobals.class, null);

        globals.store(new PageTesterContext(_contextPath));

        Map<Class, ComponentInvoker> map = newMap();
        map.put(PageLinkTarget.class, new PageLinkInvoker(_registry));
        map.put(ActionLinkTarget.class, new ActionLinkInvoker(_registry, this, _invocationMap));

        _invokerRegistry = new StrategyRegistry<ComponentInvoker>(ComponentInvoker.class, map);
    }

    /** You should call it after use */
    public void shutdown()
    {
        _registry.shutdown();
    }

    /**
     * Renders a page specified by its name.
     *
     * @param pageName
     *            The name of the page to be rendered.
     * @return The DOM created. Typically you will assert against it.
     */
    public Document renderPage(String pageName)
    {
        return invoke(new ComponentInvocation(new PageLinkTarget(pageName), new String[0], null));
    }

    /**
     * Simulates a click on a link.
     *
     * @param link
     *            The Link object to be "clicked" on.
     * @return The DOM created. Typically you will assert against it.
     */
    public Document clickLink(Element link)
    {
        notNull(link, "link");

        ComponentInvocation invocation = getInvocation(link);

        return invoke(invocation);
    }

    private ComponentInvocation getInvocation(Element element)
    {
        ComponentInvocation invocation = _invocationMap.get(element);

        if (invocation == null)
            throw new IllegalArgumentException(
                    "No component invocation object is associated with the Element.");

        return invocation;
    }

    public Document invoke(ComponentInvocation invocation)
    {
        // It is critical to clear the map before invoking an invocation (render a page or click a
        // link).
        _invocationMap.clear();

        setThreadLocale();

        ComponentInvoker invoker = _invokerRegistry.getByInstance(invocation.getTarget());

        return invoker.invoke(invocation);
    }

    private void setThreadLocale()
    {
        _localizationSetter.setThreadLocale(_preferedLanguage);
    }

    /**
     * Simulates a submission of the form specified. The caller can specify values for the form
     * fields.
     *
     * @param form
     *            the form to be submitted.
     * @param parameters
     *            the query parameter name/value pairs
     * @return The DOM created. Typically you will assert against it.
     */
    public Document submitForm(Element form, Map<String, String> parameters)
    {
        notNull(form, "form");

        _request.clear();

        _request.loadParameters(parameters);

        addHiddenFormFields(form);

        ComponentInvocation invocation = getInvocation(form);

        return invoke(invocation);
    }

    /**
     * Simulates a submission of the form by clicking the specified submit button. The caller can
     * specify values for the form fields.
     *
     * @param submitButton
     *            the submit button to be clicked.
     * @param fieldValues
     *            the field values keyed on field names.
     * @return The DOM created. Typically you will assert against it.
     */
    public Document clickSubmit(Element submitButton, Map<String, String> fieldValues)
    {
        notNull(submitButton, "submitButton");

        assertIsSubmit(submitButton);

        Element form = getFormAncestor(submitButton);
        String value = submitButton.getAttribute("value");

        if (value == null) value = DEFAULT_SUBMIT_VALUE_ATTRIBUTE;

        fieldValues.put(submitButton.getAttribute("name"), value);

        return submitForm(form, fieldValues);
    }

    private void assertIsSubmit(Element element)
    {
        if (element.getName().equals("input"))
        {
            String type = element.getAttribute("type");

            if ("submit".equals(type)) return;
        }

        throw new IllegalArgumentException("The specified element is not a submit button.");
    }

    private Element getFormAncestor(Element element)
    {
        while (true)
        {
            if (element == null)
                throw new IllegalArgumentException("The given element is not contained by a form.");

            if (element.getName().equalsIgnoreCase("form")) return element;

            element = element.getParent();
        }
    }

    private void addHiddenFormFields(Element element)
    {
        if (isHiddenFormField(element))
            _request.loadParameter(element.getAttribute("name"), element.getAttribute("value"));

        for (Node child : element.getChildren())
        {
            if (child instanceof Element)
            {
                addHiddenFormFields((Element) child);
            }
        }
    }

    private boolean isHiddenFormField(Element element)
    {
        return element.getName().equalsIgnoreCase("input")
                && "hidden".equalsIgnoreCase(element.getAttribute("type"));
    }

    public void setPreferedLanguage(Locale preferedLanguage)
    {
        _preferedLanguage = preferedLanguage;
    }
}
TOP

Related Classes of org.apache.tapestry.test.PageTester

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.