/* * � Copyright IBM Corp. 2012, 2014 * * 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. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 9 Nov 2012 * RenderBooleanPropertyTest.java */ package com.ibm.xsp.test.framework.render; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.registry.FacesComponentDefinition; import com.ibm.xsp.registry.FacesProperty; import com.ibm.xsp.registry.FacesSharableRegistry; import com.ibm.xsp.registry.RegistryUtil; import com.ibm.xsp.test.framework.AbstractXspTest; import com.ibm.xsp.test.framework.TestProject; import com.ibm.xsp.test.framework.XspRenderUtil; import com.ibm.xsp.test.framework.XspTestUtil; import com.ibm.xsp.test.framework.setup.SkipFileContent; import com.ibm.xsp.util.TypedUtil; /** * * @author Maire Kehoe (mkehoe@ie.ibm.com) */ public class RenderBooleanPropertyTest extends AbstractXspTest { @Override public String getDescription() { // This was extracted from RenderDojoPropertyTest 2012-11-09 return "verify that property values absent in the XPage source are absent in the HTML source, " +"and when non-default values are set in the XPage, they appear in the HTML source."; } public void testGetterDefaultMatchesOutputDefault() throws Exception { String fails= ""; FacesSharableRegistry reg = TestProject.createRegistry(this); // create an empty view FacesContext context = TestProject.createFacesContext(this); ResponseBuffer.initContext(context); UIViewRoot root = TestProject.loadEmptyPage(this, context); UIComponent p = XspRenderUtil.createContainerParagraph(root); Object[][] neverInHtmlSkips = initSkips(getNeverInHtmlSkips()); // for all definitions for (FacesComponentDefinition def : TestProject.getLibComponents(reg, this)) { if( ! def.isTag() ){ continue; } // verify rendered output as expected. String pageWithNoPropertyValueSet, pageWithDefaultPropertyValueSet, pageWithOtherPropertyValueSet; { UIComponent instance; try{ instance = newInstance(def); }catch(TestedElsewhereContinueException e){ // note, ComponentRendererTest will fail if non-instantiable, so not log here continue; } // render the un-modified blank control instance XspRenderUtil.resetContainerChild(root, p, instance); XspRenderUtil.initControl(this, instance, context); String page; try{ page = encode(p, context); }catch(TestedElsewhereContinueException e){ // note, ComponentRendererTest will fail for this, so not log here continue; } pageWithNoPropertyValueSet = page; } // new Object[]{ String propName,Boolean getterDefault } List<Object[]> booleanProps = new ArrayList<Object[]>(); for (FacesProperty prop : RegistryUtil.getProperties(def) ) { if( boolean.class.equals(prop.getJavaClass()) ){ if( ! "loaded".equals(prop.getName()) && ! "rendered".equals(prop.getName()) && ! prop.isAttribute() ){ booleanProps.add(new Object[]{prop.getName(), null}); } } } { UIComponent instance; try{ instance = (UIComponent) def.getJavaClass().newInstance(); }catch(Exception e){ e.printStackTrace(); String msg = XspTestUtil.loc(def)+ " Subsequent call to (new " +XspTestUtil.getShortClass(def.getJavaClass())+ "()) after the initial new&encode is failing: " +e; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg); continue; } // set the default value Map<String, Object> instanceAttrs = TypedUtil.getAttributes(instance); for (Object[] boolPropNameAndDefault : booleanProps) { String propName = (String) boolPropNameAndDefault[0]; Object getterDefaultObj = instanceAttrs.get(propName); if( null == getterDefaultObj ){ throw new RuntimeException("No getter method for "+XspTestUtil.loc(def) + " " + propName); } boolean getterDefault = (Boolean)getterDefaultObj; boolPropNameAndDefault[1] = getterDefault; instanceAttrs.put(propName, getterDefault); } // render the un-modified blank control instance XspRenderUtil.resetContainerChild(root, p, instance); XspRenderUtil.initControl(this, instance, context); String page; try{ page = ResponseBuffer.encode(p, context); if( ! page.startsWith("<p>") //|| page.equals("<p></p>") ){ System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+XspTestUtil.loc(def)+"\n"+page); throw new RuntimeException("Page not <p>...</p>"); } }catch(Exception e){ ResponseBuffer.clear(context); e.printStackTrace(); String msg = XspTestUtil.loc(def)+ " Subsequent call to encode, after set boolean props to default value, is failing: " +e; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg); continue; } pageWithDefaultPropertyValueSet = page; } { UIComponent instance; try{ instance = (UIComponent) def.getJavaClass().newInstance(); }catch(Exception e){ e.printStackTrace(); String msg = XspTestUtil.loc(def)+ " Further subsequent call to (new " +XspTestUtil.getShortClass(def.getJavaClass())+ "()) after 2 attempts at new&encode is failing: " +e; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg); continue; } // set the non-default value Map<String, Object> instanceAttrs = TypedUtil.getAttributes(instance); for (Object[] boolPropNameAndDefault : booleanProps) { String propName = (String) boolPropNameAndDefault[0]; boolean getterDefault = (Boolean) boolPropNameAndDefault[1]; boolean otherPossibleValue = !getterDefault; instanceAttrs.put(propName, otherPossibleValue); } // render the un-modified blank control instance XspRenderUtil.resetContainerChild(root, p, instance); XspRenderUtil.initControl(this, instance, context); String page; try{ page = ResponseBuffer.encode(p, context); if( ! page.startsWith("<p>") //|| page.equals("<p></p>") ){ System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+XspTestUtil.loc(def)+"\n"+page); throw new RuntimeException("Page not <p>...</p>"); } }catch(Exception e){ ResponseBuffer.clear(context); e.printStackTrace(); String msg = XspTestUtil.loc(def)+ " Subsequent call to encode, after set boolean props to non-default value, is failing: " +e; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg); continue; } pageWithOtherPropertyValueSet = page; } // Now that you have the 3 page outputs String[] pages = new String[]{pageWithNoPropertyValueSet, pageWithDefaultPropertyValueSet, pageWithOtherPropertyValueSet}; // for each property check whether the property is ever passed through to the HTML page. for (Object[] boolPropNameAndDefault : booleanProps) { String propName = (String) boolPropNameAndDefault[0]; boolean getterDefault = (Boolean) boolPropNameAndDefault[1]; boolean otherPossibleValue = !getterDefault; boolean foundInAny = false; String[] propertyValuesInHTML = new String[3]; int k = 0; for (String outputPage : pages) { propertyValuesInHTML[k] = XspRenderUtil.findAttribute(outputPage, propName); if( null != propertyValuesInHTML[k] ){ foundInAny = true; } k++; } if( ! foundInAny ){ if( !isMarkSkipped(neverInHtmlSkips, propName, def.getJavaClass()) ){ String msg = XspTestUtil.loc(def) + " " + propName + " Always absent in HTML when set to true or false."; // msg += " new Object[]{\"" +prop.getName()+"\", " // +XspTestUtil.getShortClass(def.getJavaClass())+".class},"; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg +"\n"+pageWithNoPropertyValueSet); } continue; } // ok, so the property is sometimes passed through to HTML. // verify the 3 different scenarios // when property value absent in XPage source, should be absent in HTML output if( null != propertyValuesInHTML[0] ){ String msg = XspTestUtil.loc(def) + " " + propName + " Wrong HTML output, expected absent (not set), but was : " + propName+"=\""+propertyValuesInHTML[1]+"\""; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg +"\n"+pageWithNoPropertyValueSet); } String expectedRenderedFirstValue = null; if( ! StringUtil.equals(expectedRenderedFirstValue, propertyValuesInHTML[1]) ){ String msg = XspTestUtil.loc(def) + " " + propName + " Wrong HTML output, expected absent (set to default), but was : " + propName+"=\""+propertyValuesInHTML[1]+"\""; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg +"\n"+pageWithDefaultPropertyValueSet); } String expectedRenderedSecondValue = ""+otherPossibleValue; if( ! StringUtil.equals(expectedRenderedSecondValue, propertyValuesInHTML[2]) ){ if( false == otherPossibleValue && null == propertyValuesInHTML[2] ){ String msg = XspTestUtil.loc(def) + " " + propName + " Wrong HTML output, expected " + propName+ "=\"false\", but was null. (Default is true) " +"[note ResponseWriter.writeAttribute(\"propName\", false, null) doesn't write output]"; fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg +"\n"+pageWithOtherPropertyValueSet); }else if( true == otherPossibleValue && propName.equals(propertyValuesInHTML[2]) ){ // Note, per http://dojotoolkit.org/reference-guide/1.8/dojo/parser.html#boolean-parameters // these: {"checked", "disabled","selected"} // are expecting just checked or checked=checked // so this shouldn't fail for those attributes and whatever others // are handled specially by the dojo parser & web browsers. // TODO how do the web browsers & dojo parser handle foo=foo and foo=false // for both HTML-native and invented boolean attributes. // Does foo=false evaluate to true? if that was so for all attributes, // there'd be no way to set property values that default to true to the value false. // So is there a hard-coded list of attributes where any value is treated as true? // Does such a list vary depending on the HTML element (INPUT etc)?. // PASS. // String msg = XspTestUtil.loc(def) + " " + propName // + " Wrong HTML output, expected " // + propName+ "=\"true\", but was " // + propName+ "=\"" +propName+"\" " // +"[note ResponseWriter.writeAttribute(\"propName\", true, null) writes propName=\"propName\"]"; // fails += msg + "\n"; // System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg // +"\n"+pageWithOtherPropertyValueSet); }else{ String msg = XspTestUtil.loc(def) + " " + propName + " Wrong HTML output, expected " + propName+ "=\"" +expectedRenderedSecondValue+"\", but was " + ((propertyValuesInHTML[2] == null) ? "null" : propName + "=\"" + propertyValuesInHTML[2] + "\""); fails += msg + "\n"; System.out.println("BooleanPropertyDefaultTest.testGetterDefaultMatchesOutputDefault() "+msg +"\n"+pageWithOtherPropertyValueSet); } } }// end for each property } for (Object[] skip : neverInHtmlSkips) { if( Boolean.FALSE.equals(skip[2]) ){ String className = XspTestUtil.getShortClass((Class<?>)skip[1]); fails += "Unused skip: neverInHtmlSkips " + "{\"" +skip[0]+"\", " +className+".class}\n"; } } fails = XspTestUtil.removeMultilineFailSkips(fails, SkipFileContent.concatSkips(getSkips(), this, "testGetterDefaultMatchesOutputDefault")); if( fails.length() > 0 ){ fail(XspTestUtil.getMultilineFailMessage(fails)); } } /** * @param neverInHtmlSkips * @return */ private Object[][] initSkips(Object[][] neverInHtmlSkips) { Object[][] withMarkUsedBoolean = new Object[neverInHtmlSkips.length][3]; int i = 0; for (Object[] originalSkip : neverInHtmlSkips) { Object[] newArr =withMarkUsedBoolean[i]; newArr[0] = originalSkip[0]; newArr[1] = originalSkip[1]; newArr[2] = false; i++; } return withMarkUsedBoolean; } private boolean isMarkSkipped(Object[][] neverInHtmlSkips, String propertyName, Class<?> controlClass){ int index = -1; int i = 0; for (Object[] skip : neverInHtmlSkips) { if( skip[0].equals(propertyName) ){ if( ((Class<?>)skip[1]).isAssignableFrom(controlClass) ){ index = i; break; } } i++; } if( -1 != index ){ neverInHtmlSkips[index][2] = true; // used return true; } return false; } /** * May be overridden in the subclasses to provide * a hard-coded list of fails to be skipped/ignored. * @return */ protected String[] getSkips(){ return StringUtil.EMPTY_STRING_ARRAY; } protected Object[][] getNeverInHtmlSkips(){ // new Object[]{ propertyName/*String*/, UIComponent.class/*class*/}, return new Object[0][]; } public static UIComponent newInstance(FacesComponentDefinition def) throws TestedElsewhereContinueException{ try{ return (UIComponent) def.getJavaClass().newInstance(); }catch(Exception e){ // note, RenderControlTest will fail for this, so not log here throw new TestedElsewhereContinueException(); } } public static String encode(UIComponent p, FacesContext context) throws TestedElsewhereContinueException{ String page; try{ page = ResponseBuffer.encode(p, context); }catch(Exception e){ // note, RenderControlTest will fail for this, so not log here ResponseBuffer.clear(context); throw new TestedElsewhereContinueException(); } if( ! page.startsWith("<p>") || page.equals("<p></p>")){ // note, RenderControlTest will fail for this, so not log here throw new TestedElsewhereContinueException(); } return page; } public static class TestedElsewhereContinueException extends Exception{ private static final long serialVersionUID = 1L; public TestedElsewhereContinueException() { super(); } } }