Repository: wicket Updated Branches: refs/heads/WICKET-6220-tag-tester-void-elements b27e6aca9 -> 069735395
WICKET-6220 improved support for void elements; unified code for createTagsXXX and createTagXX Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/06973539 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/06973539 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/06973539 Branch: refs/heads/WICKET-6220-tag-tester-void-elements Commit: 069735395955b6a68347fae61217df05811d8a43 Parents: b27e6ac Author: Sven Meier <[email protected]> Authored: Mon Aug 1 10:49:21 2016 +0200 Committer: Sven Meier <[email protected]> Committed: Mon Aug 1 10:49:21 2016 +0200 ---------------------------------------------------------------------- .../apache/wicket/util/tester/TagTester.java | 302 +++++++------------ .../wicket/util/tester/TagTesterTest.java | 114 ++++++- 2 files changed, 214 insertions(+), 202 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/06973539/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java b/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java index 04f8280..610b181 100644 --- a/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java +++ b/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java @@ -16,20 +16,22 @@ */ package org.apache.wicket.util.tester; +import static org.apache.wicket.markup.parser.filter.HtmlHandler.requiresCloseTag; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Map; +import java.util.Stack; +import java.util.function.Function; import java.util.regex.Pattern; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.markup.parser.XmlPullParser; import org.apache.wicket.markup.parser.XmlTag; -import org.apache.wicket.markup.parser.filter.HtmlHandler; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.value.IValueMap; - /** * Tag tester is used to test that a generated markup tag contains the correct attributes, values * etc. This can be done instead of comparing generated markup with some expected markup. The @@ -359,7 +361,8 @@ public class TagTester */ public String getValue() { - if (openTag == closeTag) { + if (openTag == closeTag) + { return null; } @@ -374,85 +377,33 @@ public class TagTester * that it will return the first tag which matches the criteria. * * @param markup - * the markup to look for the tag to create the <code>TagTester</code> from - * the value which the attribute must have + * the markup to look for the tag to create the <code>TagTester</code> from the value + * which the attribute must have * @return the <code>TagTester</code> which matches the tag by name in the markup */ - public static TagTester createTagByAttribute(String markup, String tagName) + public static TagTester createTagByName(String markup, String tagName) { - TagTester tester = null; - - if (Strings.isEmpty(markup) == false && Strings.isEmpty(tagName) == false) + List<TagTester> tester = createTags(markup, xmlTag -> xmlTag.getName().equalsIgnoreCase(tagName), true); + if ((tester == null) || (tester.size() == 0)) { - try - { - // remove the CDATA and - // the id attribute of the component because it is often the same as the element's id - markup = AJAX_COMPONENT_CDATA_OPEN.matcher(markup).replaceAll("<component>"); - markup = AJAX_COMPONENT_CDATA_CLOSE.matcher(markup).replaceAll("</component>"); - - XmlPullParser parser = new XmlPullParser(); - parser.parse(markup); - - XmlTag elm; - XmlTag openTag = null; - XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null && closeTag == null) - { - XmlTag xmlTag = elm; - - String xmlTagName = xmlTag.getName(); - if (openTag == null && xmlTagName.equalsIgnoreCase(tagName)) - { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } - } - else if (openTag != null) - { - String openTagName = openTag.getName(); - if (xmlTag.isOpen() && xmlTagName.equals(openTagName)) - { - level++; - } - - if (xmlTag.isClose()) - { - if (xmlTagName.equals(openTagName)) - { - if (level == 0) - { - closeTag = xmlTag; - closeTag.setOpenTag(openTag); - } - else - { - level--; - } - } - } - } - } - - if (openTag != null && closeTag != null) - { - tester = new TagTester(parser, openTag, closeTag); - } - } - catch (Exception e) - { - throw new WicketRuntimeException(e); - } + return null; } + return tester.get(0); + } - return tester; + /** + * Static factory method for creating a <code>TagTester</code> based on a tag name. Please note + * that it will return the first tag which matches the criteria. + * + * @param markup + * the markup to look for the tag to create the <code>TagTester</code> from the value + * which the attribute must have + * @return the <code>TagTester</code> which matches the tag by name in the markup + */ + @Deprecated + public static TagTester createTagByAttribute(String markup, String tagName) + { + return createTagByName(markup, tagName); } /** @@ -472,97 +423,18 @@ public class TagTester */ public static TagTester createTagByAttribute(String markup, String attribute, String value) { - TagTester tester = null; - - if (Strings.isEmpty(markup) == false && Strings.isEmpty(attribute) == false && - Strings.isEmpty(value) == false) + List<TagTester> tester = createTagsByAttribute(markup, attribute, value, true); + if ((tester == null) || (tester.size() == 0)) { - try - { - // remove the CDATA and - // the id attribute of the component because it is often the same as the element's id - markup = AJAX_COMPONENT_CDATA_OPEN.matcher(markup).replaceAll("<component>"); - markup = AJAX_COMPONENT_CDATA_CLOSE.matcher(markup).replaceAll("</component>"); - - XmlPullParser parser = new XmlPullParser(); - parser.parse(markup); - - XmlTag elm; - XmlTag openTag = null; - XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null && closeTag == null) - { - XmlTag xmlTag = elm; - - if (openTag == null) - { - IValueMap attributeMap = xmlTag.getAttributes(); - - for (Map.Entry<String, Object> entry : attributeMap.entrySet()) - { - String attr = entry.getKey(); - if (attr.equals(attribute) && value.equals(entry.getValue())) - { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } - } - } - } - else - { - if (xmlTag.isOpen() && xmlTag.getName().equals(openTag.getName())) - { - level++; - } - - if (xmlTag.isClose()) - { - if (xmlTag.getName().equals(openTag.getName())) - { - if (level == 0) - { - closeTag = xmlTag; - closeTag.setOpenTag(openTag); - } - else - { - level--; - } - } - } - } - } - - if (openTag != null && closeTag != null) - { - tester = new TagTester(parser, openTag, closeTag); - } - else if (openTag != null) - { - tester = new TagTester(parser, openTag, openTag); - } - } - catch (Exception e) - { - throw new WicketRuntimeException(e); - } + return null; } - - return tester; + return tester.get(0); } /** * Static factory method for creating a <code>TagTester</code> based on a tag found by an * attribute with a specific value. Please note that it will return the first tag which matches - * the criteria. It's therefore good for attributes such as "id" or "wicket:id", but only if + * the criteria. It's therefore good for attributes suck as "id" or "wicket:id", but only if * "wicket:id" is unique in the specified markup. * * @param markup @@ -574,14 +446,33 @@ public class TagTester * @return the <code>TagTester</code> which matches the tag in the markup, that has the given * value on the given attribute */ + @Deprecated public static TagTester createTagsByAttribute(String markup, String attribute, String value) { - List<TagTester> tester = createTagsByAttribute(markup, attribute, value, true); - if ((tester == null) || (tester.size() == 0)) + return createTagByAttribute(markup, attribute, value); + } + + /** + * find the correct openTag to the given closeTag and remove all unclosed openTags between both + * in given array {@code stack} + * + * @param closeTag + * tag to search for corresponding openTag + * @param stack + * array of unclosed openTags + * @return corresponding openTag or {@code null} + */ + private static XmlTag findOpenTag(XmlTag closeTag, Stack<XmlTag> stack) + { + while (stack.size() > 0) { - return null; + XmlTag popped = stack.pop(); + if (popped.getName().equals(closeTag.getName())) + { + return popped; + } } - return tester.get(0); + return null; } /** @@ -601,13 +492,20 @@ public class TagTester * @return the <code>TagTester</code> which matches the tag in the markup, that has the given * value on the given attribute */ - public static List<TagTester> createTagsByAttribute(String markup, String attribute, - String value, boolean stopAfterFirst) + public static List<TagTester> createTagsByAttribute(String markup, String attribute, String value, boolean stopAfterFirst) + { + if (Strings.isEmpty(attribute) || Strings.isEmpty(value)) { + return Collections.emptyList(); + } + + return createTags(markup, xmlTag -> value.equals(xmlTag.getAttributes().get(attribute)), stopAfterFirst); + } + + public static List<TagTester> createTags(String markup, Function<XmlTag, Boolean> accept, boolean stopAfterFirst) { List<TagTester> testers = new ArrayList<>(); - if ((Strings.isEmpty(markup) == false) && (Strings.isEmpty(attribute) == false) && - (Strings.isEmpty(value) == false)) + if ((Strings.isEmpty(markup) == false)) { try { @@ -619,70 +517,74 @@ public class TagTester XmlPullParser parser = new XmlPullParser(); parser.parse(markup); - XmlTag elm; XmlTag openTag = null; XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null) + + // temporary Tag-Hierarchy after openTag + Stack<XmlTag> stack = new Stack<>(); + + while (true) { - XmlTag xmlTag = elm; + XmlTag xmlTag = parser.nextTag(); + if (xmlTag == null) + { + break; + } + if (openTag == null) { - IValueMap attributeMap = xmlTag.getAttributes(); - for (Map.Entry<String, Object> entry : attributeMap.entrySet()) + if (accept.apply(xmlTag)) { - if (entry.getKey().equals(attribute) && value.equals(entry.getValue())) + if (xmlTag.isOpen()) { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } + openTag = xmlTag; + } + else if (xmlTag.isOpenClose()) + { + openTag = xmlTag; + closeTag = xmlTag; } } } else { - if (xmlTag.isOpen() && xmlTag.getName().equals(openTag.getName())) + if (xmlTag.isOpen() && !xmlTag.isOpenClose()) { - level++; + stack.push(xmlTag); } - if (xmlTag.isClose()) { - if (xmlTag.getName().equals(openTag.getName())) + XmlTag foundTag = findOpenTag(xmlTag, stack); + if (foundTag == null) { - if (level == 0) + if (xmlTag.getName().equals(openTag.getName())) { closeTag = xmlTag; closeTag.setOpenTag(openTag); } + else if (requiresCloseTag(openTag.getName()) == false) + { + // no closeTag for current openTag (allowed) + closeTag = openTag; + } else { - level--; + // no closeTag for current openTag (invalid structure) + // thus reset state + openTag = null; + closeTag = null; } } } } - if ((openTag != null) && (closeTag != null) && (level == 0)) + if ((openTag != null) && (closeTag != null)) { TagTester tester = new TagTester(parser, openTag, closeTag); testers.add(tester); openTag = null; closeTag = null; } - else if (openTag != null && !HtmlHandler.requiresCloseTag(openTag.getName())) - { - TagTester tester = new TagTester(parser, openTag, openTag); - testers.add(tester); - openTag = null; - closeTag = null; - } if (stopAfterFirst && (closeTag != null)) { @@ -698,4 +600,4 @@ public class TagTester return testers; } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/06973539/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java b/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java index d184869..f992310 100644 --- a/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java @@ -342,7 +342,117 @@ public class TagTesterTest extends Assert @Test public void valueFromTagsByAttribute() { - TagTester tagTester = TagTester.createTagsByAttribute(MARKUP_1, "id", "test2"); + TagTester tagTester = TagTester.createTagByAttribute(MARKUP_1, "id", "test2"); assertEquals("mock", tagTester.getValue()); } -} + + private static final String MARKUP = + "<wicket:panel>" + + "<ul wicket:id=\"container\">" + + "<wicket:container wicket:id=\"items\">" + + "<li wicket:id=\"item\" id=\"item1\">" + + "<p wicket:id=\"p\" id=\"p1\">" + + "<img wicket:id=\"img\" src=\"bild1.jpg\">" + + "</p>" + + "<hr wicket:id=\"hr\" id=\"hr1\"/>" + + "</li>" + + "<li wicket:id=\"item\" id=\"item2\">" + + "<p wicket:id=\"p\" id=\"p2\">" + + "<img wicket:id=\"img\" src=\"bild2.jpg\">" + + "<hr wicket:id=\"hr\" id=\"hr2\"/>" + + "</li>" + + "</wicket:container>" + + "</ul>" + + "</wicket:panel>"; + + private static final String WRONG_MARKUP = + "<wicket:panel>" + + "<ul wicket:id=\"container\">" + + "<wicket:container wicket:id=\"items\">" + + "<li wicket:id=\"item\" id=\"item1\">" + + "<span wicket:id=\"p\" id=\"p1\">" + + "<img wicket:id=\"img\" src=\"bild1.jpg\">" + + "<hr wicket:id=\"hr\" id=\"hr1\"/>" + + "</li>" + + "<li wicket:id=\"item\" id=\"item2\">" + + "<span wicket:id=\"p\" id=\"p2\">" + + "<img wicket:id=\"img\" src=\"bild2.jpg\">" + + "<hr wicket:id=\"hr\" id=\"hr2\"/>" + + "</span>" + + "</li>" + + "</wicket:container>" + + "</ul>" + + "</wicket:panel>"; + + /** + * WICKET-6220 + */ + @Test + public void testOpenAndClose() { + List<TagTester> tags = TagTester.createTagsByAttribute(MARKUP, "wicket:id", "item", false); + assertEquals(2, tags.size()); + assertEquals("li", tags.get(0).getName()); + assertEquals("item1", tags.get(0).getAttribute("id")); + assertEquals("<p wicket:id=\"p\" id=\"p1\"><img wicket:id=\"img\" src=\"bild1.jpg\"></p><hr wicket:id=\"hr\" id=\"hr1\"/>", tags.get(0).getValue()); + assertEquals("li", tags.get(1).getName()); + assertEquals("item2", tags.get(1).getAttribute("id")); + assertEquals("<p wicket:id=\"p\" id=\"p2\"><img wicket:id=\"img\" src=\"bild2.jpg\"><hr wicket:id=\"hr\" id=\"hr2\"/>", tags.get(1).getValue()); + } + + /** + * WICKET-6220 + */ + @Test + public void testWrongHtmlStructure() { + List<TagTester> tags = TagTester.createTagsByAttribute(WRONG_MARKUP, "wicket:id", "p", false); + assertEquals(1, tags.size()); + assertEquals("span", tags.get(0).getName()); + assertEquals("p2", tags.get(0).getAttribute("id")); + assertEquals("<img wicket:id=\"img\" src=\"bild2.jpg\"><hr wicket:id=\"hr\" id=\"hr2\"/>", tags.get(0).getValue()); + } + + /** + * WICKET-6220 + */ + @Test + public void testOpenOrClose() { + List<TagTester> tags = TagTester.createTagsByAttribute(MARKUP, "wicket:id", "p", false); + assertEquals(2, tags.size()); + assertEquals("p", tags.get(0).getName()); + assertEquals("p1", tags.get(0).getAttribute("id")); + assertEquals("<img wicket:id=\"img\" src=\"bild1.jpg\">", tags.get(0).getValue()); + assertEquals("p", tags.get(1).getName()); + assertEquals("p2", tags.get(1).getAttribute("id")); + assertEquals("<p wicket:id=\"p\" id=\"p2\">", tags.get(1).getMarkup()); + } + + /** + * WICKET-6220 + */ + @Test + public void testOpen() { + List<TagTester> tags = TagTester.createTagsByAttribute(MARKUP, "wicket:id", "img", false); + assertEquals(2, tags.size()); + assertEquals("img", tags.get(0).getName()); + assertEquals("bild1.jpg", tags.get(0).getAttribute("src")); + assertEquals("<img wicket:id=\"img\" src=\"bild1.jpg\">", tags.get(0).getMarkup()); + assertEquals("img", tags.get(1).getName()); + assertEquals("bild2.jpg", tags.get(1).getAttribute("src")); + assertEquals("<img wicket:id=\"img\" src=\"bild2.jpg\">", tags.get(1).getMarkup()); + } + + /** + * WICKET-6220 + */ + @Test + public void testOpenClose() { + List<TagTester> tags = TagTester.createTagsByAttribute(MARKUP, "wicket:id", "hr", false); + assertEquals(2, tags.size()); + assertEquals("hr", tags.get(0).getName()); + assertEquals("hr1", tags.get(0).getAttribute("id")); + assertEquals("<hr wicket:id=\"hr\" id=\"hr1\"/>", tags.get(0).getMarkup()); + assertEquals("hr", tags.get(1).getName()); + assertEquals("hr2", tags.get(1).getAttribute("id")); + assertEquals("<hr wicket:id=\"hr\" id=\"hr2\"/>", tags.get(1).getMarkup()); + } +} \ No newline at end of file
