package org.nate; import static java.util.Arrays.asList; import static java.util.Collections.singletonMap; import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.junit.Assert.assertThat; import static org.nate.testutil.WhiteSpaceIgnoringXmlMatcher.matchesXmlIgnoringWhiteSpace; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import org.nate.exception.EncoderNotAvailableException; public abstract class EngineTestBase { @Test(expected = IllegalArgumentException.class) public void shouldThrowAnExceptionWhenKeyIsNotAString() throws Exception { Engine engine = encodeHtmlFragment("<html/>"); engine.inject(singletonMap(42, "b")); } @Test public void shouldIgnoreNullValuesForTextInjection() throws Exception { Engine engine = encodeHtmlFragment("<a/>"); Engine result = engine.inject(singletonMap("a", null)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<a/>")); } @Test public void shouldMatchAndInjectSingleDataValue() throws Exception { Engine engine = encodeHtmlFragment("<span class='section'><span class='content'></span></span>"); Engine result = engine.inject(singletonMap(".section", "Hello")); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<span class='section'>Hello</span> ")); } @Test public void shouldMatchAndInjectMultipleDataValues() throws Exception { Engine engine = encodeHtmlFragment("Before<div class='section'><span class='content'></span></div>After"); Map<String, List<String>> data = singletonMap(".section", asList("Section 1", "Section 2")); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("Before<div class='section'> Section 1</div><div class='section'> Section 2</div>After")); } @Test public void shouldMatchAndInjectMultipleTimes() throws Exception { Engine engine = encodeHtmlFragment("<h1>First Header</h1><h2>Second Header</h2><h1>Third Header</h1>"); Map<String, List<String>> data = singletonMap("h1", asList("Hello", "There")); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<h1>Hello</h1><h1>There</h1><h2>Second Header</h2><h1>Hello</h1><h1>There</h1>")); } @Test public void shouldMatchAndInjectIntoASubselection() throws Exception { Engine engine = encodeHtmlFragment("<span class='section'><span class='greeting'></span></span>"); Object data = singletonMap(".section", singletonMap(".greeting", "Hello")); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<span class='section'><span class='greeting'>Hello</span></span>")); } @SuppressWarnings("unchecked") @Test public void shouldMatchAndInjectMultipleDataValuesIntoSubselection() throws Exception { Engine engine = encodeHtmlFragment( "<section>This is before <div class='section'><span class='greeting'></span></div> and after</section>"); Object data = singletonMap(".section", asList(singletonMap(".greeting", "Hello"), singletonMap(".greeting", "Goodbye"))); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace(("<section> This is before <div class='section'> <span class='greeting'>Hello</span> </div>" + " <div class='section'> <span class='greeting'>Goodbye</span> </div> and after </section>"))); } @Test public void shouldMatchAndInjectIntoElementAttribute() throws Exception { Engine engine = encodeHtmlFragment("<a>my link</a>"); Object data = singletonMap("a", singletonMap("@@href", "http://www.example.com")); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<a href='http://www.example.com'>my link</a>")); } @Test public void shouldMatchAndInjectIntoElementAttributeUsingCombinationOfCssAndAttributeSelection() throws Exception { Engine engine = encodeHtmlFragment("<a>my link</a>"); Object data = singletonMap("a @@href", "http://www.example.com"); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<a href='http://www.example.com'>my link</a>")); } @Test public void shouldRemoveAttributes() throws Exception { Engine engine = encodeHtmlFragment("<a href='xxx'>my link</a>"); Object data = singletonMap("a @@href", null); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<a>my link</a>")); } @Test public void shouldIgnoreAttemptsToRemoveNonexistentAttributes() throws Exception { Engine engine = encodeHtmlFragment("<a>my link</a>"); Object data = singletonMap("a @@href", null); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<a>my link</a>")); } @Test public void shouldAllowValuesToBeAnyObject() throws Exception { Engine engine = encodeHtmlFragment("<span/>"); Object data = singletonMap("span", 42L); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<span>42</span>")); } @Test public void shouldAllowHtmlWithDoctype() throws Exception { String original = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + "<html><head/><body><div/></body></html>"; Engine engine = encodeHtmlDocument(original); Object data = singletonMap("div", "hello"); Engine result = engine.inject(data); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<html><head/><body><div>hello</div></body></html>")); } @Test public void shouldBeAbleToExtractClippings() throws Exception { Engine engine = encodeHtmlFragment("<span id='header'>Header</span><div id='content'><h1>Content</h1></div>"); Engine header = engine.select("#header"); assertThat(header.render(), matchesXmlIgnoringWhiteSpace("<span id='header'>Header</span>")); } @Test public void shouldNotBeModifiedByASelect() throws Exception { String original = "<div id='header'>Header</div><div id='content'><h1>Content</h1></div>"; Engine engine = encodeHtmlFragment(original); engine.select("#header"); assertThat(engine.render(), matchesXmlIgnoringWhiteSpace(original)); } @Test public void shouldBeAbleToSelectAllContent() throws Exception { Engine engine = encodeHtmlFragment("<div id='header'>header text</div><div id='content'>content text</div><div id='footer'><h1>footer</h1></div>"); Engine header = engine.select("##div"); assertThat(header.render(), matchesXmlIgnoringWhiteSpace("header textcontent text <h1>footer</h1>")); } @Test public void shouldNotBeModifiedByASelectContent() throws Exception { String original = "<div id='header'>header text</div><div id='content'>content text</div><div id='footer'><h1>footer</h1></div>"; Engine engine = encodeHtmlFragment(original); engine.select("##div"); assertThat(engine.render(), matchesXmlIgnoringWhiteSpace(original)); } @Test public void shouldReturnSelectionsWhereNodesMatchThatAreInAncestralRelationship() throws Exception { String original = "<div id='outer'><div id='inner'>hello</div></div>"; Engine engine = encodeHtmlFragment(original); String selection = engine.select("div").render(); assertThat(selection, matchesXmlIgnoringWhiteSpace("<div id='outer'><div id='inner'>hello</div></div><div id='inner'>hello</div>")); assertThat(engine.render(), matchesXmlIgnoringWhiteSpace(original)); } @Test public void shouldBeAbleToInjectNateEnginesIntoNateEngines() throws Exception { Engine engine1 = encodeHtmlFragment("<div id='header'>Header</div><div id='content'></div>"); Engine engine2 = encodeHtmlFragment("<h1>Hello</h1>"); Engine result = engine1.inject(singletonMap("#content", engine2)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div id='header'>Header</div><div id='content'><h1>Hello</h1></div>")); } @Test public void shouldBeAbleToInjectASeletionFromOneDocumentToAnother() throws Exception { Engine source = encodeHtmlFragment("<div><p>one</p>and<p>two</p></div>"); Engine selection = source.select("p"); Engine destination = encodeHtmlFragment("<div id='header'>Header</div><div id='content'></div>"); Engine result = destination.inject(singletonMap("#content", selection)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div id='header'>Header</div><div id='content'><p>one</p><p>two</p></div>")); } @Test public void shouldNotModifyAnInjectedEngine() throws Exception { Engine source = encodeHtmlFragment("<div><p>hello</p></div>"); Engine selection = source.select("p"); Engine destination = encodeHtmlFragment("<div class='content'/>"); Engine result = destination.inject(singletonMap(".content", selection)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div class='content'><p>hello</p></div>")); assertThat(selection.render(), matchesXmlIgnoringWhiteSpace("<p>hello</p>")); } @Test public void shouldBeAbleToInjectASelectionMultipleTimes() throws Exception { Engine source = encodeHtmlFragment("<div><p>hello</p></div>"); Engine selection = source.select("p"); Engine destination = encodeHtmlFragment("<div class='content'/><div class='content'/>"); Engine result = destination.inject(singletonMap(".content", selection)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div class='content'><p>hello</p></div><div class='content'><p>hello</p></div>")); } @Test public void shouldCopeWithNameSpaces() throws Exception { Engine engine = encodeHtmlDocument("<html xmls='http://www.w3.org/1999/xhtml'><head/><body><div id='header'>header</div></body></html>"); Engine result = engine.inject(singletonMap("body", "hi")); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<html xmls='http://www.w3.org/1999/xhtml'><head/><body>hi</body></html>")); } @Test public void shouldReplaceContentWhenSubselectIsTheSpecialContentPseudoSelector() throws Exception { Engine engine = encodeHtmlFragment("<a href='#'>my link</a>"); Map<String, Object> anchorData = new HashMap<String, Object>(); anchorData.put("@@href", "http://www.example.com"); anchorData.put(Engine.CONTENT_ATTRIBUTE, "example.com"); Engine result = engine.inject(singletonMap("a", anchorData)); assertXMLEqual("<a href='http://www.example.com'>example.com</a>", result.render()); } @Test public void shouldRecursivelyApplyTransformsWhenSubselectIsTheSpecialContentPseudoSelector() throws Exception { Engine engine = encodeHtmlFragment("<div class=''><span/></div>"); Map<String, Object> anchorData = new HashMap<String, Object>(); anchorData.put("@@class", "show"); anchorData.put(Engine.CONTENT_ATTRIBUTE, singletonMap("span", "hello")); Engine result = engine.inject(singletonMap("div", anchorData)); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div class='show'><span>hello</span></div>")); } @Test public void shouldHandleNonAsciiCharactersInStrings() throws Exception { // Random chinese characters. Hope they don't mean anything offensive! Engine engine = encodeHtmlFragment("<div>\u4E10</div><p/>"); Engine result = engine.inject(singletonMap("p", "\u4E11")); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<div>\u4E10</div><p>\u4E11</p>")); } @Test public void shouldHandleNonAsciiCharactersInWholeDocumentStrings() throws Exception { // Random chinese characters. Hope they don't mean anything offensive! String original = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + "<html><head/><body><div>\u4E10</div><p/></body></html>"; Engine engine = encodeHtmlDocument(original); Engine result = engine.inject(singletonMap("p", "\u4E11")); assertThat(result.render(), matchesXmlIgnoringWhiteSpace("<html><head/><body><div>\u4E10</div><p>\u4E11</p></body></html>")); } @Test(expected=EncoderNotAvailableException.class) public void shouldThrowExceptionForUnknownEncodings() throws Exception { Nate.newWith("banana", Nate.encoders().encoderFor("unknown")); } protected abstract Engine encodeHtmlFragment(String html); protected abstract Engine encodeHtmlDocument(String html); }