View Javadoc

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