/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * 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 com.xpn.xwiki.objects.classes; import java.util.Arrays; import java.util.List; import java.util.Map; import org.codehaus.plexus.util.StringUtils; import org.junit.Rule; import org.junit.Test; import org.xwiki.model.reference.DocumentReference; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.ListProperty; import com.xpn.xwiki.test.MockitoOldcoreRule; import com.xpn.xwiki.test.reference.ReferenceComponentList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; /** * Unit tests for {@link StaticListClass}. * * @version $Id: 38e3d62001640cd9f475247c5c6fd52b6515e07a $ */ @ReferenceComponentList public class StaticListClassTest { /** * Static list values that contain HTML special characters that need to be escaped in the HTML display. */ private static final List<String> VALUES_WITH_HTML_SPECIAL_CHARS = Arrays.asList("a<b>c", "1\"2'3", "x{y&z"); @Rule public MockitoOldcoreRule oldcore = new MockitoOldcoreRule(); /** Tests that {@link StaticListClass#getList} returns values sorted according to the property's sort option. */ @Test public void testGetListIsSorted() { StaticListClass listClass = new StaticListClass(); listClass.setValues("a=A|c=D|d=C|b"); assertEquals("Default order was not preserved.", "[a, c, d, b]", listClass.getList(this.oldcore.getXWikiContext()).toString()); listClass.setSort("none"); assertEquals("Default order was not preserved.", "[a, c, d, b]", listClass.getList(this.oldcore.getXWikiContext()).toString()); listClass.setSort("id"); assertEquals("Items were not ordered by ID.", "[a, b, c, d]", listClass.getList(this.oldcore.getXWikiContext()).toString()); listClass.setSort("value"); assertEquals("Items were not ordered by value.", "[a, b, d, c]", listClass.getList(this.oldcore.getXWikiContext()).toString()); } /** * Tests that {@link StaticListClass#getMap} properly supports the values definition syntax: * <ul> * <li>values are separated by {@code |}</li> * <li>simple {@code key} entries are allowed, and the key is also used as the label</li> * <li>{@code key=a pretty label} is allowed</li> * <li>{@code key=label with = in it} is allowed</li> * <li>{@code key=label with \| in it} is allowed</li> * </ul> */ @Test public void testValuesSyntax() { StaticListClass listClass = new StaticListClass(); listClass.setValues("a|b=B and B|c=<=C=>|d=d\\|D|e"); Map<String, ListItem> result = listClass.getMap(this.oldcore.getXWikiContext()); assertEquals("Proper splitting not supported", 5, result.size()); assertEquals("key syntax not supported", "a", result.get("a").getValue()); assertEquals("key=label syntax not supported", "B and B", result.get("b").getValue()); assertEquals("= in labels not supported", "<=C=>", result.get("c").getValue()); assertEquals("Escaped \\| in labels not supported", "d|D", result.get("d").getValue()); } /** * Tests that the list values are joined using the specified separator without being XML-encoded. * * @see "XWIKI-9680: Apostrophes in static list value are encoded on .display()" */ @Test public void testDisplayView() { ListProperty listProperty = new ListProperty(); listProperty.setValue(VALUES_WITH_HTML_SPECIAL_CHARS); String propertyName = "foo"; BaseObject object = new BaseObject(); object.addField(propertyName, listProperty); StaticListClass staticListClass = new StaticListClass(); staticListClass.setSeparator(" * "); staticListClass.setValues(VALUES_WITH_HTML_SPECIAL_CHARS.get(0) + '|' + VALUES_WITH_HTML_SPECIAL_CHARS.get(1) + '=' + StringUtils.reverse(VALUES_WITH_HTML_SPECIAL_CHARS.get(1)) + '|' + VALUES_WITH_HTML_SPECIAL_CHARS.get(2)); assertEquals("a<b>c * 3'2\"1 * x{y&z", staticListClass.displayView(propertyName, "", object, null)); } /** * Tests the HTML output produced in edit mode. * * @param displayType the display type (input, radio, select, etc.) * @param selectedValues the selected values * @param expectedHTML the expected HTML output */ private void testDisplayEdit(String displayType, List<String> selectedValues, String expectedHTML) { ListProperty listProperty = new ListProperty(); listProperty.setValue(selectedValues); // Use special XML characters, even if they are not valid inside an XML name, just to test the XML escaping. String propertyName = "b&a<r"; String prefix = "w>v"; BaseObject object = new BaseObject(); object.addField(propertyName, listProperty); StaticListClass staticListClass = new StaticListClass(); staticListClass.setSize(7); StringBuilder values = new StringBuilder(); for (String value : VALUES_WITH_HTML_SPECIAL_CHARS) { if (values.length() > 0) { values.append('|'); } values.append(value).append('=').append(StringUtils.reverse(value)); } staticListClass.setValues(values.toString()); staticListClass.setDisplayType(displayType); assertEquals(expectedHTML, staticListClass.displayEdit(propertyName, prefix, object, null)); } /** * Tests the 'input' display type. */ @Test public void testDisplayEditInput() { testDisplayEdit("input", VALUES_WITH_HTML_SPECIAL_CHARS, "<input size='7' id='w>vb&a<r' " + "value='a<b>c|1"2'3|x{y&z' name='w>vb&a<r' type='text'/>"); } /** * Tests the 'select' display type. */ @Test public void testDisplayEditSelect() { String expectedHTML = "<select id='w>vb&a<r' name='w>vb&a<r' size='7'>" + "<option value='a<b>c' label='c>b<a'>c>b<a</option>" + "<option selected='selected' value='1"2'3' label='3'2"1'>3'2"1</option>" + "<option value='x{y&z' label='z&y{x'>z&y{x</option>" + "</select><input name='w>vb&a<r' type='hidden' value=''/>"; testDisplayEdit("select", Arrays.asList(VALUES_WITH_HTML_SPECIAL_CHARS.get(1)), expectedHTML); } /** * Tests the 'radio' display type. */ @Test public void testDisplayEditRadio() { StringBuilder expectedHTML = new StringBuilder(); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-0\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-0' value='a<b>c'"); expectedHTML.append(" name='w>vb&a<r' type='radio'/>c>b<a</label>"); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-1\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-1' checked='checked' value='1"2'3' "); expectedHTML.append("name='w>vb&a<r' type='radio'/>3'2"1</label>"); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-2\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-2' value='x{y&z'"); expectedHTML.append(" name='w>vb&a<r' type='radio'/>z&y{x</label>"); expectedHTML.append("<input name='w>vb&a<r' type='hidden' value=''/>"); testDisplayEdit("radio", Arrays.asList(VALUES_WITH_HTML_SPECIAL_CHARS.get(1)), expectedHTML.toString()); } /** * Tests the 'checkbox' display type. */ @Test public void testDisplayEditCheckbox() { StringBuilder expectedHTML = new StringBuilder(); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-0\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-0' value='a<b>c'"); expectedHTML.append(" name='w>vb&a<r' type='checkbox'/>c>b<a</label>"); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-1\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-1' checked='checked' value='1"2'3' "); expectedHTML.append("name='w>vb&a<r' type='checkbox'/>3'2"1</label>"); expectedHTML.append("<label class=\"xwiki-form-listclass\" for=\"xwiki-form-b&a<r-0-2\">"); expectedHTML.append("<input id='xwiki-form-b&a<r-0-2' value='x{y&z'"); expectedHTML.append(" name='w>vb&a<r' type='checkbox'/>z&y{x</label>"); expectedHTML.append("<input name='w>vb&a<r' type='hidden' value=''/>"); testDisplayEdit("checkbox", Arrays.asList(VALUES_WITH_HTML_SPECIAL_CHARS.get(1)), expectedHTML.toString()); } /** * Tests the hidden display type. */ @Test public void testDisplayHidden() { ListProperty listProperty = new ListProperty(); listProperty.setValue(VALUES_WITH_HTML_SPECIAL_CHARS); // Use special XML characters, even if they are not valid inside an XML name, just to test the XML escaping. String propertyName = "f<o&o"; BaseObject object = new BaseObject(); object.addField(propertyName, listProperty); assertEquals( "<input id='f<o&o' value='a<b>c|1"2'3|x{y&z' " + "name='f<o&o' type='hidden'/>", new StaticListClass().displayHidden(propertyName, "", object, null)); } /** * Tests the suggest code generated when "use suggest" is set. */ @Test public void testDisplayEditWithSuggest() throws Exception { ListProperty listProperty = new ListProperty(); listProperty.setValue(VALUES_WITH_HTML_SPECIAL_CHARS); // Use special XML characters, even if they are not valid inside an XML name, just to test the XML escaping. String propertyName = "b&a<r"; String prefix = "w>v"; BaseObject object = new BaseObject(); object.addField(propertyName, listProperty); StaticListClass staticListClass = new StaticListClass(); BaseClass ownerClass = new BaseClass(); ownerClass.setDocumentReference(new DocumentReference("xwiki", "ClassSpace", "ClassName")); staticListClass.setName(propertyName); staticListClass.setObject(ownerClass); staticListClass.setSize(7); StringBuilder values = new StringBuilder(); for (String value : VALUES_WITH_HTML_SPECIAL_CHARS) { if (values.length() > 0) { values.append('|'); } values.append(value).append('=').append(StringUtils.reverse(value)); } staticListClass.setValues(values.toString()); staticListClass.setDisplayType("input"); staticListClass.setPicker(true); doReturn("/xwiki/bin/view/Main/WebHome").when(this.oldcore.getSpyXWiki()).getURL("Main.WebHome", "view", this.oldcore.getXWikiContext()); String output = staticListClass.displayEdit(propertyName, prefix, object, this.oldcore.getXWikiContext()); System.err.println(output); assertTrue( output.contains("new ajaxSuggest(this, {script:"/xwiki/bin/view/Main/WebHome?xpage=suggest&" + "classname=ClassSpace.ClassName&fieldname=b&a<r&firCol=-&" + "secCol=-&", varname:"input"} )")); } }