/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.teststeps.assertions.basic;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.TestAssertionConfig;
import com.eviware.soapui.impl.wsdl.panels.assertions.AssertionCategoryMapping;
import com.eviware.soapui.impl.wsdl.panels.assertions.AssertionListEntry;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathImpl;
import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathsContainer;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
import com.eviware.soapui.impl.wsdl.teststeps.assertions.AbstractTestAssertionFactory;
import com.eviware.soapui.model.TestPropertyHolder;
import com.eviware.soapui.model.iface.SubmitContext;
import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
import com.eviware.soapui.model.testsuite.Assertable;
import com.eviware.soapui.model.testsuite.AssertionError;
import com.eviware.soapui.model.testsuite.AssertionException;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.types.StringList;
import com.eviware.soapui.support.xml.XmlUtils;
import junit.framework.ComparisonFailure;
import org.apache.xmlbeans.XmlAnySimpleType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlQName;
import org.apache.xmlbeans.impl.values.XmlValueDisconnectedException;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLAssert;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.swing.JTextArea;
/**
* Assertion that matches a specified XPath expression and its expected result
* against the associated WsdlTestRequests response message
*
* @author Ole.Matzura
*/
public class XPathContainsAssertion extends AbstractXmlContainsAssertion {
public static final String ID = "XPath Match";
public static final String LABEL = "XPath Match";
public static final String DESCRIPTION = "Uses an XPath expression to select content from the target property and compares the result to an expected value. Applicable to any property containing XML.";
public XPathContainsAssertion(TestAssertionConfig assertionConfig, Assertable assertable) {
super(assertionConfig, assertable, true, true, true, true);
}
public String assertContent(String response, SubmitContext context, String type) throws AssertionException {
try {
if (path == null) {
return "Missing path for XPath assertion";
}
if (expectedContent == null) {
return "Missing content for XPath assertion";
}
XmlOptions options = new XmlOptions();
if (ignoreComments) {
options.setLoadStripComments();
}
// XmlObject xml = XmlObject.Factory.parse( response, options );
XmlObject xml = XmlUtils.createXmlObject(response, options);
String expandedPath = PropertyExpander.expandProperties(context, path);
XmlObject[] items = xml.selectPath(expandedPath);
AssertedXPathsContainer assertedXPathsContainer = (AssertedXPathsContainer) context
.getProperty(AssertedXPathsContainer.ASSERTEDXPATHSCONTAINER_PROPERTY);
XmlObject contentObj = null;
String expandedContent = PropertyExpander.expandProperties(context, expectedContent);
// stupid check for text selection for those situation that the
// selected
// text actually contains xml which should be compared as a string.
if (!expandedPath.endsWith("text()")) {
try {
// contentObj = XmlObject.Factory.parse( expandedContent, options
// );
contentObj = XmlUtils.createXmlObject(expandedContent, options);
} catch (Exception e) {
// this is ok.. it just means that the content to match is not
// xml
// but
// (hopefully) just a string
}
}
if (items.length == 0) {
throw new Exception("Missing content for xpath [" + path + "] in " + type);
}
options.setSavePrettyPrint();
options.setSaveOuter();
for (int c = 0; c < items.length; c++) {
try {
AssertedXPathImpl assertedXPathImpl = null;
if (assertedXPathsContainer != null) {
String xpath = XmlUtils.createAbsoluteXPath(items[c].getDomNode());
if (xpath != null) {
XmlObject xmlObj = items[c];
assertedXPathImpl = new AssertedXPathImpl(this, xpath, xmlObj);
assertedXPathsContainer.addAssertedXPath(assertedXPathImpl);
}
}
if (contentObj == null) {
if (items[c] instanceof XmlAnySimpleType && !(items[c] instanceof XmlQName)) {
String value = ((XmlAnySimpleType) items[c]).getStringValue();
String expandedValue = PropertyExpander.expandProperties(context, value);
XMLAssert.assertEquals(expandedContent, expandedValue);
} else {
Node domNode = items[c].getDomNode();
switch (domNode.getNodeType()) {
case Node.ELEMENT_NODE:
String expandedValue = PropertyExpander.expandProperties(context,
XmlUtils.getElementText((Element) domNode));
if (allowWildcards) {
Tools.assertSimilar(expandedContent, expandedValue, '*');
} else {
XMLAssert.assertEquals(expandedContent, expandedValue);
}
break;
case Node.ATTRIBUTE_NODE:
expandedValue = PropertyExpander.expandProperties(context, domNode.getNodeValue());
if (allowWildcards) {
Tools.assertSimilar(expandedContent, expandedValue, '*');
} else {
XMLAssert.assertEquals(expandedContent, expandedValue);
}
break;
default:
expandedValue = PropertyExpander.expandProperties(context, domNode.getNodeValue());
XMLAssert.assertEquals(expandedContent, expandedValue);
break;
}
}
} else {
compareValues(contentObj.xmlText(options), items[c].xmlText(options), items[c]);
}
break;
} catch (Throwable e) {
if (c == items.length - 1) {
throw e;
}
}
}
} catch (Throwable e) {
String msg = "";
if (e instanceof ComparisonFailure) {
ComparisonFailure cf = (ComparisonFailure) e;
String expected = cf.getExpected();
String actual = cf.getActual();
// if( expected.length() > ERROR_LENGTH_LIMIT )
// expected = expected.substring(0, ERROR_LENGTH_LIMIT) + "..";
//
// if( actual.length() > ERROR_LENGTH_LIMIT )
// actual = actual.substring(0, ERROR_LENGTH_LIMIT) + "..";
msg = "XPathContains comparison failed for path [" + path + "], expecting [" + expected + "], actual was [" + actual + "]";
} else {
msg = "XPathContains assertion failed for path [" + path + "] : " + e.getClass().getSimpleName() + ":"
+ e.getMessage();
}
throw new AssertionException(new AssertionError(msg));
}
return type + " matches content for [" + path + "]";
}
private void compareValues(String expandedContent, String expandedValue, XmlObject object) throws Exception {
Diff diff = new Diff(expandedContent, expandedValue);
InternalDifferenceListener internalDifferenceListener = new InternalDifferenceListener();
diff.overrideDifferenceListener(internalDifferenceListener);
if (!diff.identical()) {
throw new Exception(diff.toString());
}
StringList nodesToRemove = internalDifferenceListener.getNodesToRemove();
if (!nodesToRemove.isEmpty()) {
for (String node : nodesToRemove) {
if (node == null) {
continue;
}
int ix = node.indexOf("\n/");
if (ix != -1) {
node = node.substring(0, ix + 1) + "./" + node.substring(ix + 1);
} else if (node.startsWith("/")) {
node = "/" + node;
}
XmlObject[] paths = object.selectPath(node);
if (paths.length > 0) {
Node domNode = paths[0].getDomNode();
if (domNode.getNodeType() == Node.ATTRIBUTE_NODE) {
((Attr) domNode).getOwnerElement().removeAttributeNode((Attr) domNode);
} else {
domNode.getParentNode().removeChild(domNode);
}
try {
object.set(object.copy());
} catch (XmlValueDisconnectedException e) {
// this means that we've excluded the root note.. it's ok..
return;
}
}
}
}
}
public String getHelpURL() {
return HelpUrls.ASSERTION_XPATH_CONTENT;
}
public void selectFromCurrent() {
XmlCursor cursor = null;
try {
String assertableContent = getAssertable().getAssertableContentAsXml();
if (assertableContent == null || assertableContent.trim().length() == 0) {
UISupport.showErrorMessage("Missing content to select from");
return;
}
JTextArea pathArea = getPathArea();
String txt = pathArea == null || !pathArea.isVisible() ? getPath() : pathArea.getSelectedText();
if (txt == null) {
txt = pathArea == null ? "" : pathArea.getText();
}
WsdlTestRunContext context = new WsdlTestRunContext(getAssertable().getTestStep());
String expandedPath = PropertyExpander.expandProperties(context, txt.trim());
JTextArea contentArea = getContentArea();
if (contentArea != null && contentArea.isVisible()) {
contentArea.setText("");
}
// XmlObject xml = XmlObject.Factory.parse( assertableContent );
XmlObject xml = XmlUtils.createXmlObject(assertableContent);
cursor = xml.newCursor();
cursor.selectPath(expandedPath);
if (!cursor.toNextSelection()) {
UISupport.showErrorMessage("No match in current response");
} else if (cursor.hasNextSelection()) {
UISupport.showErrorMessage("More than one match in current response");
} else {
String stringValue = XmlUtils.getValueForMatch(cursor);
if (contentArea != null && contentArea.isVisible()) {
contentArea.setText(stringValue);
} else {
setExpectedContent(stringValue, false);
}
}
} catch (Throwable e) {
UISupport.showErrorMessage("Invalid XPath expression.");
SoapUI.logError(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
}
public static class Factory extends AbstractTestAssertionFactory {
public Factory() {
super(XPathContainsAssertion.ID, XPathContainsAssertion.LABEL, XPathContainsAssertion.class);
}
@Override
public String getCategory() {
return AssertionCategoryMapping.VALIDATE_RESPONSE_CONTENT_CATEGORY;
}
@Override
public Class<? extends WsdlMessageAssertion> getAssertionClassType() {
return XPathContainsAssertion.class;
}
@Override
public AssertionListEntry getAssertionListEntry() {
return new AssertionListEntry(XPathContainsAssertion.ID, XPathContainsAssertion.LABEL,
XPathContainsAssertion.DESCRIPTION);
}
@Override
public boolean canAssert(TestPropertyHolder modelItem, String property) {
if (!modelItem.getProperty(property).getSchemaType().isPrimitiveType()) {
return true;
}
String content = modelItem.getPropertyValue(property);
return XmlUtils.seemsToBeXml(content);
}
}
protected String getQueryType() {
return "XPath";
}
}