Adding a Component to Metamer

Version 8

    This tutorial shows how to add a new component to the Metamer, a test application for RichFaces 4. It will add standard JSF command button with all its attributes. All other components can be added in a similar way. Tutorial shows how to modify front page, list of component pages and how to add a simple page with command button and all its attributes. There will be one input text, command button for submitting form and three outputs. The first output will be the content of the input, the second will be the input processed by selected action and the third will be the input processed by selected action listener.

    Front page

    When a user deploys and lauches test application, a list of all available components is displayed. To add command button to this list, add command button's identifier and description to the class org.richfaces.tests.metamer.bean.RichBean. Put the following into method createComponentsMap:
        components.put("commandButton", "JSF Command Button");
    commandButton is component's identifier (componentId) that will be used in many places later.

    Managed bean

    Create a new class called CommandButtonBean in package org.richfaces.tests.metamer.bean and set bean's name and scope. It is important to create a property of type org.richfaces.testapp.Attributes and its getter and setter method. Initialize it in this way:
    @PostConstruct
    public void init() {
        logger = LoggerFactory.getLogger(getClass());
        logger.info("initializing bean " + getClass().getName());
        
        // for non-RichFaces components
        attributes = Attributes.getComponentAttributesFromClass(javax.faces.component.html.HtmlCommandButton.class, getClass());
        
        // for non-RichFaces behaviors
        // attributes = Attributes.getBehaviorAttributesFromClass(javax.faces.component.behavior.AjaxBehavior.class, getClass());
        // for RichFaces components
        // attributes = Attributes.getComponentAttributesFromFacesConfig(org.richfaces.component.UIAccordion.class, getClass());
        // for RichFaces behaviors
        // attributes = Attributes.getBehaviorAttributesFromFacesConfig(org.ajax4jsf.component.behavior.AjaxBehavior.class, getClass());   
    }
    

     

    This will create a map of all command button's attributes where key in the map is name of an attribute and value is a java bean storing information about an attribute, i.e. it's name, value, type and help and select options used on the page.

    Actions and Action Listeners

    You can create as many actions and action listeners as you want. Since the application doesn't support Expression Language 2.2, actions and action listeners have to have the following signature:
    public String myAction() { ... }
    
    public void myActionListener(ActionEvent event) { ... }
    

     

    Since the actions and action listeners usually won't be called directly, their names have to end with Action or ActionListener.

     

    List of pages

    The test application is designed to contain many various pages for one component. After the user selects a component on the front page, a list of all available pages for given component is displayed. This might be a simple page (will be created in this tutorial), internationalized component, component in a special container, etc. This file has to be named components/<componentId>/list.xhtml, e.g. components/commandButton/list.xhtml. The easiest way is to copy an existing list page which will guarantee that all pages look the same.

    Test page with attributes

    Now a new page with command button will be created. To do so, create a new page in directory components/<componentId>,
    e.g. components/commandButton/simple.xhtml. This is the skeleton of the page:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" 
          xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" 
          xmlns:a4j="http://richfaces.org/a4j" xmlns:ta="http://java.sun.com/jsf/composite/testapp">
     
        
        <ui:composition template="/templates/template.xhtml">
            <ui:define name="view"/>
            <ui:define name="head"/>
            <ui:define name="outOfTemplateBefore"/>
            <ui:define name="component"/>
            <ui:define name="outOfTemplateAfter"/>
        </ui:composition>
     
    </html>
    
    • view - will be put inside f:view but outside h:head and h:body, e.g. f:metadata should be put here
    • head - javascrip, css and metadata
    • component - component will be placed here
    • outOfTemplateBefore - everything what should be placed above component
    • outOfTemplateAfter - everything what should be placed beneath componet

     

    View parameters

    In order to make writing Selenium tests easier, each page should define view parameters. Unfortunately, it is not possible to define them in template so they have to be defined on each page separately.
    <ui:define name="view">
        <f:metadata>
            <f:viewParam name="templates" value="#{templateBean.templates}">
                <f:converter converterId="templatesListConverter" />
            </f:viewParam>
        </f:metadata>
    </ui:define>
    

    Component section

    This is the part of a page that contains tested component inside selected template(s). It should contain a simple test case so that many component's attributes can be tested. If it is not possible, a new page can be created that will test specific functionality. Simple page for h:commandButton might look like this:
    <ui:define name="component">
     
        <h:panelGrid id="panel" columns="2">
            <h:inputText id="input" value="#{buttonBean.input}" />
     
            <h:commandButton id="commandButton"
                action="#{buttonBean.attributes.action}"
                actionListener="#{buttonBean.attributes.actionListener}"
                ...
                execute="@form"
                immediate="#{buttonBean.attributes['immediate'].value}" />
       </h:panelGrid>
     
       
       <h:panelGrid id="outputs" columns="2">
           output: <h:outputText value="#{buttonBean.input}" id="output1" />
        </h:panelGrid>
    </ui:define>
    

     

    Notice how action and actionListener attributes are defined. Actions and action listeners aren't called directly because they can contain null, a string or a method expression. Therefore this universal approach, usable by all components, was developed. All other attributes are mapped directly, see attribute value. Sometimes it is not possible to set a value expression. In these cases the attribute should be tested in other way, e.g. on separate pages.

     

    Component section is by default wrapped in a h:form tag. In order not to render form, you need to define variable dontRenderForm in view's metadata section:

     

    <ui:composition template="/templates/template.xhtml">
        <ui:param name="dontRenderForm" value="true" />
        ...
    </ui:composition>
    

    Attributes of tested component

    Test application contains a composite component which renders input components for each attribute of tested component. Attributes usually don't have to be inserted inside templates so it can be defined outside template:
    <ui:define name="outOfTemplateAfter">
        <ta:attributes value="#{buttonBean.attributes}" id="attributes" render="panel"/>
    </ui:define>
    
    Component's standard behavior is to render a checkbox for boolean attributes and h:inputText for all other attributes. However, you have an option to render radio buttons by defining select options in component's property file. To do so, a new property file has to be created. It's name and package has to equal managed bean's name and package, e.g. org.richfaces.testapp.bean.CommandButtonBean.properties. If you want to define select options for attribute styleClass, add following to the property file:
    # attr.<name of attribute>.key=value
    attr.styleClass.red=red
    attr.styleClass.blue=blue
    attr.styleClass.big=big
    attr.styleClass.wide=wide
    attr.styleClass.none=
    
    This will generate five options on the page. If these classes are defined in a property file, button's appearance can be changed on the fly. This mechanism works for all attributes however there is a small difference for action and actionListener. These two enables user to change action and action listener in runtime. If you want to invoke actions from component's managed bean, you only need to add following to the property file (where e.g. first6CharsAction is a method's name) :
    attr.action.first6CharsAction=first6CharsAction
    attr.action.doubleStringAction=doubleStringAction
    attr.action.toUpperCaseAction=toUpperCaseAction
    
    There is also an option to render some help for attributes. To do so, add following to the property file:
    # testapp.help.<name of attribute>=value
    testapp.help.action=Help for attribute action.
    testapp.help.styleClass=Help for attribute styleClass.
    

    When defining select options, it's good to select default value in bean. For our case, put the following into CommandButtonBean:

     

    @PostConstruct
    public void init() {
        ...
        attributes.setAttribute("value", "command button");
        attributes.setAttribute("rendered", "true");
        attributes.setAttribute("render", "output1 output2 output3");
        attributes.setAttribute("action", "first6CharsAction");
        attributes.setAttribute("actionListener", "toUpperCaseActionListener");
    }
    

    Models

    All iteration components need some model. There are two models defined in managed bean Model.

    #{model.capitals}

    Simple model that should be used only if more complex model would be overhead.

     

    • String name
    • String state
    • String timeZone
    #{model.employees}

    If there is a need for testing advanced features, this model should be used. Employees' names were chosen in such a way that there are names starting with digits, unicode names in Latin and Cyrillic, some names are there many times. If data are grouped by name or title, it is alway possible to sort them or filter them and see some results.

     

    String name

     

    • suitable for testing inputs and outputs

    String title

    • suitable for testing e.g. combo box

    List<Company> companies

    • Company = { String name, String state, String phone }
    • suitable for testing iteration components

    List<String[]> phones

    • 0...type of the phone
    • type is one of {"Cell phone", "Work phone", "Home phone"}
    • 1... phone number
    • suitable for testing iteration components

    java.util.Date birthdate

    • suitable for testing calendar component and inputs/outputs with converters & validators

    java.awt.Color favoriteColor

    • suitable for testing color picker

    int numberOfKids

    • suitable for testing slider, spinner and other components showing numbers

    boolean smoker

    • suitable for testing boolean values - checkboxes, radio buttons

    Sex sex

    • Sex is an enum containing FEMALE, MALE
    • there are two images in /resources/images that can be used on pages

    String email

    • suitable for testing regexp validators

    Conclusion

    For further reference, please have a look into source code

     

    metamer.png