From fe226526b4830de37d35d23ae3e188135140151d Mon Sep 17 00:00:00 2001 From: Mohamed Rojbeni Date: Tue, 3 Oct 2023 09:56:44 +0200 Subject: [PATCH] [AGNT-366][C2-20895] add room selector component (#384) * [AGNT-366][C2-20895] add room selector component * [AGNT-366][C2-20895] consider value attribute as string * [AGNT-366][C2-20895] use declared variable * [AGNT-366][C2-20895] fix exception message --- .../symphony/messageml/MessageMLParser.java | 7 + .../symphony/messageml/bi/BiFields.java | 1 + .../messageml/elements/RoomSelector.java | 234 ++++++++++++ .../markdown/nodes/form/RoomSelectorNode.java | 28 ++ .../elements/form/RoomSelectorTest.java | 336 ++++++++++++++++++ 5 files changed, 606 insertions(+) create mode 100644 src/main/java/org/symphonyoss/symphony/messageml/elements/RoomSelector.java create mode 100644 src/main/java/org/symphonyoss/symphony/messageml/markdown/nodes/form/RoomSelectorNode.java create mode 100644 src/test/java/org/symphonyoss/symphony/messageml/elements/form/RoomSelectorTest.java diff --git a/src/main/java/org/symphonyoss/symphony/messageml/MessageMLParser.java b/src/main/java/org/symphonyoss/symphony/messageml/MessageMLParser.java index 96d8dde8..379585ca 100644 --- a/src/main/java/org/symphonyoss/symphony/messageml/MessageMLParser.java +++ b/src/main/java/org/symphonyoss/symphony/messageml/MessageMLParser.java @@ -59,6 +59,7 @@ import org.symphonyoss.symphony.messageml.elements.PersonSelector; import org.symphonyoss.symphony.messageml.elements.Preformatted; import org.symphonyoss.symphony.messageml.elements.Radio; +import org.symphonyoss.symphony.messageml.elements.RoomSelector; import org.symphonyoss.symphony.messageml.elements.Select; import org.symphonyoss.symphony.messageml.elements.Span; import org.symphonyoss.symphony.messageml.elements.SplittableElement; @@ -586,6 +587,9 @@ public Element createElement(org.w3c.dom.Element element, Element parent) throws case PersonSelector.MESSAGEML_TAG: return new PersonSelector(parent, messageFormat); + case RoomSelector.MESSAGEML_TAG: + return new RoomSelector(parent, messageFormat); + case DateSelector.MESSAGEML_TAG: return new DateSelector(parent, messageFormat); @@ -787,6 +791,9 @@ private Element createElementFromDiv(org.w3c.dom.Element element, Element parent } else if (containsAttribute(elementClass, PersonSelector.MESSAGEML_TAG)) { removeAttribute(element, CLASS_ATTR, PersonSelector.MESSAGEML_TAG); return new PersonSelector(parent, FormatEnum.PRESENTATIONML); + } else if (containsAttribute(elementClass, RoomSelector.MESSAGEML_TAG)) { + removeAttribute(element, CLASS_ATTR, RoomSelector.MESSAGEML_TAG); + return new RoomSelector(parent, FormatEnum.PRESENTATIONML); } else if (containsAttribute(elementClass, DateSelector.MESSAGEML_TAG)) { removeAttribute(element, CLASS_ATTR, DateSelector.MESSAGEML_TAG); return new DateSelector(parent, FormatEnum.PRESENTATIONML); 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 acfd1b8f..8c682398 100644 --- a/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java +++ b/src/main/java/org/symphonyoss/symphony/messageml/bi/BiFields.java @@ -17,6 +17,7 @@ public enum BiFields { TIMEZONE_PICKER("timezonepicker", BiEventType.MESSAGEML_ELEMENT_SENT), DATE_SELECTOR("dateselector", BiEventType.MESSAGEML_ELEMENT_SENT), PERSON_SELECTOR("personselector", BiEventType.MESSAGEML_ELEMENT_SENT), + ROOM_SELECTOR("roomselector", BiEventType.MESSAGEML_ELEMENT_SENT), SELECT("dropdownmenu", BiEventType.MESSAGEML_ELEMENT_SENT), CHIME("chimes", BiEventType.MESSAGEML_MESSAGE_SENT, "0"), CODE("codes", BiEventType.MESSAGEML_MESSAGE_SENT, "0"), diff --git a/src/main/java/org/symphonyoss/symphony/messageml/elements/RoomSelector.java b/src/main/java/org/symphonyoss/symphony/messageml/elements/RoomSelector.java new file mode 100644 index 00000000..857d09ed --- /dev/null +++ b/src/main/java/org/symphonyoss/symphony/messageml/elements/RoomSelector.java @@ -0,0 +1,234 @@ +package org.symphonyoss.symphony.messageml.elements; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.commonmark.node.Node; +import org.symphonyoss.symphony.messageml.MessageMLContext; +import org.symphonyoss.symphony.messageml.MessageMLParser; +import org.symphonyoss.symphony.messageml.bi.BiContext; +import org.symphonyoss.symphony.messageml.bi.BiFields; +import org.symphonyoss.symphony.messageml.bi.BiItem; +import org.symphonyoss.symphony.messageml.exceptions.InvalidInputException; +import org.symphonyoss.symphony.messageml.exceptions.ProcessingException; +import org.symphonyoss.symphony.messageml.markdown.nodes.form.PersonSelectorNode; +import org.symphonyoss.symphony.messageml.markdown.nodes.form.RoomSelectorNode; +import org.symphonyoss.symphony.messageml.util.XmlPrintStream; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Class representing a room selector inside a Symphony Elements form. + */ +public class RoomSelector extends FormElement implements LabelableElement, TooltipableElement { + public static final String MESSAGEML_TAG = "room-selector"; + + private static final String PLACEHOLDER_ATTR = "placeholder"; + private static final String REQUIRED_ATTR = "required"; + private static final Set VALID_VALUES_FOR_REQUIRED_ATTR = + new HashSet<>(Arrays.asList("true", "false")); + private static final String VALUE_ATTR = "value"; + + private static final String PRESENTATIONML_TAG = "div"; + private static final String PRESENTATIONML_PLACEHOLDER_ATTR = "data-placeholder"; + private static final String PRESENTATIONML_REQUIRED_ATTR = "data-required"; + private static final String PRESENTATIONML_VALUE_ATTR = "data-value"; + + private final static String MARKDOWN = "Room Selector"; + private static final String CLASS_ATTR = "class"; + private static final String PRESENTATIONML_NAME_ATTR = "data-name"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + public RoomSelector(Element parent, FormatEnum messageFormat) { + super(parent, MESSAGEML_TAG, messageFormat); + } + + static { + MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + } + + @Override + public void buildAll(MessageMLParser parser, org.w3c.dom.Element element) + throws InvalidInputException, ProcessingException { + switch (getFormat()) { + case MESSAGEML: + super.buildAll(parser, element); + break; + case PRESENTATIONML: + buildElementFromDiv(parser, element); + this.validate(); + break; + default: + throw new InvalidInputException( + String.format("Invalid message format for \"%s\" element", MESSAGEML_TAG)); + } + } + + @Override + public void validate() throws InvalidInputException { + super.validate(); + + if (getAttribute(NAME_ATTR) == null) { + throw new InvalidInputException("The attribute \"name\" is required"); + } + + if (getAttribute(REQUIRED_ATTR) != null) { + assertAttributeValue(REQUIRED_ATTR, VALID_VALUES_FOR_REQUIRED_ATTR); + } + + if (getAttribute(VALUE_ATTR) != null) { + String attribute = getAttribute(VALUE_ATTR); + try { + MAPPER.readValue(attribute, + MAPPER.getTypeFactory().constructCollectionType(List.class, String.class)); + } catch (JsonProcessingException e) { + throw new InvalidInputException( + String.format( + "Attribute \"%s\" contains an unsupported format, should be an array of room ids", + VALUE_ATTR) + ); + } + } + + assertNoContent(); + assertAttributeNotBlank(NAME_ATTR); + } + + @Override + public void asPresentationML(XmlPrintStream out, + MessageMLContext context) { + Map presentationAttrs = buildRoomSelectorInputAttributes(); + if (isSplittable()) { + // open div + adding splittable elements + presentationAttrs.put("id", splittableAsPresentationML(out, context)); + // render element + innerAsPresentationML(out, presentationAttrs); + // close div + out.closeElement(); + } else { + innerAsPresentationML(out, presentationAttrs); + } + } + + private void innerAsPresentationML(XmlPrintStream out, Map presentationAttrs) { + out.openElement(PRESENTATIONML_TAG, presentationAttrs); + out.closeElement(); + } + + @Override + public Node asMarkdown() { + return new RoomSelectorNode(getAttribute(PLACEHOLDER_ATTR), getAttribute(LABEL), + getAttribute(TITLE)); + } + + @Override + protected void buildAttribute(MessageMLParser parser, + org.w3c.dom.Node item) throws InvalidInputException { + switch (item.getNodeName()) { + case NAME_ATTR: + case PLACEHOLDER_ATTR: + case REQUIRED_ATTR: + case LABEL: + case TITLE: + case VALUE_ATTR: + setAttribute(item.getNodeName(), getStringAttribute(item)); + break; + case ID_ATTR: + if (format != FormatEnum.PRESENTATIONML) { + throwInvalidInputException(item); + } + fillAttributes(parser, item); + break; + default: + throwInvalidInputException(item); + } + } + + private Map buildRoomSelectorInputAttributes() { + Map presentationAttrs = new LinkedHashMap<>(); + + presentationAttrs.put(CLASS_ATTR, MESSAGEML_TAG); + presentationAttrs.put(PRESENTATIONML_NAME_ATTR, getAttribute(NAME_ATTR)); + + if (getAttribute(PLACEHOLDER_ATTR) != null) { + presentationAttrs.put(PRESENTATIONML_PLACEHOLDER_ATTR, getAttribute(PLACEHOLDER_ATTR)); + } + + if (getAttribute(REQUIRED_ATTR) != null) { + presentationAttrs.put(PRESENTATIONML_REQUIRED_ATTR, getAttribute(REQUIRED_ATTR)); + } + + if (getAttribute(VALUE_ATTR) != null) { + presentationAttrs.put(PRESENTATIONML_VALUE_ATTR, getAttribute(VALUE_ATTR)); + } + + return presentationAttrs; + } + + private void buildElementFromDiv(MessageMLParser parser, org.w3c.dom.Element element) + throws InvalidInputException, ProcessingException { + + element.setAttribute(NAME_ATTR, element.getAttribute(PRESENTATIONML_NAME_ATTR)); + element.removeAttribute(PRESENTATIONML_NAME_ATTR); + + if (element.hasAttribute(PRESENTATIONML_PLACEHOLDER_ATTR)) { + element.setAttribute(PLACEHOLDER_ATTR, element.getAttribute(PRESENTATIONML_PLACEHOLDER_ATTR)); + element.removeAttribute(PRESENTATIONML_PLACEHOLDER_ATTR); + } + + if (element.hasAttribute(PRESENTATIONML_REQUIRED_ATTR)) { + element.setAttribute(REQUIRED_ATTR, element.getAttribute(PRESENTATIONML_REQUIRED_ATTR)); + element.removeAttribute(PRESENTATIONML_REQUIRED_ATTR); + } + + if (element.hasAttribute(PRESENTATIONML_VALUE_ATTR)) { + element.setAttribute(VALUE_ATTR, element.getAttribute(PRESENTATIONML_VALUE_ATTR)); + element.removeAttribute(PRESENTATIONML_VALUE_ATTR); + } + + NamedNodeMap attributes = element.getAttributes(); + + for (int i = 0; i < attributes.getLength(); i++) { + buildAttribute(parser, attributes.item(i)); + } + + NodeList children = element.getChildNodes(); + + for (int i = 0; i < children.getLength(); i++) { + buildNode(parser, children.item(i)); + } + + } + + @Override + public String getPresentationMLTag() { + return PRESENTATIONML_TAG; + } + + @Override + public String getElementId() { + return MESSAGEML_TAG; + } + + @Override + public void updateBiContext(BiContext context) { + Map attributesMapBi = new HashMap<>(); + + this.putOneIfPresent(attributesMapBi, BiFields.TITLE.getValue(), TITLE); + this.putOneIfPresent(attributesMapBi, BiFields.LABEL.getValue(), LABEL); + this.putOneIfPresent(attributesMapBi, BiFields.PLACEHOLDER.getValue(), PLACEHOLDER_ATTR); + this.putOneIfPresent(attributesMapBi, BiFields.REQUIRED.getValue(), REQUIRED_ATTR); + + context.addItem(new BiItem(BiFields.ROOM_SELECTOR.getValue(), attributesMapBi)); + } + +} diff --git a/src/main/java/org/symphonyoss/symphony/messageml/markdown/nodes/form/RoomSelectorNode.java b/src/main/java/org/symphonyoss/symphony/messageml/markdown/nodes/form/RoomSelectorNode.java new file mode 100644 index 00000000..836bacb5 --- /dev/null +++ b/src/main/java/org/symphonyoss/symphony/messageml/markdown/nodes/form/RoomSelectorNode.java @@ -0,0 +1,28 @@ +package org.symphonyoss.symphony.messageml.markdown.nodes.form; + + +/** + * Class that Represents a Markdown Node for the "RoomSelector" form element. + * + * @author Mohamed Rojbeni + * @since 09/15/2023 + */ +public class RoomSelectorNode extends FormElementNode implements PlaceholderLabelTooltipNode { + private final static String MARKDOWN = "Room Selector"; + + private String placeholder; + private String label; + private String tooltip; + + public RoomSelectorNode(String placeholder, String label, String tooltip) { + super(MARKDOWN, placeholder); + this.placeholder = placeholder; + this.label = label; + this.tooltip = tooltip; + } + + @Override + public String getText() { + return generateMarkdownPlaceholderLabelAndTooltip(placeholder, label, tooltip); + } +} diff --git a/src/test/java/org/symphonyoss/symphony/messageml/elements/form/RoomSelectorTest.java b/src/test/java/org/symphonyoss/symphony/messageml/elements/form/RoomSelectorTest.java new file mode 100644 index 00000000..887adcd0 --- /dev/null +++ b/src/test/java/org/symphonyoss/symphony/messageml/elements/form/RoomSelectorTest.java @@ -0,0 +1,336 @@ +package org.symphonyoss.symphony.messageml.elements.form; + +import static org.junit.Assert.assertEquals; +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; +import org.symphonyoss.symphony.messageml.bi.BiItem; +import org.symphonyoss.symphony.messageml.elements.Element; +import org.symphonyoss.symphony.messageml.elements.ElementTest; +import org.symphonyoss.symphony.messageml.elements.Form; +import org.symphonyoss.symphony.messageml.elements.MessageML; +import org.symphonyoss.symphony.messageml.elements.RoomSelector; +import org.symphonyoss.symphony.messageml.exceptions.InvalidInputException; +import org.symphonyoss.symphony.messageml.exceptions.ProcessingException; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class RoomSelectorTest extends ElementTest { + private static final String FORM_ID_ATTR = "id"; + + @Test + public void sendValidRoomSelectorOnPresentationML() throws Exception { + context.parseMessageML("
" + + ACTION_BTN_ELEMENT + + "", null, MessageML.MESSAGEML_VERSION); + assertDataFromValidParsedTag("one-name", "some-placeholder", true); + } + + @Test + public void sendValidRoomSelectorWithLabelAndTitleOnPresentationML() throws Exception { + context.parseMessageML("" + + "
" + + "" + + ACTION_BTN_ELEMENT + + "
", null, MessageML.MESSAGEML_VERSION); + + Element messageML = context.getMessageML(); + Element form = messageML.getChildren().get(0); + Element roomSelector = form.getChildren().get(0); + + assertEquals(Form.class, form.getClass()); + assertEquals(RoomSelector.class, roomSelector.getClass()); + + String presentationML = context.getPresentationML(); + String roomSelectorRegex = ".*(\"room-selector-(.*?)\").*"; + Pattern pattern = Pattern.compile(roomSelectorRegex); + Matcher matcher = pattern.matcher(presentationML); + String uniqueLabelId = matcher.matches() ? matcher.group(2) : null; + + String expectedPresentationML = "
" + + "
" + + "
" + + "" + + "" + + "
" + + "
" + + ACTION_BTN_ELEMENT + + "
" + + "
"; + + assertEquals("The parsed content should be equivalent to the expected presentation ML", + expectedPresentationML, presentationML); + + String expectedMarkdownText = "[label here][title here]"; + assertEquals( + "Form (log into desktop client to answer):\n---\n(Room Selector:" + expectedMarkdownText + + ")" + ACTION_BTN_MARKDOWN + + "\n---\n", context.getMarkdown()); + } + + @Test + public void sendValidRoomSelectorWithValue() throws Exception { + context.parseMessageML("" + + "
" + + " " + + " " + + " " + + "
", null, MessageML.MESSAGEML_VERSION); + + String presentationML = context.getPresentationML(); + + String expectedPresentationML = + "
" + + "
" + + "
"; + + assertEquals("The parsed content should be equivalent to the expected presentation ML", + expectedPresentationML, presentationML); + } + + @Test + public void sendValidRoomSelectorWithValueMultipleIds() throws Exception { + context.parseMessageML("" + + "
" + + " " + + + " " + + " " + + "
", null, MessageML.MESSAGEML_VERSION); + + String presentationML = context.getPresentationML(); + + String expectedPresentationML = "
" + + "
" + + "
" + + + " " + + "
" + + "
"; + + assertEquals("The parsed content should be equivalent to the expected presentation ML", + expectedPresentationML, presentationML); + } + + @Test + public void sendInvalidRoomSelectorWithValueNotALong() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Attribute \"value\" contains an unsupported format, should be an array of room ids"); + context.parseMessageML("" + + "
" + + " " + + + " " + + " " + + "
", null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendInvalidRoomSelectorWithValueWrongFormat() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Attribute \"value\" contains an unsupported format, should be an array of room ids"); + context.parseMessageML("" + + "
" + + " " + + + " " + + " " + + "
", null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendInvalidAttrRoomSelectorOnPresentationML() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"dummy\" is not allowed in \"room-selector\""); + context.parseMessageML("
", + null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendInvalidContentRoomSelectorOnPresentationML() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Element \"room-selector\" may not have child elements or text content"); + context.parseMessageML("
hey
", + null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendValidRoomSelector() throws Exception { + String name = "some-name"; + context.parseMessageML( + "
" + ACTION_BTN_ELEMENT + + "
", null, MessageML.MESSAGEML_VERSION); + assertDataFromValidParsedTag(name, "Add some user here...", false); + } + + @Test + public void sendValidRoomSelectorWithClosingTag() throws Exception { + String name = "other-name"; + context.parseMessageML( + "
" + ACTION_BTN_ELEMENT + + "
", null, MessageML.MESSAGEML_VERSION); + assertDataFromValidParsedTag(name, "Add some user here...", null); + } + + @Test + public void sendRoomSelectorWithChildElement() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Element \"room-selector\" may not have child elements or text content"); + context.parseMessageML("
a
", null, + MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendRoomSelectorWithInvalidRequiredAttribute() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage( + "Attribute \"required\" of element \"room-selector\" can only be one of the following " + + "values: [true, false]"); + context.parseMessageML("
", + null, MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendRoomSelectorWithInvalidAttribute() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("Attribute \"class\" is not allowed in \"room-selector\""); + context.parseMessageML("
", null, + MessageML.MESSAGEML_VERSION); + } + + @Test + public void sendRoomSelectorWithBlankName() throws Exception { + expectedException.expect(InvalidInputException.class); + expectedException.expectMessage("The attribute \"name\" is required"); + context.parseMessageML("
", null, + MessageML.MESSAGEML_VERSION); + } + + + @Test + public void sendRoomSelectorOutsideForm() throws Exception { + context.parseMessageML("
" + ACTION_BTN_ELEMENT + + "
", null, MessageML.MESSAGEML_VERSION); + + MessageML messageML = context.getMessageML(); + Element form = messageML.getChildren().get(0); + Element roomSelector = form.getChildren().get(0).getChildren().get(0); + assertEquals(form.getClass(), Form.class); + assertEquals(roomSelector.getClass(), RoomSelector.class); + assertEquals( + "
" + + ACTION_BTN_ELEMENT + "
", context.getPresentationML()); + assertEquals("Form (log into desktop client to answer):\n---\n(Room Selector)\n\n" + + ACTION_BTN_MARKDOWN + + "\n---\n", context.getMarkdown()); + } + + @Test + public void testBiContextRoomSelector() + throws InvalidInputException, IOException, ProcessingException { + MessageMLContext messageMLContext = new MessageMLContext(null); + String input = "" + + "
" + + "" + + "" + + "" + + "" + + "
"; + + messageMLContext.parseMessageML(input, null, MessageML.MESSAGEML_VERSION); + List items = messageMLContext.getBiContext().getItems(); + + Map expectedAttributesFirst = Stream.of(new Object[][] { + {BiFields.PLACEHOLDER.getValue(), 1}, + {BiFields.TITLE.getValue(), 1}, + {BiFields.LABEL.getValue(), 1}, + {BiFields.REQUIRED.getValue(), 1}, + }).collect(Collectors.toMap(property -> property[0], property -> property[1])); + + Map expectedAttributesSecond = Stream.of(new Object[][] { + {BiFields.PLACEHOLDER.getValue(), 1}, + {BiFields.TITLE.getValue(), 1}, + }).collect(Collectors.toMap(property -> property[0], property -> property[1])); + + BiItem roomSelectorFirstBiItemExpected = new BiItem(BiFields.ROOM_SELECTOR.getValue(), + expectedAttributesFirst.entrySet() + .stream() + .collect(Collectors.toMap(e -> + String.valueOf(e.getKey()), Map.Entry::getValue))); + + BiItem roomSelectorSecondBiItemExpected = new BiItem(BiFields.ROOM_SELECTOR.getValue(), + expectedAttributesSecond.entrySet() + .stream() + .collect(Collectors.toMap(e -> + String.valueOf(e.getKey()), Map.Entry::getValue))); + + + assertEquals(5, items.size()); + assertEquals(BiFields.ROOM_SELECTOR.getValue(), items.get(0).getName()); + assertEquals(BiFields.ROOM_SELECTOR.getValue(), items.get(1).getName()); + assertSameBiItem(roomSelectorFirstBiItemExpected, items.get(0)); + assertSameBiItem(roomSelectorSecondBiItemExpected, items.get(1)); + assertMessageLengthBiItem(items.get(4), input.length()); + } + + private void assertDataFromValidParsedTag(String dataName, String dataPlaceholder, + Boolean dataRequired) { + MessageML messageML = context.getMessageML(); + Element form = messageML.getChildren().get(0); + Element roomSelector = form.getChildren().get(0); + assertEquals(form.getClass(), Form.class); + assertEquals(roomSelector.getClass(), RoomSelector.class); + assertEquals( + "
" + ACTION_BTN_ELEMENT + "
", context.getPresentationML()); + String expectedMarkdownText = + (dataPlaceholder != null) ? ":[" + addEscapeCharacter(dataPlaceholder) + "]" : ""; + assertEquals( + "Form (log into desktop client to answer):\n---\n(Room Selector" + expectedMarkdownText + + ")" + ACTION_BTN_MARKDOWN + + "\n---\n", context.getMarkdown()); + } + +}