// Copyright � 2002-2007 Canoo Engineering AG, Switzerland.
package com.canoo.webtest.steps.verify;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import org.apache.xpath.XPathContext;
import org.apache.xpath.functions.FunctionDef1Arg;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XString;
import com.canoo.webtest.engine.xpath.XPathHelper;
import com.canoo.webtest.steps.BaseStepTestCase;
import com.canoo.webtest.steps.Step;
/**
* Unit tests for {@link VerifyXPath}.
* @author <a href="balld@webslingerZ.com">Donald Ball</a>, Carsten Seibert, Dierk K�nig
* @author Marc Guillemot, Paul King
*/
public class VerifyXPathTest extends BaseStepTestCase {
private VerifyXPath fStep;
protected void setUp() throws Exception {
super.setUp();
fStep = (VerifyXPath) getStep();
}
protected Step createStep() {
return new VerifyXPath();
}
public void testRejectsNullXpath() {
assertStepRejectsNullParam("xpath", getExecuteStepTestBlock());
}
public void testHandleHtmlPage() throws Exception {
getContext().setDefaultResponse("<html><head></head><body><h1>hello</h1></body></html>");
fStep.setXpath("/html/body/h1");
executeStep(fStep);
}
public void testHandleXmlPage() throws Exception {
getContext().setDefaultResponse("<xml><body><h1>hello</h1></body></xml>", "text/xml");
fStep.setXpath("/xml/body/h1");
executeStep(fStep);
}
/**
* Tests an xpath check on an XmlPage containing an badly formed xml document
* @throws Exception if the test fails
*/
public void testHandleInvalidXmlPage() throws Exception {
getContext().setDefaultResponse("<xml type='foo & fii'></xml>", "text/xml");
fStep.setXpath("/xml");
assertFailOnExecute(fStep, "", "");
}
public void testHandleMissingPage() throws Exception {
fStep.setXpath("'valeur'='value'");
fStep.setText("false");
executeStep(fStep);
fStep.setXpath("/not/here");
assertFailOnExecute(fStep, "", "");
}
public void testHandleUnknownPage() {
getContext().setDefaultResponse("hello", "text/plain");
fStep.setXpath("/html/head/title");
assertFailOnExecute(fStep, "", "");
}
public void testLocateI18nText() throws Exception {
final String htmlContent
= "<html><body><h1>你</h1></body></html>";
getContext().setDefaultResponse(htmlContent);
fStep.setXpath("/html/body/h1");
fStep.setText("你");
assertFailOnExecute(fStep, "Numeric entity not found", "");
fStep.setText("\u4f60");
executeStep(fStep);
}
public void testLocateI18nNamedEntitiesText() throws Exception {
final String htmlContent
= "<html><body><h1>ü</h1></body></html>";
getContext().setDefaultResponse(htmlContent);
fStep.setXpath("/html/body/h1");
fStep.setText("ü");
assertFailOnExecute(fStep, "Named entity not found", "");
fStep.setText("\u00fc");
executeStep(fStep);
}
public void testExistingXPathExpression() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("/html/head/title");
executeStep(fStep);
}
public void testNonExistingXPathExpression() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("/not/here");
assertFailOnExecute(fStep, "", "");
}
public void testNodeValueOK() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("/html/head/title");
fStep.setText("foo");
executeStep(fStep);
}
public void testNodeValueNotOK() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("/html/head/title");
fStep.setText("bar");
assertFailOnExecute(fStep, "", "");
}
public static class ReverseFunction extends FunctionDef1Arg
{
public XObject execute(final XPathContext xctxt) throws TransformerException
{
final StringBuffer sb = new StringBuffer();
final String arg = getArg0AsString(xctxt).toString();
for (int i=arg.length()-1; i>=0; --i)
sb.append(arg.charAt(i));
return new XString(sb.toString());
}
}
/**
* Test use of variable, function and namespaces contexts
* @throws Exception if the test fails
*/
public void testContext() throws Exception {
defineSampleDocumentAsCurrent();
final XPathHelper xpathHelper = fStep.getContext().getXPathHelper();
xpathHelper.getVariableContext().setVariableValue(new QName("foo"), "123");
fStep.setXpath("$foo");
fStep.setText("123");
executeStep(fStep);
// function without context
fStep.setXpath("myReverse('abcdef')");
fStep.setText("fedcba");
// assertThrowOnExecute(fStep, "", "", XPathException.class);
xpathHelper.getFunctionContext().registerFunction(new QName(null, "myReverse"), ReverseFunction.class);
executeStep(fStep);
// function with context
fStep.setXpath("wt:myReverse2('abcdef')");
fStep.setText("fedcba");
assertThrowOnExecute(fStep, "", "", XPathExpressionException.class);
xpathHelper.getFunctionContext().registerFunction(new QName("webtest/xpath", "myReverse2"), ReverseFunction.class);
assertThrowOnExecute(fStep, "", "", XPathExpressionException.class);
xpathHelper.getNamespaceContext().addNamespace("wt", "webtest/xpath");
executeStep(fStep);
}
/**
* Xpath evaluating to boolean true without text should pass
* @throws Exception if the test fails
*/
public void testBooleanValueNoTextTrue() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("1 > 0");
executeStep(fStep);
}
/**
* Xpath evaluating to boolean true with text "true" should pass
* @throws Exception if the test fails
*/
public void testBooleanValueTextTrue() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("1 > 0");
fStep.setText("true");
executeStep(fStep);
}
/**
* Xpath evaluating to boolean false without text should fail
* @throws Exception if the test fails
*/
public void testBooleanValueNoTextFalse() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("1 < 0");
assertFailOnExecute(fStep, "", "");
}
/**
* Xpath evaluating to boolean false with text "false" should pass
* @throws Exception if the test fails
*/
public void testBooleanValueTextFalse() throws Exception {
defineSampleDocumentAsCurrent();
fStep.setXpath("1 < 0");
fStep.setText("false");
executeStep(fStep);
}
public void testMissingSelectValue() throws Exception {
final VerifyXPath step = new VerifyXPath();
step.setXpath("/bla");
assertFalse(step.isComparingPathAndValue());
step.setText("bla");
assertTrue(step.isComparingPathAndValue());
}
private void defineSampleDocumentAsCurrent() throws Exception {
final String htmlDocument = "<html><head><title>foo</title></head><body>foobar</body></html>";
getContext().setDefaultResponse(htmlDocument);
}
/**
* Test that text is NOT trimmed
* @throws Exception if the test fails
*/
public void testBlankBeforeElementContent() throws Exception {
final String document
= "<html><body>"
+ "<table>"
+ "<tr><td>1.1</td><td>1.2</td><td>1.3</td></tr>"
+ "<tr><td> 2.1</td><td>2.2</td><td>2.3</td></tr>"
+ "</table>"
+ "</body></html>";
getContext().setDefaultResponse(document);
fStep.setXpath("//tr[td/text() = ' 2.1']/td[3]");
fStep.setText("2.3");
executeStep(fStep);
}
/**
* Test for WT-253: automatically extract namespaces from the xml
* @throws Exception if the test fails
*/
public void testXPathNamespaces() throws Exception
{
final String xml = "<?xml version=\"1.0\"?>\n"
+ "<SOAP-ENV:Envelope\n"
+ " xmlns:SOAP-ENV=\"http://webtest.canoo.com/soap/envelope/\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" >\n"
+ " <SOAP-ENV:Body>\n"
+ " <calculateFibonacci \n"
+ " type=\"xsi:positiveInteger\">10</calculateFibonacci>\n"
+ " </SOAP-ENV:Body>\n"
+ "</SOAP-ENV:Envelope>";
getContext().setDefaultResponse(xml, "text/xml");
fStep.setXpath("//SOAP-ENV:Body/calculateFibonacci/text()");
fStep.setText("10");
executeStep(fStep);
// check that WebTest goodies are still available
fStep.setXpath("wt:cleanText(//SOAP-ENV:Body/calculateFibonacci)");
fStep.setText("10");
executeStep(fStep);
}
// /**
// * @throws Exception if the test fails
// */
// public void testXPathNamespaces2() throws Exception
// {
// final String xml = "<?xml version=\"1.0\"?>\n"
// + "<identity xmlns='http://10.0.1.1/identity/v1' "
// + "xmlns:fsapi-v1='http://10.0.1.1/v1' version='1.0.20071219.0' "
// + "statusMessage='OK' statusCode='200'>"
// + "<session id='434DF084FA5968055B802AF6F4073725'/></identity>";
//
// getContext().setDefaultResponse(xml, "text/xml");
// fStep.setXpath("//*:identitiy");
//// fStep.setText("10");
// executeStep(fStep);
// }
}