// Copyright 2006, 2007, 2008, 2009, 2011 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.MarkupWriterListener;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.dom.XMLMarkupModel;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
import org.testng.annotations.Test;
public class MarkupWriterImplTest extends InternalBaseTestCase
{
/**
* TAP5-349
*/
@Test
public void single_root_element_only()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.element("root1");
w.end();
try
{
w.element("root2");
unreachable();
}
catch (RuntimeException ex)
{
assertEquals(ex.getMessage(),
"A document must have exactly one root element. Element <root1> is already the root element.");
}
}
@Test
public void write_whitespace_before_start_of_root_element_is_retained()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.write(" ");
w.element("root");
w.end();
assertEquals(w.toString(), "<?xml version=\"1.0\"?>\n" + " <root/>");
}
@Test
public void write_whitespace_after_end_of_root_element_is_retained_in_preamble()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.element("root");
w.end();
w.write(" ");
assertEquals(w.toString(), "<?xml version=\"1.0\"?>\n <root/>");
}
@Test
public void preamble_content() throws Exception
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.comment(" preamble start ");
w.write("preamble text");
w.cdata("CDATA content");
w.writeRaw(" ");
w.element("root");
w.end();
// You really shouldn't have any text after the close tag of the document, so it
// gets moved to the top, to the "preamble", before the first element.
w.comment(" content after root element in preamble ");
assertEquals(w.getDocument().toString(), readFile("preamble_content.txt"));
}
/** TAP5-1145 */
@Test
public void document_without_root_element()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.write("preamble text");
assertEquals(w.getDocument().toString(), "<?xml version=\"1.0\"?>\n" + "preamble text");
}
@Test(expectedExceptions = IllegalStateException.class)
public void attribute_ns_with_no_current_element()
{
MarkupWriter w = new MarkupWriterImpl();
w.attributeNS("foo", "bar", "baz");
}
@Test(expectedExceptions = IllegalStateException.class)
public void define_namespace_with_no_current_element()
{
MarkupWriter w = new MarkupWriterImpl();
w.defineNamespace("foo", "bar");
}
@Test(expectedExceptions = IllegalStateException.class)
public void end_with_no_current_element()
{
MarkupWriter w = new MarkupWriterImpl();
w.end();
}
@Test(expectedExceptions = IllegalStateException.class)
public void attributes_with_no_current_element()
{
MarkupWriter w = new MarkupWriterImpl();
w.attributes("fail", "now");
}
@Test
public void current_element_at_end_of_root_element_is_null()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
assertNull(w.end());
}
@Test
public void element_nesting()
{
MarkupWriter w = new MarkupWriterImpl();
Element root = w.element("root");
w.attributes("foo", "bar");
w.write("before child");
assertNotSame(w.element("nested"), root);
w.write("inner text");
assertSame(w.end(), root);
w.write("after child");
root.attribute("gnip", "gnop");
assertEquals(w.toString(),
"<root gnip=\"gnop\" foo=\"bar\">before child<nested>inner text</nested>after child</root>");
}
@Test
public void element_with_attributes()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("img", "src", "foo.png", "width", 20, "height", 20);
w.end();
// img is a tag with an end tag style of omit, so no close tag is written.
assertEquals(w.toString(), "<img height=\"20\" width=\"20\" src=\"foo.png\"/>");
}
@Test
public void attributes()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.attributes("foo", "bar", "gnip", "gnop");
assertEquals(w.toString(), "<root gnip=\"gnop\" foo=\"bar\"></root>");
}
@Test
public void attributes_odd_number()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("img");
try
{
w.attributes("src", "foo.png", "width", 20, 30);
unreachable();
}
catch (RuntimeException ex)
{
assertMessageContains(ex, "Writing attributes of the element 'img' failed.",
"An attribute name or value is omitted [src, foo.png, width, 20, 30].",
"Please provide an even number of values, alternating names and values");
}
}
@Test
public void comment()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.comment(" A comment ");
w.end();
assertEquals(w.toString(), "<root><!-- A comment --></root>");
}
@Test
public void entities_inside_comment_not_converted()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.comment(" <&> ");
w.end();
assertEquals(w.toString(), "<root><!-- <&> --></root>");
}
@Test
public void new_text_node_after_comment_node()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.write("before");
w.comment(" A comment ");
w.write("after");
w.end();
assertEquals(w.toString(), "<root>before<!-- A comment -->after</root>");
}
@Test
public void null_write_is_ok()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.write(null);
w.end();
assertEquals(w.toString(), "<root></root>");
}
@Test
public void writef()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.writef("Test name: %s", "writef");
assertEquals(w.toString(), "<root>Test name: writef</root>");
}
@Test
public void write_raw()
{
MarkupWriter w = new MarkupWriterImpl();
w.element("root");
w.write("<");
w.writeRaw(" ");
w.write(">");
w.end();
assertEquals(w.toString(), "<root>< ></root>");
}
@Test
public void namespaced_elements_and_attributes()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
Element root = w.elementNS("fredns", "root");
assertSame(root.defineNamespace("fredns", "fred"), root);
root.defineNamespace("barneyns", "barney");
assertSame(w.attributeNS("fredns", "foo", "bar"), root);
Element child = w.elementNS("barneyns", "child");
assertSame(child.getContainer(), root);
w.end(); // child
w.end(); // root
assertEquals(
w.toString(),
"<?xml version=\"1.0\"?>\n<fred:root fred:foo=\"bar\" xmlns:barney=\"barneyns\" xmlns:fred=\"fredns\"><barney:child/></fred:root>");
}
@Test
public void cdata_content()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
w.element("root");
w.write("Normal Text ");
w.cdata("< & >");
w.write("More Normal Text");
assertEquals(w.toString(),
"<?xml version=\"1.0\"?>\n<root>Normal Text <![CDATA[< & >]]>More Normal Text</root>");
}
@Test
public void listeners()
{
MarkupWriter w = new MarkupWriterImpl(new XMLMarkupModel());
MarkupWriterListener l = new MarkupWriterListener()
{
public void elementDidStart(Element element)
{
element.text("[Start: " + element.getName() + "]");
}
public void elementDidEnd(Element element)
{
element.text("[End: " + element.getName() + "]");
}
};
w.element("root");
w.element("no-listener");
w.write("before listener");
w.addListener(l);
w.element("listener");
w.write("before n-w-l");
w.element("nested-with-listener");
w.write("n-w-l text");
w.end();
w.write("after n-w-l");
w.end();
w.removeListener(l);
w.write("after listener");
w.end();
w.end();
// Because we are invoking Element.text(), the text added by the listener is appended to the body of the
// element,
// which is correct but may not be what you'd expect.
assertEquals(
w.toString(),
"<?xml version=\"1.0\"?>\n"
+ "<root><no-listener>before listener<listener>[Start: listener]before n-w-l<nested-with-listener>[Start: nested-with-listener]n-w-l text[End: nested-with-listener]</nested-with-listener>after n-w-l[End: listener]</listener>after listener</no-listener></root>");
}
}