View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.impl.wsdl.teststeps.assertions.basic;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.config.TestAssertionConfig;
17  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
18  import com.eviware.soapui.impl.wsdl.WsdlInterface;
19  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
20  import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathImpl;
21  import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathsContainer;
22  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
23  import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
24  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
25  import com.eviware.soapui.impl.wsdl.teststeps.assertions.AbstractTestAssertionFactory;
26  import com.eviware.soapui.model.TestModelItem;
27  import com.eviware.soapui.model.iface.MessageExchange;
28  import com.eviware.soapui.model.iface.SubmitContext;
29  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
30  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
31  import com.eviware.soapui.model.support.XPathReference;
32  import com.eviware.soapui.model.support.XPathReferenceContainer;
33  import com.eviware.soapui.model.support.XPathReferenceImpl;
34  import com.eviware.soapui.model.testsuite.*;
35  import com.eviware.soapui.model.testsuite.AssertionError;
36  import com.eviware.soapui.support.StringUtils;
37  import com.eviware.soapui.support.UISupport;
38  import com.eviware.soapui.support.components.JUndoableTextArea;
39  import com.eviware.soapui.support.components.JXToolBar;
40  import com.eviware.soapui.support.types.StringList;
41  import com.eviware.soapui.support.xml.XmlObjectConfigurationBuilder;
42  import com.eviware.soapui.support.xml.XmlObjectConfigurationReader;
43  import com.eviware.soapui.support.xml.XmlUtils;
44  import com.jgoodies.forms.builder.ButtonBarBuilder;
45  import junit.framework.ComparisonFailure;
46  import org.apache.log4j.Logger;
47  import org.apache.xmlbeans.*;
48  import org.custommonkey.xmlunit.*;
49  import org.w3c.dom.Attr;
50  import org.w3c.dom.Element;
51  import org.w3c.dom.Node;
52  
53  import javax.swing.*;
54  import java.awt.*;
55  import java.awt.event.ActionEvent;
56  import java.awt.event.WindowAdapter;
57  import java.awt.event.WindowEvent;
58  import java.util.ArrayList;
59  import java.util.List;
60  
61  /***
62   * Assertion that matches a specified XPath expression and its expected result
63   * against the associated WsdlTestRequests response message
64   *
65   * @author Ole.Matzura
66   */
67  
68  public class XPathContainsAssertion extends WsdlMessageAssertion implements RequestAssertion, ResponseAssertion, XPathReferenceContainer
69  {
70     private final static Logger log = Logger.getLogger( XPathContainsAssertion.class );
71     private String expectedContent;
72     private String path;
73     private JDialog configurationDialog;
74     private JTextArea pathArea;
75     private JTextArea contentArea;
76     private boolean configureResult;
77     private boolean allowWildcards;
78     private boolean ignoreNamspaceDifferences;
79  
80     public static final String ID = "XPath Match";
81     public static final String LABEL = "XPath Match";
82     private JCheckBox allowWildcardsCheckBox;
83     private JCheckBox ignoreNamspaceDifferencesCheckBox;
84  
85     public XPathContainsAssertion( TestAssertionConfig assertionConfig, Assertable assertable )
86     {
87        super( assertionConfig, assertable, true, true, true, true );
88  
89        XmlObjectConfigurationReader reader = new XmlObjectConfigurationReader( getConfiguration() );
90        path = reader.readString( "path", null );
91        expectedContent = reader.readString( "content", null );
92        allowWildcards = reader.readBoolean( "allowWildcards", false );
93        ignoreNamspaceDifferences = reader.readBoolean( "ignoreNamspaceDifferences", false );
94     }
95  
96     public String getExpectedContent()
97     {
98        return expectedContent;
99     }
100 
101    public void setExpectedContent( String expectedContent )
102    {
103       this.expectedContent = expectedContent;
104       setConfiguration( createConfiguration() );
105    }
106 
107    /***
108     * @deprecated
109     */
110 
111    @Deprecated
112    public void setContent( String content )
113    {
114       setExpectedContent( content );
115    }
116 
117    public String getPath()
118    {
119       return path;
120    }
121 
122    public void setPath( String path )
123    {
124       this.path = path;
125       setConfiguration( createConfiguration() );
126    }
127 
128    public boolean isAllowWildcards()
129    {
130       return allowWildcards;
131    }
132 
133    public void setAllowWildcards( boolean allowWildcards )
134    {
135       this.allowWildcards = allowWildcards;
136    }
137 
138    public boolean isIgnoreNamspaceDifferences()
139    {
140       return ignoreNamspaceDifferences;
141    }
142 
143    public void setIgnoreNamspaceDifferences( boolean ignoreNamspaceDifferences )
144    {
145       this.ignoreNamspaceDifferences = ignoreNamspaceDifferences;
146    }
147 
148    @Override
149    protected String internalAssertResponse( MessageExchange messageExchange, SubmitContext context )
150            throws AssertionException
151    {
152       if( !messageExchange.hasResponse() )
153          return "Missing Response";
154       else
155          return assertContent( messageExchange.getResponseContentAsXml(), context, "Response" );
156    }
157 
158    public String assertContent( String response, SubmitContext context, String type ) throws AssertionException
159    {
160       try
161       {
162          if( path == null )
163             return "Missing path for XPath assertion";
164          if( expectedContent == null )
165             return "Missing content for XPath assertion";
166 
167          XmlObject xml = XmlObject.Factory.parse( response );
168          String expandedPath = PropertyExpansionUtils.expandProperties( context, path );
169          XmlObject[] items = xml.selectPath( expandedPath );
170          AssertedXPathsContainer assertedXPathsContainer = (AssertedXPathsContainer)
171                  context.getProperty( AssertedXPathsContainer.ASSERTEDXPATHSCONTAINER_PROPERTY );
172 
173          XmlObject contentObj = null;
174          String expandedContent = PropertyExpansionUtils.expandProperties( context, expectedContent );
175 
176          // stupid check for text selection for those situation that the selected
177          // text actually contains xml which should be compared as a string.
178          if( !expandedPath.endsWith( "text()" ) )
179          {
180             try
181             {
182                contentObj = XmlObject.Factory.parse( expandedContent );
183             }
184             catch( Exception e )
185             {
186                // this is ok.. it just means that the content to match is not xml
187                // but
188                // (hopefully) just a string
189             }
190          }
191 
192          if( items.length == 0 )
193             throw new Exception( "Missing content for xpath [" + path + "] in " + type );
194 
195          XmlOptions options = new XmlOptions();
196          options.setSavePrettyPrint();
197          options.setSaveOuter();
198 
199          for( int c = 0; c < items.length; c++ )
200          {
201             try
202             {
203                AssertedXPathImpl assertedXPathImpl = null;
204                if( assertedXPathsContainer != null )
205                {
206                   String xpath = XmlUtils.createAbsoluteXPath( items[c].getDomNode() );
207                   if( xpath != null )
208                   {
209                      XmlObject xmlObj = items[c]; //XmlObject.Factory.parse( items[c].xmlText( options ) );
210 
211                      assertedXPathImpl = new AssertedXPathImpl( this, xpath, xmlObj );
212                      assertedXPathsContainer.addAssertedXPath( assertedXPathImpl );
213                   }
214                }
215 
216                if( contentObj == null )
217                {
218                   if( items[c] instanceof XmlAnySimpleType && !( items[c] instanceof XmlQName ) )
219                   {
220                      String value = ( (XmlAnySimpleType) items[c] ).getStringValue();
221                      String expandedValue = PropertyExpansionUtils.expandProperties( context, value );
222                      XMLAssert.assertEquals( expandedContent, expandedValue );
223                   }
224                   else
225                   {
226                      Node domNode = items[c].getDomNode();
227                      if( domNode.getNodeType() == Node.ELEMENT_NODE )
228                      {
229                         String expandedValue = PropertyExpansionUtils.expandProperties( context, XmlUtils
230                                 .getElementText( (Element) domNode ) );
231                         XMLAssert.assertEquals( expandedContent, expandedValue );
232                      }
233                      else
234                      {
235                         String expandedValue = PropertyExpansionUtils.expandProperties( context, domNode
236                                 .getNodeValue() );
237                         XMLAssert.assertEquals( expandedContent, expandedValue );
238                      }
239                   }
240                }
241                else
242                {
243                   compareValues( contentObj.xmlText( options ), items[c].xmlText( options ), items[c] );
244                }
245 
246                break;
247             }
248             catch( Throwable e )
249             {
250                if( c == items.length - 1 )
251                   throw e;
252             }
253          }
254       }
255       catch( Throwable e )
256       {
257          String msg = "";
258 
259          if( e instanceof ComparisonFailure )
260          {
261             ComparisonFailure cf = (ComparisonFailure) e;
262             String expected = cf.getExpected();
263             String actual = cf.getActual();
264 
265 //				if( expected.length() > ERROR_LENGTH_LIMIT )
266 //					expected = expected.substring(0, ERROR_LENGTH_LIMIT) + "..";
267 //				
268 //				if( actual.length() > ERROR_LENGTH_LIMIT )
269 //					actual = actual.substring(0, ERROR_LENGTH_LIMIT) + "..";
270 
271             msg = "XPathContains comparison failed, expecting [" + expected + "], actual was [" + actual + "]";
272          }
273          else
274          {
275             msg = "XPathContains assertion failed for path [" + path + "] : " + e.getClass().getSimpleName() + ":"
276                     + e.getMessage();
277          }
278 
279          throw new AssertionException( new AssertionError( msg ) );
280       }
281 
282       return type + " matches content for [" + path + "]";
283    }
284 
285    private void compareValues( String expandedContent, String expandedValue, XmlObject object ) throws Exception
286    {
287       Diff diff = new Diff( expandedContent, expandedValue );
288       InternalDifferenceListener internalDifferenceListener = new InternalDifferenceListener();
289       diff.overrideDifferenceListener( internalDifferenceListener );
290 
291       if( !diff.identical() )
292          throw new Exception( diff.toString() );
293 
294       StringList nodesToRemove = internalDifferenceListener.getNodesToRemove();
295 
296       if( !nodesToRemove.isEmpty() )
297       {
298          for( String node : nodesToRemove )
299          {
300             if( node == null )
301                continue;
302 
303             int ix = node.indexOf( "\n/" );
304             if( ix != -1 )
305                node = node.substring( 0, ix + 1 ) + "/" + node.substring( ix + 1 );
306             else if( node.startsWith( "/" ) )
307                node = "/" + node;
308 
309             XmlObject[] paths = object.selectPath( node );
310             if( paths.length > 0 )
311             {
312                Node domNode = paths[0].getDomNode();
313                if( domNode.getNodeType() == Node.ATTRIBUTE_NODE )
314                   ( (Attr) domNode ).getOwnerElement().removeAttributeNode( (Attr) domNode );
315                else
316                   domNode.getParentNode().removeChild( domNode );
317 
318                object.set( object.copy() );
319             }
320          }
321       }
322    }
323 
324    @Override
325    public boolean configure()
326    {
327       if( configurationDialog == null )
328          buildConfigurationDialog();
329 
330       pathArea.setText( path );
331       contentArea.setText( expectedContent );
332       allowWildcardsCheckBox.setSelected( allowWildcards );
333 
334       UISupport.showDialog( configurationDialog );
335       return configureResult;
336    }
337 
338    protected void buildConfigurationDialog()
339    {
340       configurationDialog = new JDialog( UISupport.getMainFrame() );
341       configurationDialog.setTitle( "XPath Match configuration" );
342       configurationDialog.addWindowListener( new WindowAdapter()
343       {
344          @Override
345          public void windowOpened( WindowEvent event )
346          {
347             SwingUtilities.invokeLater( new Runnable()
348             {
349                public void run()
350                {
351                   pathArea.requestFocusInWindow();
352                }
353             } );
354          }
355       } );
356 
357       JPanel contentPanel = new JPanel( new BorderLayout() );
358       contentPanel.add( UISupport.buildDescription( "Specify xpath expression and expected result",
359               "declare namespaces with <code>declare namespace &lt;prefix&gt;='&lt;namespace&gt;';</code>", null ),
360               BorderLayout.NORTH );
361 
362       JSplitPane splitPane = UISupport.createVerticalSplit();
363 
364       pathArea = new JUndoableTextArea();
365       pathArea.setToolTipText( "Specifies the XPath expression to select from the message for validation" );
366 
367       JPanel pathPanel = new JPanel( new BorderLayout() );
368       JXToolBar pathToolbar = UISupport.createToolbar();
369       addPathEditorActions( pathToolbar );
370 
371       pathPanel.add( pathToolbar, BorderLayout.NORTH );
372       pathPanel.add( new JScrollPane( pathArea ), BorderLayout.CENTER );
373 
374       splitPane.setTopComponent( UISupport.addTitledBorder( pathPanel, "XPath Expression" ) );
375 
376       contentArea = new JUndoableTextArea();
377       contentArea.setToolTipText( "Specifies the expected result of the XPath expression" );
378 
379       JPanel matchPanel = new JPanel( new BorderLayout() );
380       JXToolBar contentToolbar = UISupport.createToolbar();
381       addMatchEditorActions( contentToolbar );
382 
383       matchPanel.add( contentToolbar, BorderLayout.NORTH );
384       matchPanel.add( new JScrollPane( contentArea ), BorderLayout.CENTER );
385 
386       splitPane.setBottomComponent( UISupport.addTitledBorder( matchPanel, "Expected Result" ) );
387       splitPane.setDividerLocation( 150 );
388       splitPane.setBorder( BorderFactory.createEmptyBorder( 0, 1, 0, 1 ) );
389 
390       contentPanel.add( splitPane, BorderLayout.CENTER );
391 
392       ButtonBarBuilder builder = new ButtonBarBuilder();
393 
394       ShowOnlineHelpAction showOnlineHelpAction = new ShowOnlineHelpAction( HelpUrls.XPATHASSERTIONEDITOR_HELP_URL );
395       builder.addFixed( UISupport.createToolbarButton( showOnlineHelpAction ) );
396       builder.addGlue();
397 
398       JButton okButton = new JButton( new OkAction() );
399       builder.addFixed( okButton );
400       builder.addRelatedGap();
401       builder.addFixed( new JButton( new CancelAction() ) );
402 
403       builder.setBorder( BorderFactory.createEmptyBorder( 1, 5, 5, 5 ) );
404 
405       contentPanel.add( builder.getPanel(), BorderLayout.SOUTH );
406 
407       configurationDialog.setContentPane( contentPanel );
408       configurationDialog.setSize( 600, 500 );
409       configurationDialog.setModal( true );
410       UISupport.initDialogActions( configurationDialog, showOnlineHelpAction, okButton );
411    }
412 
413    protected void addPathEditorActions( JXToolBar toolbar )
414    {
415       toolbar.addFixed( new JButton( new DeclareNamespacesFromCurrentAction() ) );
416    }
417 
418    protected void addMatchEditorActions( JXToolBar toolbar )
419    {
420       toolbar.addFixed( new JButton( new SelectFromCurrentAction() ) );
421       toolbar.addRelatedGap();
422       toolbar.addFixed( new JButton( new TestPathAction() ) );
423       allowWildcardsCheckBox = new JCheckBox( "Allow Wildcards" );
424 
425       Dimension dim = new Dimension( 100, 20 );
426 
427       allowWildcardsCheckBox.setSize( dim );
428       allowWildcardsCheckBox.setPreferredSize( dim );
429 
430       allowWildcardsCheckBox.setOpaque( false );
431       toolbar.addRelatedGap();
432       toolbar.addFixed( allowWildcardsCheckBox );
433 
434       Dimension largerDim = new Dimension( 200, 20 );
435       ignoreNamspaceDifferencesCheckBox = new JCheckBox( "Ignore namespace prefixes" );
436       ignoreNamspaceDifferencesCheckBox.setSize( largerDim );
437       ignoreNamspaceDifferencesCheckBox.setPreferredSize( largerDim );
438       ignoreNamspaceDifferencesCheckBox.setOpaque( false );
439       toolbar.addRelatedGap();
440       toolbar.addFixed( ignoreNamspaceDifferencesCheckBox );
441    }
442 
443    public XmlObject createConfiguration()
444    {
445       XmlObjectConfigurationBuilder builder = new XmlObjectConfigurationBuilder();
446       builder.add( "path", path );
447       builder.add( "content", expectedContent );
448       builder.add( "allowWildcards", allowWildcards );
449       builder.add( "ignoreNamspaceDifferences", ignoreNamspaceDifferences );
450       return builder.finish();
451    }
452 
453    public void selectFromCurrent()
454    {
455       XmlCursor cursor = null;
456 
457       try
458       {
459          String assertableContent = getAssertable().getAssertableContent();
460          if( assertableContent == null || assertableContent.trim().length() == 0 )
461          {
462             UISupport.showErrorMessage( "Missing content to select from" );
463             return;
464          }
465 
466          XmlObject xml = XmlObject.Factory.parse( assertableContent );
467 
468          String txt = pathArea == null || !pathArea.isVisible() ? getPath() : pathArea.getSelectedText();
469          if( txt == null )
470             txt = pathArea == null ? "" : pathArea.getText();
471 
472          WsdlTestRunContext context = new WsdlTestRunContext( (TestStep) getAssertable().getModelItem() );
473 
474          String expandedPath = PropertyExpansionUtils.expandProperties( context, txt.trim() );
475 
476          if( contentArea != null && contentArea.isVisible() )
477             contentArea.setText( "" );
478 
479          cursor = xml.newCursor();
480          cursor.selectPath( expandedPath );
481          if( !cursor.toNextSelection() )
482          {
483             UISupport.showErrorMessage( "No match in current response" );
484          }
485          else if( cursor.hasNextSelection() )
486          {
487             UISupport.showErrorMessage( "More than one match in current response" );
488          }
489          else
490          {
491             String stringValue = XmlUtils.getValueForMatch( cursor );
492 
493             if( contentArea != null && contentArea.isVisible() )
494                contentArea.setText( stringValue );
495             else
496                setExpectedContent( stringValue );
497          }
498       }
499       catch( Throwable e )
500       {
501          UISupport.showErrorMessage( e.toString() );
502          SoapUI.logError( e );
503       }
504       finally
505       {
506          if( cursor != null )
507             cursor.dispose();
508       }
509    }
510 
511    private final class InternalDifferenceListener implements DifferenceListener
512    {
513       private StringList nodesToRemove = new StringList();
514 
515       public int differenceFound( Difference diff )
516       {
517          if( allowWildcards
518                  && ( diff.getId() == DifferenceEngine.TEXT_VALUE.getId() || diff.getId() == DifferenceEngine.ATTR_VALUE.getId() ) )
519          {
520             if( diff.getControlNodeDetail().getValue().equals( "*" ) )
521             {
522                Node node = diff.getTestNodeDetail().getNode();
523                String xp = XmlUtils.createAbsoluteXPath( node.getNodeType() == Node.ATTRIBUTE_NODE ? node : node.getParentNode() );
524                nodesToRemove.add( xp );
525                return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
526             }
527          }
528          else if( ignoreNamspaceDifferences && diff.getId() == DifferenceEngine.NAMESPACE_PREFIX_ID )
529          {
530             return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
531          }
532 
533          return Diff.RETURN_ACCEPT_DIFFERENCE;
534       }
535 
536       public void skippedComparison( Node arg0, Node arg1 )
537       {
538 
539       }
540 
541       public StringList getNodesToRemove()
542       {
543          return nodesToRemove;
544       }
545    }
546 
547    public class OkAction extends AbstractAction
548    {
549       public OkAction()
550       {
551          super( "Save" );
552       }
553 
554       public void actionPerformed( ActionEvent arg0 )
555       {
556          setPath( pathArea.getText().trim() );
557          setExpectedContent( contentArea.getText() );
558          setAllowWildcards( allowWildcardsCheckBox.isSelected() );
559          setIgnoreNamspaceDifferences( ignoreNamspaceDifferencesCheckBox.isSelected() );
560          setConfiguration( createConfiguration() );
561          configureResult = true;
562          configurationDialog.setVisible( false );
563       }
564    }
565 
566    public class CancelAction extends AbstractAction
567    {
568       public CancelAction()
569       {
570          super( "Cancel" );
571       }
572 
573       public void actionPerformed( ActionEvent arg0 )
574       {
575          configureResult = false;
576          configurationDialog.setVisible( false );
577       }
578    }
579 
580    public class DeclareNamespacesFromCurrentAction extends AbstractAction
581    {
582       public DeclareNamespacesFromCurrentAction()
583       {
584          super( "Declare" );
585          putValue( Action.SHORT_DESCRIPTION, "Add namespace declaration from current message to XPath expression" );
586       }
587 
588       public void actionPerformed( ActionEvent arg0 )
589       {
590          try
591          {
592             String content = getAssertable().getAssertableContent();
593             if( content != null && content.trim().length() > 0 )
594             {
595                pathArea.setText( XmlUtils.declareXPathNamespaces( content ) + pathArea.getText() );
596             }
597             else if( UISupport.confirm( "Declare namespaces from schema instead?", "Missing Response" ) )
598             {
599                pathArea.setText( XmlUtils.declareXPathNamespaces( (WsdlInterface) getAssertable().getInterface() )
600                        + pathArea.getText() );
601             }
602          }
603          catch( Exception e )
604          {
605             log.error( e.getMessage() );
606          }
607       }
608    }
609 
610    public class TestPathAction extends AbstractAction
611    {
612       public TestPathAction()
613       {
614          super( "Test" );
615          putValue( Action.SHORT_DESCRIPTION,
616                  "Tests the XPath expression for the current message against the Expected Content field" );
617       }
618 
619       public void actionPerformed( ActionEvent arg0 )
620       {
621          String oldPath = getPath();
622          String oldContent = getExpectedContent();
623          boolean oldAllowWildcards = isAllowWildcards();
624 
625          setPath( pathArea.getText().trim() );
626          setExpectedContent( contentArea.getText() );
627          setAllowWildcards( allowWildcardsCheckBox.isSelected() );
628          setIgnoreNamspaceDifferences( ignoreNamspaceDifferencesCheckBox.isSelected() );
629 
630          try
631          {
632             String msg = assertContent( getAssertable().getAssertableContent(), new WsdlTestRunContext( (TestStep) getAssertable()
633                     .getModelItem() ), "Response" );
634             UISupport.showInfoMessage( msg, "Success" );
635          }
636          catch( AssertionException e )
637          {
638             UISupport.showErrorMessage( e.getMessage() );
639          }
640 
641          setPath( oldPath );
642          setExpectedContent( oldContent );
643          setAllowWildcards( oldAllowWildcards );
644       }
645    }
646 
647    public class SelectFromCurrentAction extends AbstractAction
648    {
649       public SelectFromCurrentAction()
650       {
651          super( "Select from current" );
652          putValue( Action.SHORT_DESCRIPTION,
653                  "Selects the XPath expression from the current message into the Expected Content field" );
654       }
655 
656       public void actionPerformed( ActionEvent arg0 )
657       {
658          selectFromCurrent();
659       }
660    }
661 
662    @Override
663    protected String internalAssertRequest( MessageExchange messageExchange, SubmitContext context )
664            throws AssertionException
665    {
666       if( !messageExchange.hasRequest( true ) )
667          return "Missing Request";
668       else
669          return assertContent( messageExchange.getRequestContent(), context, "Request" );
670    }
671 
672    public JTextArea getContentArea()
673    {
674       return contentArea;
675    }
676 
677    public JTextArea getPathArea()
678    {
679       return pathArea;
680    }
681 
682    @Override
683    public PropertyExpansion[] getPropertyExpansions()
684    {
685       List<PropertyExpansion> result = new ArrayList<PropertyExpansion>();
686 
687       result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this, "expectedContent" ) );
688       result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this, "path" ) );
689 
690       return result.toArray( new PropertyExpansion[result.size()] );
691    }
692 
693    public XPathReference[] getXPathReferences()
694    {
695       List<XPathReference> result = new ArrayList<XPathReference>();
696 
697       if( StringUtils.hasContent( getPath() ) )
698       {
699          TestModelItem testStep = (TestModelItem) getAssertable().getModelItem();
700          TestProperty property = testStep instanceof WsdlTestRequestStep ? testStep.getProperty( "Response" ) : testStep.getProperty( "Request" );
701          result.add( new XPathReferenceImpl( "XPath for " + getName() + " XPathContainsAssertion in " + testStep.getName(), property, this, "path" ) );
702       }
703 
704       return result.toArray( new XPathReference[result.size()] );
705    }
706 
707    public static class Factory extends AbstractTestAssertionFactory
708    {
709       public Factory()
710       {
711          super( XPathContainsAssertion.ID, XPathContainsAssertion.LABEL, XPathContainsAssertion.class );
712       }
713    }
714 }