/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.renderkit; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.same; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.text.MessageFormat; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.el.ELContext; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.component.behavior.ClientBehavior; import javax.faces.component.behavior.ClientBehaviorContext; import javax.faces.component.behavior.ClientBehaviorHint; import javax.faces.component.behavior.ClientBehaviorHolder; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.easymock.EasyMock; import org.jboss.test.faces.mock.MockFacesEnvironment; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google.common.collect.Maps; /** * @author Nick Belaevski * */ public class RenderKitUtilsMocksTest { /** * */ private static final String CLIENT_ID = "submittedId"; private static final String FACET_NAME = "facetName"; private MockFacesEnvironment facesEnvironment; private ResponseWriter responseWriter; private FacesContext facesContext; private ExternalContext externalContext; private Map<String, Object> componentAttributes; private Map<String, List<ClientBehavior>> behaviorsMap; private Map<String, ComponentAttribute> knownAttributes; @Before public void setUp() throws Exception { facesEnvironment = MockFacesEnvironment.createEnvironment().withExternalContext(); facesContext = facesEnvironment.getFacesContext(); externalContext = facesEnvironment.getExternalContext(); responseWriter = facesEnvironment.createMock(ResponseWriter.class); expect(facesContext.getResponseWriter()).andStubReturn(responseWriter); expect(responseWriter.getContentType()).andStubReturn("application/xhtml+xml"); componentAttributes = new HashMap<String, Object>(); behaviorsMap = new HashMap<String, List<ClientBehavior>>(); knownAttributes = new TreeMap<String, ComponentAttribute>(); } @After public void tearDown() throws Exception { this.facesEnvironment.verify(); this.facesEnvironment.release(); this.facesEnvironment = null; this.responseWriter = null; this.facesContext = null; this.externalContext = null; this.componentAttributes = null; this.behaviorsMap = null; this.knownAttributes = null; } private UIComponent createMockComponent() { UIComponent component = facesEnvironment.createMock(UIComponent.class); expect(component.getAttributes()).andStubReturn(componentAttributes); expect(component.getClientId(same(facesContext))).andStubReturn(CLIENT_ID); return component; } private ClientBehaviorHolder createMockClientBehaviorHolder() { UIComponent component = facesEnvironment.createMock(AbstractClientBehaviorHolderComponent.class); expect(component.getClientId(same(facesContext))).andStubReturn(CLIENT_ID); expect(component.getAttributes()).andStubReturn(componentAttributes); ClientBehaviorHolder behaviorHolder = (ClientBehaviorHolder) component; expect(behaviorHolder.getClientBehaviors()).andStubReturn(behaviorsMap); expect(behaviorHolder.getEventNames()).andStubReturn( Arrays.asList("click", "action", "mousemove", "keypress", "blur", "contextmenu")); return behaviorHolder; } @Test public void testRenderPassThroughAttributes() throws Exception { knownAttributes.put("disabled", new ComponentAttribute("disabled")); knownAttributes.put("checked", new ComponentAttribute("checked")); knownAttributes.put("style", new ComponentAttribute("style")); knownAttributes.put("src", new ComponentAttribute("src")); knownAttributes.put("lang", new ComponentAttribute("lang")); knownAttributes.put("class", new ComponentAttribute("class").setComponentAttributeName("styleClass")); componentAttributes.put("disabled", Boolean.TRUE); componentAttributes.put("checked", Boolean.FALSE); componentAttributes.put("style", "color:red"); componentAttributes.put("src", "urn:abc"); componentAttributes.put("facelets.Mark", 123); componentAttributes.put("lang", "ru"); componentAttributes.put("styleClass", "rich-component"); UIComponent component = createMockComponent(); responseWriter.writeAttribute(eq("disabled"), eq(Boolean.TRUE), EasyMock.<String>isNull()); // checked attribute shouldn't be rendered - it's 'false' responseWriter.writeAttribute(eq("style"), eq("color:red"), EasyMock.<String>isNull()); responseWriter.writeURIAttribute(eq("src"), eq("urn:abc"), EasyMock.<String>isNull()); // facelets.Mark shouldn't be rendered - it's unknown responseWriter.writeAttribute(eq("xml:lang"), eq("ru"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("class"), eq("rich-component"), EasyMock.<String>isNull()); facesEnvironment.replay(); RenderKitUtils.renderPassThroughAttributes(facesContext, component, knownAttributes); } private ClientBehavior createClientBehavior(String handlerData, Set<ClientBehaviorHint> hints) { ClientBehavior behavior = facesEnvironment.createMock(ClientBehavior.class); expect(behavior.getScript(EasyMock.<ClientBehaviorContext>notNull())).andStubReturn( MessageFormat.format("prompt({0})", handlerData)); expect(behavior.getHints()).andStubReturn(hints); return behavior; } @Test public void testBehaviors() throws Exception { knownAttributes.put("onclick", new ComponentAttribute("onclick").setEventNames(new String[] { "click", "action" })); knownAttributes.put("onmousemove", new ComponentAttribute("onmousemove").setEventNames(new String[] { "mousemove" })); knownAttributes.put("onkeypress", new ComponentAttribute("onkeypress").setEventNames(new String[] { "keypress" })); knownAttributes.put("oncontextmenu", new ComponentAttribute("oncontextmenu").setEventNames(new String[] { "contextmenu" })); componentAttributes.put("onkeypress", "alert(keypress)"); componentAttributes.put("onmousemove", "alert(mousemove)"); componentAttributes.put("onclick", "alert(click)"); Set<ClientBehaviorHint> emptyHintsSet = EnumSet.noneOf(ClientBehaviorHint.class); Set<ClientBehaviorHint> submittingHintsSet = EnumSet.of(ClientBehaviorHint.SUBMITTING); ClientBehavior keypressBehavior = createClientBehavior("keypress", emptyHintsSet); ClientBehavior actionBehavior1 = createClientBehavior("action1", emptyHintsSet); ClientBehavior actionBehavior2 = createClientBehavior("action2", submittingHintsSet); ClientBehavior actionBehavior3 = createClientBehavior("action3", emptyHintsSet); ClientBehavior contextmenuBehavior = createClientBehavior("contextmenu", emptyHintsSet); behaviorsMap.put("keypress", Arrays.asList(keypressBehavior)); behaviorsMap.put("action", Arrays.asList(actionBehavior1, actionBehavior2, actionBehavior3)); behaviorsMap.put("contextmenu", Arrays.asList(contextmenuBehavior)); ClientBehaviorHolder behaviorHolder = createMockClientBehaviorHolder(); UIComponent component = (UIComponent) behaviorHolder; responseWriter.writeAttribute(eq("onkeypress"), eq("return jsf.util.chain(this, event, 'alert(keypress)','prompt(keypress)')"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("onclick"), eq("return jsf.util.chain(this, event, 'alert(click)','prompt(action1)','prompt(action2)')"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("onmousemove"), eq("alert(mousemove)"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("oncontextmenu"), eq("prompt(contextmenu)"), EasyMock.<String>isNull()); facesEnvironment.replay(); RenderKitUtils.renderPassThroughAttributes(facesContext, component, knownAttributes); } private UIComponent setupBehaviorsTestForDisabledComponent() throws IOException { knownAttributes.put("style", new ComponentAttribute("style")); knownAttributes.put("onclick", new ComponentAttribute("onclick").setEventNames(new String[] { "click", "action" })); knownAttributes.put("onmousemove", new ComponentAttribute("onmousemove").setEventNames(new String[] { "mousemove" })); componentAttributes.put("onmousemove", "alert(mousemove)"); componentAttributes.put("onclick", "alert(click)"); componentAttributes.put("style", "color:green"); Set<ClientBehaviorHint> emptyHintsSet = EnumSet.noneOf(ClientBehaviorHint.class); ClientBehavior actionBehavior1 = createClientBehavior("action1", emptyHintsSet); behaviorsMap.put("action", Arrays.asList(actionBehavior1)); ClientBehaviorHolder behaviorHolder = createMockClientBehaviorHolder(); UIComponent component = (UIComponent) behaviorHolder; return component; } @Test public void testBehaviorsForDisabledComponent() throws Exception { componentAttributes.put("disabled", Boolean.TRUE); UIComponent component = setupBehaviorsTestForDisabledComponent(); responseWriter.writeAttribute(eq("style"), eq("color:green"), EasyMock.<String>isNull()); facesEnvironment.replay(); RenderKitUtils.renderPassThroughAttributes(facesContext, component, knownAttributes); } @Test public void testBehaviorsForNonDisabledComponent() throws Exception { componentAttributes.put("disabled", Boolean.FALSE); UIComponent component = setupBehaviorsTestForDisabledComponent(); responseWriter.writeAttribute(eq("onclick"), eq("return jsf.util.chain(this, event, 'alert(click)','prompt(action1)')"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("onmousemove"), eq("alert(mousemove)"), EasyMock.<String>isNull()); responseWriter.writeAttribute(eq("style"), eq("color:green"), EasyMock.<String>isNull()); facesEnvironment.replay(); RenderKitUtils.renderPassThroughAttributes(facesContext, component, knownAttributes); } public void checkDisabled(Object attributeValue, boolean expectedValue) throws Exception { componentAttributes.put("disabled", attributeValue); UIComponent component = createMockComponent(); facesEnvironment.replay(); assertTrue(expectedValue == RenderKitUtils.isDisabled(component)); } @Test public void testIsDisabled() throws Exception { checkDisabled(Boolean.TRUE, true); } @Test public void testIsDisabledString() throws Exception { checkDisabled("true", true); } @Test public void testIsNonDisabled() throws Exception { checkDisabled(Boolean.FALSE, false); } @Test public void testIsNonDisabledString() throws Exception { checkDisabled("false", false); } @Test public void testIsNonDisabledNull() throws Exception { checkDisabled(null, false); } private UIComponent setupTestDecodeBehaviors(String behaviorSourceId, String behaviorEventName) throws Exception { ClientBehaviorHolder behaviorHolder = createMockClientBehaviorHolder(); UIComponent component = (UIComponent) behaviorHolder; Map<String, String> requestParameterMap = new HashMap<String, String>(); requestParameterMap.put(RenderKitUtils.BEHAVIOR_SOURCE_ID, behaviorSourceId); requestParameterMap.put(RenderKitUtils.BEHAVIOR_EVENT_NAME, behaviorEventName); expect(externalContext.getRequestParameterMap()).andStubReturn(requestParameterMap); ClientBehavior actionBehavior = createClientBehavior("action1", EnumSet.of(ClientBehaviorHint.SUBMITTING)); ClientBehavior actionBehavior1 = createClientBehavior("action2", EnumSet.of(ClientBehaviorHint.SUBMITTING)); behaviorsMap.put("action", Arrays.asList(actionBehavior, actionBehavior1)); ClientBehavior blurBehavior = createClientBehavior("blur1", EnumSet.of(ClientBehaviorHint.SUBMITTING)); behaviorsMap.put("blur", Arrays.asList(blurBehavior)); return component; } @Test public void testDecodeActionBehaviors() throws Exception { UIComponent component = setupTestDecodeBehaviors(CLIENT_ID, "action"); List<ClientBehavior> behaviors = behaviorsMap.get("action"); for (ClientBehavior clientBehavior : behaviors) { clientBehavior.decode(same(facesContext), same(component)); expectLastCall(); } facesEnvironment.replay(); assertEquals("action", RenderKitUtils.decodeBehaviors(facesContext, component)); } @Test public void testDecodeBlurBehaviors() throws Exception { UIComponent component = setupTestDecodeBehaviors(CLIENT_ID, "blur"); List<ClientBehavior> behaviors = behaviorsMap.get("blur"); for (ClientBehavior clientBehavior : behaviors) { clientBehavior.decode(same(facesContext), same(component)); expectLastCall(); } facesEnvironment.replay(); assertEquals("blur", RenderKitUtils.decodeBehaviors(facesContext, component)); } @Test public void testDecodeNonMatchingClientId() throws Exception { UIComponent component = setupTestDecodeBehaviors("wrongId", "action"); // nothing should be called - clientId is not matched facesEnvironment.replay(); assertNull(RenderKitUtils.decodeBehaviors(facesContext, component)); } @Test public void testDecodeNoSubmittedBehavior() throws Exception { UIComponent component = setupTestDecodeBehaviors(CLIENT_ID, null); // nothing should be called - no behavior event information was submitted facesEnvironment.replay(); assertNull(RenderKitUtils.decodeBehaviors(facesContext, component)); } @Test public void testDecodeContextMenuBehaviors() throws Exception { UIComponent component = setupTestDecodeBehaviors(CLIENT_ID, "contextmenu"); // nothing should be called - no context menu behaviors were created facesEnvironment.replay(); assertNull(RenderKitUtils.decodeBehaviors(facesContext, component)); } @Test public void when_component_has_defined_facet_and_it_is_rendered_then_hasFacet_should_return_true() { // given UIComponent component = createMockComponent(); UIComponent facetComponent = createMockComponent(); expect(component.getFacet(FACET_NAME)).andReturn(facetComponent).times(2); expect(facetComponent.isRendered()).andReturn(true); facesEnvironment.replay(); // when boolean hasFacet = RenderKitUtils.hasFacet(component, FACET_NAME); // then assertTrue("hasFacet should return true", hasFacet); } @Test public void when_component_has_defined_facet_but_it_is_not_rendered_then_hasFacet_should_return_false() { // given UIComponent component = createMockComponent(); UIComponent facetComponent = createMockComponent(); expect(component.getFacet(FACET_NAME)).andReturn(facetComponent).times(2); expect(facetComponent.isRendered()).andReturn(false); facesEnvironment.replay(); // when boolean hasFacet = RenderKitUtils.hasFacet(component, FACET_NAME); // then assertFalse("hasFacet should return false", hasFacet); } @Test public void when_component_has_not_defined_facet_then_hasFacet_should_return_false() { // given UIComponent component = createMockComponent(); expect(component.getFacet(FACET_NAME)).andReturn(null); facesEnvironment.replay(); // when boolean hasFacet = RenderKitUtils.hasFacet(component, FACET_NAME); // then assertFalse("hasFacet should return false", hasFacet); } @Test public void when_attribute_value_expression_is_null_then_evaluateAttribute_should_return_attribute_value() { // given Map<String, Object> attributes = Maps.newTreeMap(); Object value = new Object(); attributes.put("value", value); UIComponent component = createMockComponent(); expect(component.getAttributes()).andReturn(attributes); expect(component.getValueExpression("value")).andReturn(null); facesEnvironment.replay(); // when Object evaluated = RenderKitUtils.evaluateAttribute("value", component, facesContext); // then assertEquals(value, evaluated); } @Test public void when_attribute_value_expression_evaluates_to_null_then_evaluateAttribute_should_return_attribute_value() { // given Map<String, Object> attributes = Maps.newTreeMap(); Object value = new Object(); attributes.put("value", value); ELContext elContext = facesEnvironment.getElContext(); ValueExpression valueExpression = facesEnvironment.createMock(ValueExpression.class); UIComponent component = createMockComponent(); expect(facesContext.getELContext()).andReturn(elContext); expect(component.getValueExpression("value")).andReturn(valueExpression); expect(valueExpression.getValue(elContext)).andReturn(null); expect(component.getAttributes()).andReturn(attributes); facesEnvironment.replay(); // when Object evaluated = RenderKitUtils.evaluateAttribute("value", component, facesContext); // then assertEquals(value, evaluated); } @Test public void when_attribute_value_expression_evaluates_to_some_value_then_evaluateAttribute_the_value_is_returned() { // given Map<String, Object> attributes = Maps.newTreeMap(); Object attributeValue = new Object(); Object valueExpressionValue = new Object(); attributes.put("value", attributeValue); ELContext elContext = facesEnvironment.getElContext(); ValueExpression valueExpression = facesEnvironment.createMock(ValueExpression.class); UIComponent component = createMockComponent(); expect(facesContext.getELContext()).andReturn(elContext); expect(component.getValueExpression("value")).andReturn(valueExpression); expect(valueExpression.getValue(elContext)).andReturn(valueExpressionValue); facesEnvironment.replay(); // when Object evaluated = RenderKitUtils.evaluateAttribute("value", component, facesContext); // then assertEquals(valueExpressionValue, evaluated); } public abstract static class AbstractClientBehaviorHolderComponent extends UIComponent implements ClientBehaviorHolder { } }