001: /*
002: * $Id: TagTester.java 461939 2006-08-24 21:59:20Z frankbille $
003: * $Revision: 461939 $
004: * $Date: 2006-08-24 23:59:20 +0200 (Thu, 24 Aug 2006) $
005: *
006: * ==============================================================================
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: */
019: package wicket.util.tester;
020:
021: import java.util.Iterator;
022:
023: import wicket.markup.MarkupElement;
024: import wicket.markup.parser.XmlPullParser;
025: import wicket.markup.parser.XmlTag;
026: import wicket.util.string.Strings;
027: import wicket.util.value.AttributeMap;
028:
029: /**
030: * Tag tester is used to test that a generated markup tag contains the correct
031: * attributes, values etc. This can be done instead of comparing generated
032: * markup with some expected markup. The advantage of this is that a lot of
033: * tests doesn't fail, when the generated markup changes just a little bit.
034: * <p>
035: * It also gives a more programmatic way of testing the generated output, by not
036: * having to worry about how the markup looks precisely instead of which
037: * attributes exists on the given tags and what values they have.
038: * <p>
039: * Example:
040: *
041: * <pre>
042: * ...
043: * TagTester tagTester = application.getTagByWicketId("form");
044: * assertTrue(tag.hasAttribute("action"));
045: * ...
046: * </pre>
047: *
048: * @author Frank Bille (billen)
049: */
050: public class TagTester {
051: private XmlTag openTag;
052:
053: private XmlTag closeTag;
054:
055: private XmlPullParser parser;
056:
057: /**
058: * Construct.
059: *
060: * @param parser
061: * @param openTag
062: * @param closeTag
063: */
064: private TagTester(XmlPullParser parser, XmlTag openTag,
065: XmlTag closeTag) {
066: this .parser = parser;
067: this .openTag = openTag;
068: this .closeTag = closeTag;
069: }
070:
071: /**
072: * Get the tag name.
073: *
074: * @return Tag name.
075: */
076: public String getName() {
077: return openTag.getName();
078: }
079:
080: /**
081: * Does the tag contain the attribute. Please note that this is case
082: * in-sensitive, because attributes in HTML may be case in-sensitive.
083: *
084: * @param attribute
085: * The attribute to look for in the tag.
086: * @return True if the tag has an attribute, false if not.
087: */
088: public boolean hasAttribute(String attribute) {
089: boolean hasAttribute = false;
090:
091: if (getAttribute(attribute) != null) {
092: hasAttribute = true;
093: }
094:
095: return hasAttribute;
096: }
097:
098: /**
099: * Get the attribute value for the given attribute. Please note that this is
100: * case in-sensitive, because attributes in HTML may be case in-sensitive.
101: *
102: * @param attribute
103: * The attribute to look for in the tag.
104: * @return The value of the attribute or null if it isn't found.
105: */
106: public String getAttribute(String attribute) {
107: String value = null;
108:
109: AttributeMap attributeMap = openTag.getAttributes();
110:
111: if (attributeMap != null) {
112: for (Iterator iter = attributeMap.keySet().iterator(); iter
113: .hasNext();) {
114: String attr = (String) iter.next();
115:
116: if (attr.equalsIgnoreCase(attribute)) {
117: value = attributeMap.getString(attr);
118: }
119: }
120: }
121:
122: return value;
123: }
124:
125: /**
126: * Check if an attribute contains the specified partial value.
127: * <p>
128: * For example:
129: *
130: * <p>
131: * <b>Markup:</b>
132: *
133: * <pre>
134: * <span wicket:id="helloComp" class="style1 style2">Hello</span>
135: * </pre>
136: *
137: * <p>
138: * <b>Test</b>
139: *
140: * <pre>
141: * TagTester tester = application.getTagByWicketId("helloComp");
142: * assertTrue(tester.getAttributeContains("class", "style2"));
143: * </pre>
144: *
145: * @param attribute
146: * The attribute to test on
147: * @param partialValue
148: * The partial value to test if the attribute value contains.
149: * @return True if the attribute value contains the partial value.
150: */
151: public boolean getAttributeContains(String attribute,
152: String partialValue) {
153: boolean contains = false;
154:
155: if (partialValue != null) {
156: String value = getAttribute(attribute);
157:
158: if (value != null) {
159: if (value.indexOf(partialValue) > -1) {
160: contains = true;
161: }
162: }
163: }
164:
165: return contains;
166: }
167:
168: /**
169: * Check if an attributes value is the exact same as the given parameter.
170: *
171: * @param attribute
172: * The attribute to test.
173: * @param expected
174: * The value which should be the same at the attributes value
175: * @return True if the attributes value is the same as the parameter.
176: */
177: public boolean getAttributeIs(String attribute, String expected) {
178: boolean is = false;
179:
180: String val = getAttribute(attribute);
181:
182: if (val == null && expected == null || expected != null
183: && expected.equals(val)) {
184: is = true;
185: }
186:
187: return is;
188: }
189:
190: /**
191: * Check if an attributes value ends with the given parameter.
192: *
193: * @param attribute
194: * @param expected
195: * @return True if the attributes value ends with the expected value
196: */
197: public boolean getAttributeEndsWith(String attribute,
198: String expected) {
199: boolean endsWith = false;
200:
201: if (expected != null) {
202: String val = getAttribute(attribute);
203:
204: if (val != null) {
205: if (val.endsWith(expected)) {
206: endsWith = true;
207: }
208: }
209: }
210:
211: return endsWith;
212: }
213:
214: /**
215: * Check if the tag has a child with the tagName.
216: *
217: * @param tagName
218: * The tag name to search for.
219: * @return True if this tag has a child with the given tagName.
220: */
221: public boolean hasChildTag(String tagName) {
222: boolean hasChild = false;
223:
224: if (Strings.isEmpty(tagName)) {
225: throw new IllegalArgumentException(
226: "You need to provide a not empty/not null argument.");
227: }
228:
229: if (openTag.isOpen()) {
230: try {
231: // Get the content of the tag
232: int startPos = openTag.getPos() + openTag.getLength();
233: int endPos = closeTag.getPos();
234: String markup = parser.getInput(startPos, endPos)
235: .toString();
236:
237: if (Strings.isEmpty(markup) == false) {
238: XmlPullParser p = new XmlPullParser();
239: p.parse(markup);
240:
241: XmlTag tag = null;
242: while ((tag = (XmlTag) p.nextTag()) != null) {
243: if (tagName.equalsIgnoreCase(tag.getName())) {
244: hasChild = true;
245: break;
246: }
247: }
248: }
249: } catch (Exception e) {
250: throw new IllegalStateException();
251: }
252:
253: }
254:
255: return hasChild;
256: }
257:
258: /**
259: * Get a child tag for testing. If this tag contains child tags, you can get
260: * one of them as a TagTester.
261: *
262: * @param attribute
263: * The attribute on the child tag to search for
264: * @param value
265: * The value that the attribute must have.
266: * @return The TagTester for the child tag.
267: */
268: public TagTester getChild(String attribute, String value) {
269: TagTester childTag = null;
270:
271: if (openTag.isOpen()) {
272: // Generate the markup for this tag
273: String markup = getMarkup();
274:
275: if (Strings.isEmpty(markup) == false) {
276: childTag = TagTester.createTagByAttribute(markup,
277: attribute, value);
278: }
279: }
280:
281: return childTag;
282: }
283:
284: /**
285: * Get markup for this tag. This includes every markup which is between the
286: * open tag and the close tag.
287: *
288: * @return The entire markup between the open tag and the close tag.
289: */
290: public String getMarkup() {
291: int openPos = openTag.getPos();
292: int closePos = closeTag.getPos() + closeTag.getLength();
293: String markup = parser.getInput(openPos, closePos).toString();
294:
295: return markup;
296: }
297:
298: /**
299: * Static factory method for creating a TagTester based on a tag found by an
300: * attribute with a specific value. Please note that it will return the
301: * first tag which matches the criteria. It's therefore good for attributes
302: * suck as "id" or "wicket:id", but only if "wicket:id" is unique in the
303: * specified markup.
304: *
305: * @param markup
306: * The markup to look for the tag to create the TagTester from.
307: * @param attribute
308: * The attribute which should be on the tag in the markup.
309: * @param value
310: * The value which the attribute must have.
311: * @return The TagTester which matches the tag in the markup, that has the
312: * given value on the given attribute.
313: */
314: public static TagTester createTagByAttribute(String markup,
315: String attribute, String value) {
316: TagTester tester = null;
317:
318: if (Strings.isEmpty(markup) == false
319: && Strings.isEmpty(attribute) == false
320: && Strings.isEmpty(value) == false) {
321: try {
322: XmlPullParser parser = new XmlPullParser();
323: parser.parse(markup);
324:
325: MarkupElement elm = null;
326: XmlTag openTag = null;
327: XmlTag closeTag = null;
328: int level = 0;
329: while ((elm = parser.nextTag()) != null
330: && closeTag == null) {
331: if (elm instanceof XmlTag) {
332: XmlTag xmlTag = (XmlTag) elm;
333:
334: if (openTag == null) {
335: AttributeMap attributeMap = xmlTag
336: .getAttributes();
337:
338: for (Iterator iter = attributeMap.keySet()
339: .iterator(); iter.hasNext();) {
340: String attr = (String) iter.next();
341:
342: if (attr.equals(attribute)
343: && value.equals(attributeMap
344: .get(attr))) {
345: if (xmlTag.isOpen()) {
346: openTag = xmlTag;
347: } else if (xmlTag.isOpenClose()) {
348: openTag = xmlTag;
349: closeTag = xmlTag;
350: }
351: }
352: }
353: } else {
354: if (xmlTag.isOpen()
355: && xmlTag.getName().equals(
356: openTag.getName())) {
357: level++;
358: }
359:
360: if (xmlTag.isClose()) {
361: if (xmlTag.getName().equals(
362: openTag.getName())) {
363: if (level == 0) {
364: closeTag = xmlTag;
365: closeTag.setOpenTag(openTag);
366: } else {
367: level--;
368: }
369: }
370: }
371: }
372: }
373: }
374:
375: if (openTag != null && closeTag != null) {
376: tester = new TagTester(parser, openTag, closeTag);
377: }
378: } catch (Exception e) {
379: throw new IllegalStateException();
380: }
381: }
382:
383: return tester;
384: }
385: }
|