/*
* Copyright 2010 the original author or authors.
*
* 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.xmlmatchers.xpath;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.xmlmatchers.transform.XmlConverters.*;
import static org.xmlmatchers.xpath.HasXPath.hasXPath;
import static org.xmlmatchers.xpath.XpathReturnType.returningABoolean;
import static org.xmlmatchers.xpath.XpathReturnType.returningANumber;
import static org.xmlmatchers.xpath.XpathReturnType.returningAnXmlNode;
import static org.xmlmatchers.equivalence.IsEquivalentTo.equivalentTo;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.Collection;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmlmatchers.namespace.SimpleNamespaceContext;
import org.xmlmatchers.transform.IdentityTransformer;
import org.xmlmatchers.transform.StringResult;
import org.xmlmatchers.transform.StringSource;
/**
* @author David Ehringer
*/
@RunWith(Parameterized.class)
public class HasXPathTest {
private static final String EXAMPLE_XML = "<mountains type='big'>\n"
+ " <mountain id='a' altname=''><name>Everest</name></mountain>\n"
+ " <mountain id='b'><name>K2</name></mountain>\n"
+ " <f:range xmlns:f=\"http://mountains.com\" milk=\"camel\">Caravane</f:range>\n"
+ " <oceanRidge />\n"
+ " <f:oceanRidge xmlns:f=\"http://mountains.com\" f:depth='-5000' />"
+ " <volcanoe eruptions='2' good='false' bad='0' />"
+ "</mountains>\n";
private Source xml;
private NamespaceContext usingNamespaces = new SimpleNamespaceContext()
.withBinding("m", "http://mountains.com")//
.withBinding("r", "http://rivers.com");
public HasXPathTest(Source xml) {
this.xml = xml;
}
// Test multiple source representations of XML
@Parameters
public static Collection<Source[]> data() throws Exception {
return Arrays.asList(new Source[][] {//
{ the(EXAMPLE_XML) },//
{ the(nodeVersionOf(EXAMPLE_XML)) },//
{ the(stringResultVersionOf(EXAMPLE_XML)) },//
});
}
private static StringResult stringResultVersionOf(String exampleXml) {
IdentityTransformer identity = new IdentityTransformer();
StringResult result = new StringResult();
Source source = StringSource.toSource(exampleXml);
identity.transform(source, result);
return result;
}
private static Node nodeVersionOf(String exampleXml) throws Exception {
Element element = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new ByteArrayInputStream(exampleXml.getBytes()))
.getDocumentElement();
return element;
}
@Test
public void matchesNodesInTheXmlWhenNoNamespacesAreBound() {
assertThat(xml, hasXPath("/mountains"));
assertThat(xml, hasXPath("/mountains/mountain"));
assertThat(xml, hasXPath("/mountains/mountain[2]/name"));
assertThat(xml, hasXPath("/mountains/oceanRidge"));
assertThat(xml, hasXPath("/mountains/mountain[@id='a']/@altname"));
}
@Test
public void ifANodeIsPrefixedByANamespaceAndNamespacesAreNotBoundXpathWillNotFindIt() {
assertThat(xml, not(hasXPath("/mountains/range")));
}
@Test
public void functionsCanBeUsedToReturnDataThatCanBeMatchedAgainst() {
assertThat(xml, hasXPath("count(/mountains/mountain)", equalTo("2")));
assertThat(xml,//
hasXPath("count(/mountains/mountain)", //
returningANumber(), //
greaterThanOrEqualTo(2d)));
assertThat(xml,//
hasXPath("count(/mountains/mountain)", //
returningANumber(), //
not(lessThanOrEqualTo(1d))));
}
@Test
public void matchesNodesInTheXmlWhenNamespacesAreBound() {
assertThat(xml, hasXPath("/mountains", usingNamespaces));
assertThat(xml, hasXPath("/mountains/mountain", usingNamespaces));
assertThat(xml,
hasXPath("/mountains/mountain[2]/name", usingNamespaces));
assertThat(xml, hasXPath("/mountains/oceanRidge", usingNamespaces));
assertThat(xml, not(hasXPath("/mountains/range")));
assertThat(xml, hasXPath("/mountains/m:range", usingNamespaces));
}
@Test
public void matchesAttributesInTheXmlWhenNoNamespacesAreBound() {
assertThat(xml, hasXPath("/mountains/@type"));
}
@Test
public void matchesAttributesInTheXmlWhenNamespacesAreBound() {
assertThat(xml,
hasXPath("/mountains/m:oceanRidge/@m:depth", usingNamespaces));
}
@Test
public void theResultOfTheXpathCanMatchedUsingEquivalentToWhenTheResultIsAnXmlFragment() {
assertThat(
xml,
hasXPath("/mountains/mountain[@id='a']/name",
returningAnXmlNode(),
equivalentTo(xml("<name>Everest</name>"))));
}
@Test
public void matchingNodesCanBeTestedForEquivalence() {
assertThat(
xml,
hasXPath(
"/mountains/mountain[@id='a']/name",
returningAnXmlNode(),
equivalentTo(xml("<name><!-- some comment -->Everest</name>"))));
}
@Test
public void theResultOfTheXpathCanMatchedUsingEquivalentToWhenTheResultIsAString() {
assertThat(
xml,
hasXPath("/mountains/m:oceanRidge/@m:depth", usingNamespaces,
equalTo("-5000")));
assertThat(xml,
hasXPath("/mountains/mountain[@id='a']/@altname", equalTo("")));
assertThat(
xml,
hasXPath("/mountains/mountain[@id='a']/name",
equalToIgnoringCase("EVEREST")));
assertThat(
xml,
hasXPath("/mountains/mountain[@id='a']/name/text()",
equalToIgnoringCase("EvErEsT")));
}
@Test
public void theResultOfTheXpathCanMatchedUsingEquivalentToWhenTheResultIsANumber() {
assertThat(
xml,
hasXPath("/mountains/volcanoe/@eruptions", returningANumber(),
equalTo(2.0)));
assertThat(
xml,
hasXPath("/mountains/volcanoe/@eruptions", returningANumber(),
closeTo(4, 2)));
assertThat(
xml,
hasXPath("/mountains/volcanoe/@eruptions", returningANumber(),
lessThan(5d)));
assertThat(
xml,
hasXPath("/mountains/volcanoe/@eruptions", returningANumber(),
lessThanOrEqualTo(5.0)));
assertThat(
xml,
hasXPath("count(/mountains/mountain)", returningANumber(),
equalTo(2d)));
}
@Test
public void attributeOrNodeValuesCannotBeConvertedToBooleans() {
assertThat(xml, //
not(hasXPath("/mountains/volcanoe/@good", //
returningABoolean(),//
equalTo(false))));
}
@Test
public void theResultOfTheXpathCanMatchedUsingEquivalentToWhenTheResultIsABoolean() {
assertThat(xml,//
hasXPath("/mountains/volcanoe/@good", //
returningABoolean(),//
equalTo(true)));
assertThat(xml,//
hasXPath("not(/mountains/climber)", //
returningABoolean(),//
equalTo(true)));
assertThat(xml,//
hasXPath("not(/mountains/volcanoe/@good)", //
returningABoolean(),//
equalTo(false)));
}
@Test
public void theResultOfTheXpathCanMatchedUsingStringContains() {
assertThat(
xml,
hasXPath("/mountains/mountain[@id='a']/name",
containsString("Everest")));
}
}