diff --git a/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java b/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java index 8bb11935..49ece01d 100644 --- a/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java +++ b/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java @@ -67,6 +67,7 @@ public enum BiFields { HIGHLIGHTED_OPTIONS("highlighted_options", BiEventType.MESSAGEML_ELEMENT_SENT), VALIDATION_STRICT("validation_strict", BiEventType.MESSAGEML_ELEMENT_SENT), REQUIRED("required", BiEventType.MESSAGEML_ELEMENT_SENT), + MULTI_SELECT("is_multi_select", BiEventType.MESSAGEML_ELEMENT_SENT), MESSAGE_LENGTH("message_length", BiEventType.MESSAGEML_MESSAGE_SENT), ENTITY_JSON_SIZE("entities_json_size", BiEventType.MESSAGEML_MESSAGE_SENT), FREEMARKER("use_freemarker", BiEventType.MESSAGEML_MESSAGE_SENT), diff --git a/src/main/java/org/symphonyoss/symphony/messageml/elements/Element.java b/src/main/java/org/symphonyoss/symphony/messageml/elements/Element.java index 00382d94..b5164309 100644 --- a/src/main/java/org/symphonyoss/symphony/messageml/elements/Element.java +++ b/src/main/java/org/symphonyoss/symphony/messageml/elements/Element.java @@ -989,4 +989,20 @@ protected void throwInvalidInputException(org.w3c.dom.Node item) throws InvalidI throw new InvalidInputException("Attribute \"" + item.getNodeName() + "\" is not allowed in \"" + getMessageMLTag() + "\""); } + + protected int checkIntegerAttribute(String attributeName, int minValue, String errorMessage) + throws InvalidInputException { + int value = 0; + if (getAttribute(attributeName) != null) { + try { + value = Integer.parseInt(getAttribute(attributeName)); + if (value < minValue) { + throw new InvalidInputException(errorMessage); + } + } catch (NumberFormatException e) { + throw new InvalidInputException(errorMessage, e); + } + } + return value; + } } diff --git a/src/main/java/org/symphonyoss/symphony/messageml/elements/Select.java b/src/main/java/org/symphonyoss/symphony/messageml/elements/Select.java index 2e163f3b..7e4a3f04 100644 --- a/src/main/java/org/symphonyoss/symphony/messageml/elements/Select.java +++ b/src/main/java/org/symphonyoss/symphony/messageml/elements/Select.java @@ -30,11 +30,24 @@ import java.util.Map; /** - * Class representing dropdown menu - Symphony Elements. - * - * @author lumoura - * @since 3/22/18 + * This class represents the Symphony Element Dialog which is represented with tag name "select" (drop down menu). + * The messageML representation of the select element can contain the following attributes: + * + * It can contain the following child tags: + * */ + public class Select extends FormElement implements LabelableElement, TooltipableElement { public static final String MESSAGEML_TAG = "select"; @@ -42,6 +55,11 @@ public class Select extends FormElement implements LabelableElement, Tooltipable private static final String REQUIRED_ATTR = "required"; private static final String OPTION_SELECTED_ATTR = "selected"; private static final String DATA_PLACEHOLDER_ATTR = "data-placeholder"; + private static final String MULTIPLE_ATTR = "multiple"; + private static final String MML_MIN_ATTR = "min"; + private static final String MIN_ATTR = "data-min"; + private static final String MML_MAX_ATTR = "max"; + private static final String MAX_ATTR = "data-max"; public Select(Element parent) { super(parent, MESSAGEML_TAG); @@ -59,31 +77,64 @@ public void validate() throws InvalidInputException { if (getAttribute(NAME_ATTR) == null) { throw new InvalidInputException("The attribute \"name\" is required"); } + assertAttributeNotBlank(NAME_ATTR); assertContentModel(Collections.singleton(Option.class)); assertContainsChildOfType(Collections.singleton(Option.class)); - if(getAttribute(REQUIRED_ATTR) != null) { + if (getAttribute(REQUIRED_ATTR) != null) { assertAttributeValue(REQUIRED_ATTR, Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())); } - assertOnlyOneOptionSelected(); - assertAttributeNotBlank(NAME_ATTR); + if (getAttribute(MULTIPLE_ATTR) != null) { + assertAttributeValue(MULTIPLE_ATTR, Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())); + } + + boolean multipleAttributeValue = Boolean.parseBoolean(getAttribute(MULTIPLE_ATTR)); + if (getAttribute(MIN_ATTR) != null && !multipleAttributeValue) { + throw new InvalidInputException("Attribute \"min\" is not allowed. Attribute \"multiple\" missing"); + } + + if (getAttribute(MAX_ATTR) != null && !multipleAttributeValue) { + throw new InvalidInputException("Attribute \"max\" is not allowed. Attribute \"multiple\" missing"); + } + + int min = checkIntegerAttribute(MIN_ATTR, 0, "Attribute \"min\" is not valid, it must be >= 0"); + int max = checkIntegerAttribute(MAX_ATTR, 1, "Attribute \"max\" is not valid, it must be >= 1"); + if (max > 0 && min > max) { + throw new InvalidInputException("Attribute \"min\" is greater than attribute \"max\""); + } + + if (multipleAttributeValue && Boolean.parseBoolean(getAttribute(REQUIRED_ATTR)) + && getAttribute(MIN_ATTR) != null && min == 0) { + // multiple=true required=true min=0 + throw new InvalidInputException("Attribute \"min\" cannot be 0 if \"required\" is true"); + } + + if (!multipleAttributeValue) { + assertOnlyOneOptionSelected(); + } } @Override - protected void buildAttribute(MessageMLParser parser, - Node item) throws InvalidInputException { + protected void buildAttribute(MessageMLParser parser, Node item) throws InvalidInputException { switch (item.getNodeName()) { case NAME_ATTR: case REQUIRED_ATTR: case DATA_PLACEHOLDER_ATTR: case LABEL: case TITLE: + case MULTIPLE_ATTR: setAttribute(item.getNodeName(), getStringAttribute(item)); break; + case MML_MIN_ATTR: + setAttribute(MIN_ATTR, getStringAttribute(item)); + break; + case MML_MAX_ATTR: + setAttribute(MAX_ATTR, getStringAttribute(item)); + break; case ID_ATTR: - if(format != FormatEnum.PRESENTATIONML){ + if (format != FormatEnum.PRESENTATIONML) { throwInvalidInputException(item); } fillAttributes(parser, item); @@ -94,7 +145,7 @@ protected void buildAttribute(MessageMLParser parser, } @Override - public String getElementId(){ + public String getElementId() { return ELEMENT_ID; } @@ -106,6 +157,7 @@ public void updateBiContext(BiContext context) { this.putOneIfPresent(attributesMapBi, BiFields.LABEL.getValue(), LABEL); this.putOneIfPresent(attributesMapBi, BiFields.PLACEHOLDER.getValue(), DATA_PLACEHOLDER_ATTR); this.putOneIfPresent(attributesMapBi, BiFields.REQUIRED.getValue(), REQUIRED_ATTR); + this.putOneIfPresent(attributesMapBi, BiFields.MULTI_SELECT.getValue(), MULTIPLE_ATTR); context.addItem(new BiItem(BiFields.SELECT.getValue(), attributesMapBi)); } @@ -116,7 +168,7 @@ private void assertOnlyOneOptionSelected() throws InvalidInputException { .filter(selectedAttr -> selectedAttr != null && selectedAttr.equalsIgnoreCase(Boolean.TRUE.toString())) .count(); - if(numberOfSelectedOptions > 1) { + if (numberOfSelectedOptions > 1) { throw new InvalidInputException("Element \"select\" can only have one selected \"option\""); } } diff --git a/src/test/java/org/symphonyoss/symphony/messageml/elements/form/SelectOptionTest.java b/src/test/java/org/symphonyoss/symphony/messageml/elements/form/SelectOptionTest.java index 9b034d4f..1f047eec 100644 --- a/src/test/java/org/symphonyoss/symphony/messageml/elements/form/SelectOptionTest.java +++ b/src/test/java/org/symphonyoss/symphony/messageml/elements/form/SelectOptionTest.java @@ -1,5 +1,9 @@ package org.symphonyoss.symphony.messageml.elements.form; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.symphonyoss.symphony.messageml.markdown.MarkdownRenderer.addEscapeCharacter; + import org.junit.Test; import org.symphonyoss.symphony.messageml.MessageMLContext; import org.symphonyoss.symphony.messageml.bi.BiFields; @@ -21,10 +25,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.symphonyoss.symphony.messageml.markdown.MarkdownRenderer.addEscapeCharacter; - public class SelectOptionTest extends ElementTest { private static final String NAME_ATTR = "name"; @@ -41,7 +41,9 @@ public void testCompleteRequiredSelect() throws Exception { String name = "complete-required-id"; boolean required = true; String placeholder = "placeholder-here"; - String input = "
" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); @@ -60,9 +62,11 @@ public void testCompleteRequiredSelectWithLabelAndTooltip() throws Exception { String title = "tooltip"; boolean required = true; String placeholder = "placeholder-here"; - String input = "
" + ACTION_BTN_ELEMENT + "
"; + String input = + "
" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); Element messageML = context.getMessageML(); @@ -80,7 +84,9 @@ public void testSelectWithUnderscore() throws Exception { String title = "tooltip-here"; boolean required = true; String placeholder = "placeholder-here"; - String input = "
" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); @@ -92,12 +98,13 @@ public void testSelectWithUnderscore() throws Exception { assertEquals("Select class", Select.class, select.getClass()); verifySelectPresentation((Select) select, name, true, required, placeholder, true, true); } + @Test public void testCompleteNotRequiredSelect() throws Exception { String name = "complete-id"; boolean required = false; String input = "
" + ACTION_BTN_ELEMENT + "
"; + "\">" + ACTION_BTN_ELEMENT + ""; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); Element messageML = context.getMessageML(); @@ -111,7 +118,8 @@ public void testCompleteNotRequiredSelect() throws Exception { @Test public void testSimpleSelect() throws Exception { String name = "simple-id"; - String input = "
" + ACTION_BTN_ELEMENT + String input = "" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); @@ -120,14 +128,15 @@ public void testSimpleSelect() throws Exception { Element select = form.getChildren().get(0); assertEquals("Select class", Select.class, select.getClass()); - verifySelectPresentation((Select) select, name, false,false, null, false, false); + verifySelectPresentation((Select) select, name, false, false, null, false, false); } @Test public void testDoubleOptionSelect() throws Exception { String name = "simple-id"; - String input = "
" + ACTION_BTN_ELEMENT + "
"; + String input = "
" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); Element messageML = context.getMessageML(); @@ -135,13 +144,14 @@ public void testDoubleOptionSelect() throws Exception { Element select = form.getChildren().get(0); assertEquals("Select class", Select.class, select.getClass()); - verifySelectPresentation((Select) select, name, false,false, null, false, false); + verifySelectPresentation((Select) select, name, false, false, null, false, false); } @Test public void testOptionWithSelectedAttr() throws Exception { String name = "simple-id"; - String input = "
" + ACTION_BTN_ELEMENT + "
"; context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); @@ -150,13 +160,14 @@ public void testOptionWithSelectedAttr() throws Exception { Element select = form.getChildren().get(0); assertEquals("Select class", Select.class, select.getClass()); - verifySelectPresentation((Select) select, name, false,false, null, false, false); + verifySelectPresentation((Select) select, name, false, false, null, false, false); } @Test public void testDoubleOptionWithSelectedAttrAsTrue() throws Exception { String name = "simple-id"; - String input = "
"; expectedException.expect(InvalidInputException.class); @@ -168,11 +179,13 @@ public void testDoubleOptionWithSelectedAttrAsTrue() throws Exception { @Test public void testOptionWithInvalidValueForSelectedAttr() throws Exception { String name = "simple-id"; - String input = "
"; expectedException.expect(InvalidInputException.class); - expectedException.expectMessage("Attribute \"selected\" of element \"option\" can only be one of the following values: [true, false]."); + expectedException.expectMessage( + "Attribute \"selected\" of element \"option\" can only be one of the following values: [true, false]."); context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); } @@ -180,10 +193,12 @@ public void testOptionWithInvalidValueForSelectedAttr() throws Exception { @Test public void testChildlessSelect() throws Exception { String name = "childless-select"; - String input = "
"; + String input = + "
"; expectedException.expect(InvalidInputException.class); - expectedException.expectMessage("The \"select\" element must have at least one child that is any of the following elements: [option]."); + expectedException.expectMessage( + "The \"select\" element must have at least one child that is any of the following elements: [option]."); context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); } @@ -191,17 +206,20 @@ public void testChildlessSelect() throws Exception { @Test public void testSelectWithEmptyChild() throws Exception { String name = "empty-child-select"; - String input = "
"; + String input = + "
"; expectedException.expect(InvalidInputException.class); - expectedException.expectMessage("The \"select\" element must have at least one child that is any of the following elements: [option]."); + expectedException.expectMessage( + "The \"select\" element must have at least one child that is any of the following elements: [option]."); context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); } @Test public void testSelectWithoutName() throws Exception { - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("The attribute \"name\" is required"); @@ -211,7 +229,8 @@ public void testSelectWithoutName() throws Exception { @Test public void testSelectWithBlankName() throws Exception { - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("The attribute \"name\" is required"); @@ -222,7 +241,8 @@ public void testSelectWithBlankName() throws Exception { @Test public void testOptionWithoutValue() throws Exception { String name = "nameless-option"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("The attribute \"value\" is required"); @@ -233,10 +253,12 @@ public void testOptionWithoutValue() throws Exception { @Test public void testSelectWithInvalidRequiredAttribute() throws Exception { String name = "invalid-required-select"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); - expectedException.expectMessage("Attribute \"required\" of element \"select\" can only be one of the following values: [true, false]."); + expectedException.expectMessage( + "Attribute \"required\" of element \"select\" can only be one of the following values: [true, false]."); context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); } @@ -244,7 +266,8 @@ public void testSelectWithInvalidRequiredAttribute() throws Exception { @Test public void testSelectWithInvalidAttribute() throws Exception { String name = "invalid-attribute-select"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("Attribute \"invalid\" is not allowed in \"select\""); @@ -255,7 +278,8 @@ public void testSelectWithInvalidAttribute() throws Exception { @Test public void testOptionWithInvalidAttribute() throws Exception { String name = "invalid-attribute-option"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("Attribute \"invalid\" is not allowed in \"option\""); @@ -287,7 +311,8 @@ public void testOptionOutOfSelect() throws Exception { @Test public void testSelectWithInvalidChild() throws Exception { String name = "invalid-child-select"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("Element \"span\" is not allowed in \"select\""); @@ -298,7 +323,8 @@ public void testSelectWithInvalidChild() throws Exception { @Test public void testOptionWithInvalidChild() throws Exception { String name = "invalid-child-option"; - String input = "
"; + String input = "
"; expectedException.expect(InvalidInputException.class); expectedException.expectMessage("Element \"span\" is not allowed in \"option\""); @@ -306,6 +332,361 @@ public void testOptionWithInvalidChild() throws Exception { context.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); } + @Test + public void testMultiSelect() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + + Element form = context.getMessageML().getChildren().get(0); + Element select = form.getChildren().get(0); + assertEquals("Select class", Select.class, select.getClass()); + + //language=HTML + String expectedPresentationML = "
\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + assertEquals(trimXml(expectedPresentationML), context.getPresentationML()); + } + + @Test + public void testMultiSelectMinMax() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + + Element form = context.getMessageML().getChildren().get(0); + Element select = form.getChildren().get(0); + assertEquals("Select class", Select.class, select.getClass()); + + //language=HTML + String expectedPresentationML = "
\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + assertEquals(trimXml(expectedPresentationML), context.getPresentationML()); + } + + @Test + public void testMultiSelectMultiSelected() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + + //language=HTML + String expectedPresentationML = "
\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + assertEquals(trimXml(expectedPresentationML), context.getPresentationML()); + } + + @Test + public void testMultiSelectWithoutMultiple() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is not allowed. Attribute \"multiple\" missing"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMultipleFalse() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is not allowed. Attribute \"multiple\" missing"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectWithoutMultipleMinOnly() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is not allowed. Attribute \"multiple\" missing"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectWithoutMultipleMaxOnly() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"max\" is not allowed. Attribute \"multiple\" missing"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMinGreaterThanMax() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is greater than attribute \"max\""); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMinGreaterThanMaxUnset() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + + //language=HTML + String expectedPresentationML = "
\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + assertEquals(trimXml(expectedPresentationML), context.getPresentationML()); + } + + @Test + public void testMultiSelectMinInvalid() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is not valid"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectRequiredMinInvalid() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" cannot be 0 if \"required\" is true"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectRequiredMinDefault() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + + //language=HTML + String expectedPresentationML = "
\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + assertEquals(trimXml(expectedPresentationML), context.getPresentationML()); + } + + @Test + public void testMultiSelectMinInvalidType() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"min\" is not valid"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMaxInvalid() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"max\" is not valid"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMaxInvalidType() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"max\" is not valid"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void testMultiSelectMultipleInvalid() throws Exception { + //language=XML + String input = "\n" + + "
\n" + + " \n" + + " \n" + + "
\n" + + "
"; + + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Attribute \"multiple\" of element \"select\" can only be one of the following values: [true, false]"); + + context.parseMessageML(trimXml(input), null, MessageML.MESSAGEML_VERSION); + } + @Test public void testBiContextSelect() throws InvalidInputException, IOException, ProcessingException { @@ -360,9 +741,46 @@ public void testBiContextSelect() assertMessageLengthBiItem(items.get(4), input.length()); } + @Test + public void testBiContextMultiSelect() throws InvalidInputException, IOException, ProcessingException { + MessageMLContext messageMLContext = new MessageMLContext(null); + String input = "\n" + + "
\n" + + "\n" + + " \n" + + "
\n" + + "
"; + + messageMLContext.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); + List items = messageMLContext.getBiContext().getItems(); + + Map selectExpectedAttributes = Stream.of(new Object[][] { + {BiFields.MULTI_SELECT.getValue(), 1}, + {BiFields.TITLE.getValue(), 1}, + {BiFields.LABEL.getValue(), 1}, + {BiFields.PLACEHOLDER.getValue(), 1}, + {BiFields.REQUIRED.getValue(), 1}, + }).collect(Collectors.toMap(property -> property[0], property -> property[1])); + + BiItem selectBiItemExpected = new BiItem(BiFields.SELECT.getValue(), + selectExpectedAttributes.entrySet() + .stream() + .collect(Collectors.toMap(e -> + String.valueOf(e.getKey()), Map.Entry::getValue))); + + assertEquals(5, items.size()); + assertEquals(BiFields.SELECT.getValue(), items.get(1).getName()); + assertSameBiItem(selectBiItemExpected, items.get(1)); + } + private String getRequiredPresentationML(String required) { - if(required != null) { - if(required.equals("true") || required.equals("false")) { + if (required != null) { + if (required.equals("true") || required.equals("false")) { return String.format(" required=\"%s\"", required); } } @@ -377,7 +795,7 @@ private String getExpectedSelectMarkdown(Select select, boolean hasLabel, boolea StringBuilder expectedMarkdown = new StringBuilder(FORM_MARKDOWN_HEADER); expectedMarkdown.append("(Dropdown"); String placeholder = select.getAttribute(DATA_PLACEHOLDER_ATTR); - expectedMarkdown.append((placeholder!= null || hasLabel || hasTitle) ? ":" : ""); + expectedMarkdown.append((placeholder != null || hasLabel || hasTitle) ? ":" : ""); expectedMarkdown.append((placeholder != null) ? "[" + addEscapeCharacter((placeholder)) + "]" : ""); expectedMarkdown.append(hasLabel ? "[" + addEscapeCharacter(select.getAttribute(LABEL_ATTR)) + "]" : ""); expectedMarkdown.append(hasTitle ? "[" + addEscapeCharacter(select.getAttribute(TITLE_ATTR)) + "]" : ""); @@ -385,24 +803,27 @@ private String getExpectedSelectMarkdown(Select select, boolean hasLabel, boolea for (Element option : select.getChildren()) { if (option instanceof Option) { - expectedMarkdown.append("-" + option.getChild(0).asText() + "\n"); + expectedMarkdown.append("-").append(option.getChild(0).asText()).append("\n"); } } - - expectedMarkdown.append(ACTION_BTN_MARKDOWN + "\n" + FORM_MARKDOWN_FOOTER); + + expectedMarkdown.append(ACTION_BTN_MARKDOWN + "\n").append(FORM_MARKDOWN_FOOTER); return expectedMarkdown.toString(); } - private String getExpectedSelectPresentation(Select select, boolean hasLabel, boolean hasTitle, String uniqueLabelId) { + private String getExpectedSelectPresentation(Select select, boolean hasLabel, boolean hasTitle, + String uniqueLabelId) { String selectOpeningTag = "
" + ((hasLabel || hasTitle) ? "
" : "") - + (hasLabel ? "" : "") - + (hasTitle ? "" : "") + + (hasLabel ? "" : "") + + (hasTitle ? "" : "") + ""; @@ -415,7 +836,8 @@ private String getExpectedSelectPresentation(Select select, boolean hasLabel, bo option.getAttribute(VALUE_ATTR) + "\">" + option.getChild(0).asText() + ""; } } - return selectOpeningTag + selectChildren + selectClosingTag + ((hasLabel || hasTitle) ? "
" : "") + ACTION_BTN_ELEMENT + formDivClosingTag; + return selectOpeningTag + selectChildren + selectClosingTag + ((hasLabel || hasTitle) ? "
" : "") + + ACTION_BTN_ELEMENT + formDivClosingTag; } private String getPlaceholderAttribute(String placeholder) { @@ -426,8 +848,8 @@ private String getOptionSelectedExpectedText(Element option) { return option.getAttribute(SELECTED_ATTR) != null ? " selected=\"" + option.getAttribute(SELECTED_ATTR) + "\"" : ""; } - private void verifySelectPresentation(Select select, String name, boolean requiredAttrProvided, boolean requiredValue, - String placeholder, boolean hasLabel, boolean hasTitle) { + private void verifySelectPresentation(Select select, String name, boolean requiredAttrProvided, boolean requiredValue, + String placeholder, boolean hasLabel, boolean hasTitle) { assertEquals("Select name attribute", name, select.getAttribute(NAME_ATTR)); if (requiredAttrProvided) { assertEquals("Select required attribute", String.valueOf(requiredValue), select.getAttribute(REQUIRED_ATTR)); @@ -445,8 +867,14 @@ private void verifySelectPresentation(Select select, String name, boolean requir String dropdownRegex = ".*(\"dropdown-(.*?)\").*"; Pattern pattern = Pattern.compile(dropdownRegex); Matcher matcher = pattern.matcher(presentationML); - - assertEquals("Select presentationML", getExpectedSelectPresentation(select, hasLabel, hasTitle, matcher.matches() ? matcher.group(2) : null), presentationML); + + assertEquals("Select presentationML", + getExpectedSelectPresentation(select, hasLabel, hasTitle, matcher.matches() ? matcher.group(2) : null), + presentationML); assertEquals("Select markdown", getExpectedSelectMarkdown(select, hasLabel, hasTitle), context.getMarkdown()); } + + private static String trimXml(String input) { // to avoid empty text element upon parsing + return input.replace("\n", "").replace(" ", ""); + } }