1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.teststeps.assertions;
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
21 import javax.swing.AbstractAction;
22 import javax.swing.Action;
23 import javax.swing.BorderFactory;
24 import javax.swing.JButton;
25 import javax.swing.JCheckBox;
26 import javax.swing.JDialog;
27 import javax.swing.JPanel;
28 import javax.swing.JScrollPane;
29 import javax.swing.JSplitPane;
30 import javax.swing.JTextArea;
31 import javax.swing.SwingUtilities;
32
33 import org.apache.log4j.Logger;
34 import org.apache.xmlbeans.XmlAnySimpleType;
35 import org.apache.xmlbeans.XmlCursor;
36 import org.apache.xmlbeans.XmlObject;
37 import org.apache.xmlbeans.XmlOptions;
38 import org.custommonkey.xmlunit.Diff;
39 import org.custommonkey.xmlunit.Difference;
40 import org.custommonkey.xmlunit.DifferenceEngine;
41 import org.custommonkey.xmlunit.DifferenceListener;
42 import org.custommonkey.xmlunit.XMLAssert;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.Node;
45
46 import com.eviware.soapui.SoapUI;
47 import com.eviware.soapui.config.RequestAssertionConfig;
48 import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
49 import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
50 import com.eviware.soapui.impl.wsdl.submit.filters.PropertyExpansionRequestFilter;
51 import com.eviware.soapui.impl.wsdl.support.HelpUrls;
52 import com.eviware.soapui.impl.wsdl.support.assertions.Assertable;
53 import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
54 import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
55 import com.eviware.soapui.model.iface.SubmitContext;
56 import com.eviware.soapui.support.UISupport;
57 import com.eviware.soapui.support.components.JUndoableTextArea;
58 import com.eviware.soapui.support.components.JXToolBar;
59 import com.eviware.soapui.support.xml.XmlObjectConfigurationBuilder;
60 import com.eviware.soapui.support.xml.XmlObjectConfigurationReader;
61 import com.eviware.soapui.support.xml.XmlUtils;
62 import com.jgoodies.forms.builder.ButtonBarBuilder;
63
64 /***
65 * Assertion that matches a specified XPath expression and its expected result
66 * against the associated WsdlTestRequests response message
67 *
68 * @author Ole.Matzura
69 */
70
71 public class XPathContainsAssertion extends WsdlMessageAssertion implements RequestAssertion, ResponseAssertion
72 {
73 private final static Logger log = Logger.getLogger( XPathContainsAssertion.class );
74 private String expectedContent;
75 private String path;
76 private JDialog configurationDialog;
77 private JTextArea pathArea;
78 private JTextArea contentArea;
79 private boolean configureResult;
80 private boolean allowWildcards;
81
82 public static final String ID = "XPath Match";
83 public static final String LABEL = "XPath Match";
84 private JCheckBox allowWildcardsCheckBox;
85
86 public XPathContainsAssertion( RequestAssertionConfig assertionConfig, Assertable assertable )
87 {
88 super( assertionConfig, assertable, true, true, true );
89
90 XmlObjectConfigurationReader reader = new XmlObjectConfigurationReader( getConfiguration() );
91 path = reader.readString( "path", null );
92 expectedContent = reader.readString( "content", null );
93 allowWildcards = reader.readBoolean( "allowWildcards", false );
94 }
95
96 public String getExpectedContent()
97 {
98 return expectedContent;
99 }
100
101 public void setContent( String content )
102 {
103 this.expectedContent = content;
104 setConfiguration( createConfiguration() );
105 }
106
107 public String getPath()
108 {
109 return path;
110 }
111
112 public void setPath( String path )
113 {
114 this.path = path;
115 setConfiguration( createConfiguration() );
116 }
117
118 public boolean isAllowWildcards()
119 {
120 return allowWildcards;
121 }
122
123 public void setAllowWildcards( boolean allowWildcards )
124 {
125 this.allowWildcards = allowWildcards;
126 }
127
128 protected String internalAssertResponse( WsdlMessageExchange messageExchange, SubmitContext context )
129 throws AssertionException
130 {
131 return assertContent( messageExchange.getResponseContent(), context, "Response" );
132 }
133
134 public String assertContent( String response, SubmitContext context, String type ) throws AssertionException
135 {
136 try
137 {
138 if( path == null )
139 return "Missing path for XPath assertion";
140 if( expectedContent == null )
141 return "Missing content for XPath assertion";
142
143 XmlObject xml = XmlObject.Factory.parse( response );
144 String expandedPath = PropertyExpansionRequestFilter.expandProperties( context, path );
145 XmlObject[] items = xml.selectPath( expandedPath );
146
147 XmlObject contentObj = null;
148 String expandedContent = PropertyExpansionRequestFilter.expandProperties( context, expectedContent );
149
150 try
151 {
152 contentObj = XmlObject.Factory.parse( expandedContent );
153 }
154 catch( Exception e )
155 {
156
157
158
159 }
160
161 if( items.length == 0 )
162 throw new Exception( "Missing content for xpath [" + path + "] in " + type );
163
164 XmlOptions options = new XmlOptions();
165 options.setSavePrettyPrint();
166 options.setSaveOuter();
167
168 for( int c = 0; c < items.length; c++ )
169 {
170 try
171 {
172 if( contentObj == null )
173 {
174 if( items[c] instanceof XmlAnySimpleType )
175 {
176 String value = ( ( XmlAnySimpleType ) items[c] ).getStringValue();
177 String expandedValue = PropertyExpansionRequestFilter.expandProperties( context, value );
178 XMLAssert.assertEquals( expandedContent, expandedValue );
179 }
180 else
181 {
182 Node domNode = items[c].getDomNode();
183 if( domNode.getNodeType() == Node.ELEMENT_NODE )
184 {
185 String expandedValue = PropertyExpansionRequestFilter.expandProperties( context, XmlUtils
186 .getElementText( ( Element ) domNode ) );
187 XMLAssert.assertEquals( expandedContent, expandedValue );
188 }
189 else
190 {
191 String expandedValue = PropertyExpansionRequestFilter.expandProperties( context, domNode
192 .getNodeValue() );
193 XMLAssert.assertEquals( expandedContent, expandedValue );
194 }
195 }
196 }
197 else
198 {
199 compareValues( contentObj.xmlText( options ), items[c].xmlText( options ) );
200 }
201
202 break;
203 }
204 catch( Throwable e )
205 {
206 if( c == items.length - 1 )
207 throw e;
208 }
209 }
210 }
211 catch( Throwable e )
212 {
213 String msg = "XPathContains assertion failed for path [" + path + "] : " + e.getClass().getSimpleName() + ":"
214 + e.getMessage();
215
216 throw new AssertionException( new AssertionError( msg ) );
217 }
218
219 return type + " matches content for [" + path + "]";
220 }
221
222 private void compareValues( String expandedContent, String expandedValue ) throws Exception
223 {
224 Diff diff = new Diff( expandedContent, expandedValue );
225 diff.overrideDifferenceListener( new DifferenceListener()
226 {
227
228 public int differenceFound( Difference diff )
229 {
230 if( allowWildcards
231 && ( diff.getId() == DifferenceEngine.TEXT_VALUE.getId() || diff.getId() == DifferenceEngine.ATTR_VALUE
232 .getId() ) )
233 {
234 if( diff.getControlNodeDetail().getValue().equals( "*" ) )
235 return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
236 }
237
238 return Diff.RETURN_ACCEPT_DIFFERENCE;
239 }
240
241 public void skippedComparison( Node arg0, Node arg1 )
242 {
243
244 }
245 } );
246
247 if( !diff.identical() )
248 throw new Exception( diff.toString() );
249 }
250
251 public boolean configure()
252 {
253 if( configurationDialog == null )
254 buildConfigurationDialog();
255
256 pathArea.setText( path );
257 contentArea.setText( expectedContent );
258 allowWildcardsCheckBox.setSelected( allowWildcards );
259
260 UISupport.showDialog( configurationDialog );
261 return configureResult;
262 }
263
264 protected void buildConfigurationDialog()
265 {
266 configurationDialog = new JDialog( UISupport.getMainFrame() );
267 configurationDialog.setTitle( "XPath Match configuration" );
268 configurationDialog.addWindowListener( new WindowAdapter()
269 {
270 public void windowOpened( WindowEvent event )
271 {
272 SwingUtilities.invokeLater( new Runnable()
273 {
274 public void run()
275 {
276 pathArea.requestFocusInWindow();
277 }
278 } );
279 }
280 } );
281
282 JPanel contentPanel = new JPanel( new BorderLayout() );
283 contentPanel.add( UISupport.buildDescription( "Specify xpath expression and expected result",
284 "declare namespaces with <code>declare namespace <prefix>='<namespace>';</code>", null ),
285 BorderLayout.NORTH );
286
287 JSplitPane splitPane = UISupport.createVerticalSplit();
288
289 JPanel pathPanel = new JPanel( new BorderLayout() );
290 JXToolBar pathToolbar = UISupport.createToolbar();
291 addPathEditorActions( pathToolbar );
292
293 pathArea = new JUndoableTextArea();
294 pathArea.setToolTipText( "Specifies the XPath expression to select from the message for validation" );
295
296 pathPanel.add( pathToolbar, BorderLayout.NORTH );
297 pathPanel.add( new JScrollPane( pathArea ), BorderLayout.CENTER );
298
299 splitPane.setTopComponent( UISupport.addTitledBorder( pathPanel, "XPath Expression" ) );
300
301 JPanel matchPanel = new JPanel( new BorderLayout() );
302 JXToolBar contentToolbar = UISupport.createToolbar();
303 addMatchEditorActions( contentToolbar );
304
305 contentArea = new JUndoableTextArea();
306 contentArea.setToolTipText( "Specifies the expected result of the XPath expression" );
307
308 matchPanel.add( contentToolbar, BorderLayout.NORTH );
309 matchPanel.add( new JScrollPane( contentArea ), BorderLayout.CENTER );
310
311 splitPane.setBottomComponent( UISupport.addTitledBorder( matchPanel, "Expected Result" ) );
312 splitPane.setDividerLocation( 150 );
313 splitPane.setBorder( BorderFactory.createEmptyBorder( 0, 1, 0, 1 ) );
314
315 contentPanel.add( splitPane, BorderLayout.CENTER );
316
317 ButtonBarBuilder builder = new ButtonBarBuilder();
318
319 ShowOnlineHelpAction showOnlineHelpAction = new ShowOnlineHelpAction( HelpUrls.XPATHASSERTIONEDITOR_HELP_URL );
320 builder.addFixed( UISupport.createToolbarButton( showOnlineHelpAction ) );
321 builder.addGlue();
322
323 JButton okButton = new JButton( new OkAction() );
324 builder.addFixed( okButton );
325 builder.addRelatedGap();
326 builder.addFixed( new JButton( new CancelAction() ) );
327
328 builder.setBorder( BorderFactory.createEmptyBorder( 1, 5, 5, 5 ) );
329
330 contentPanel.add( builder.getPanel(), BorderLayout.SOUTH );
331
332 configurationDialog.setContentPane( contentPanel );
333 configurationDialog.setSize( 600, 500 );
334 configurationDialog.setModal( true );
335 UISupport.initDialogActions( configurationDialog, showOnlineHelpAction, okButton );
336 }
337
338 protected void addPathEditorActions( JXToolBar toolbar )
339 {
340 toolbar.addFixed( new JButton( new DeclareNamespacesFromCurrentAction() ) );
341 }
342
343 protected void addMatchEditorActions( JXToolBar toolbar )
344 {
345 toolbar.addFixed( new JButton( new SelectFromCurrentAction() ) );
346 toolbar.addRelatedGap();
347 toolbar.addFixed( new JButton( new TestPathAction() ) );
348 allowWildcardsCheckBox = new JCheckBox( "Allow Wildcards" );
349
350 Dimension dim = new Dimension( 100, 20 );
351
352 allowWildcardsCheckBox.setSize( dim );
353 allowWildcardsCheckBox.setPreferredSize( dim );
354
355 allowWildcardsCheckBox.setOpaque( false );
356 toolbar.addRelatedGap();
357 toolbar.addFixed( allowWildcardsCheckBox );
358 }
359
360 public XmlObject createConfiguration()
361 {
362 XmlObjectConfigurationBuilder builder = new XmlObjectConfigurationBuilder();
363 builder.add( "path", path );
364 builder.add( "content", expectedContent );
365 builder.add( "allowWildcards", allowWildcards );
366 return builder.finish();
367 }
368
369 public void selectFromCurrent()
370 {
371 XmlCursor cursor = null;
372
373 try
374 {
375 XmlOptions options = new XmlOptions();
376 options.setSavePrettyPrint();
377 options.setSaveOuter();
378 options.setSaveAggressiveNamespaces();
379
380 String assertableContent = getAssertable().getAssertableContent();
381 if( assertableContent == null || assertableContent.trim().length() == 0 )
382 {
383 UISupport.showErrorMessage( "Missing content to select from" );
384 return;
385 }
386
387 XmlObject xml = XmlObject.Factory.parse( assertableContent );
388
389 String txt = pathArea == null || !pathArea.isVisible() ? getPath() : pathArea.getSelectedText();
390 if( txt == null )
391 txt = pathArea == null ? "" : pathArea.getText();
392
393 WsdlTestRunContext context = new WsdlTestRunContext( getAssertable().getTestStep() );
394
395 String expandedPath = PropertyExpansionRequestFilter.expandProperties( context, txt.trim() );
396
397 if( contentArea != null && contentArea.isVisible() )
398 contentArea.setText( "" );
399
400 cursor = xml.newCursor();
401 cursor.selectPath( expandedPath );
402 if( !cursor.toNextSelection() )
403 {
404 UISupport.showErrorMessage( "No match in current response" );
405 }
406 else if( cursor.hasNextSelection() )
407 {
408 UISupport.showErrorMessage( "More than one match in current response" );
409 }
410 else
411 {
412 Node domNode = cursor.getDomNode();
413 String stringValue = null;
414
415 if( domNode.getNodeType() == Node.ATTRIBUTE_NODE || domNode.getNodeType() == Node.TEXT_NODE )
416 {
417 stringValue = domNode.getNodeValue();
418 }
419 else if( cursor.getObject() instanceof XmlAnySimpleType )
420 {
421 stringValue = ( ( XmlAnySimpleType ) cursor.getObject() ).getStringValue();
422 }
423 else
424 {
425 if( domNode.getNodeType() == Node.ELEMENT_NODE )
426 {
427 Element elm = ( Element ) domNode;
428 if( elm.getChildNodes().getLength() == 1 && elm.getAttributes().getLength() == 0 )
429 stringValue = XmlUtils.getElementText( elm );
430 else
431 stringValue = cursor.getObject().xmlText( options );
432 }
433 else
434 {
435 stringValue = domNode.getNodeValue();
436 }
437 }
438
439 if( contentArea != null && contentArea.isVisible() )
440 contentArea.setText( stringValue );
441 else
442 setContent( stringValue );
443 }
444 }
445 catch( Throwable e )
446 {
447 UISupport.showErrorMessage( e.toString() );
448 SoapUI.logError( e );
449 }
450 finally
451 {
452 if( cursor != null )
453 cursor.dispose();
454 }
455 }
456
457 public class OkAction extends AbstractAction
458 {
459 public OkAction()
460 {
461 super( "Save" );
462 }
463
464 public void actionPerformed( ActionEvent arg0 )
465 {
466 setPath( pathArea.getText().trim() );
467 setContent( contentArea.getText() );
468 setAllowWildcards( allowWildcardsCheckBox.isSelected() );
469 setConfiguration( createConfiguration() );
470 configureResult = true;
471 configurationDialog.setVisible( false );
472 }
473 }
474
475 public class CancelAction extends AbstractAction
476 {
477 public CancelAction()
478 {
479 super( "Cancel" );
480 }
481
482 public void actionPerformed( ActionEvent arg0 )
483 {
484 configureResult = false;
485 configurationDialog.setVisible( false );
486 }
487 }
488
489 public class DeclareNamespacesFromCurrentAction extends AbstractAction
490 {
491 public DeclareNamespacesFromCurrentAction()
492 {
493 super( "Declare" );
494 putValue( Action.SHORT_DESCRIPTION, "Add namespace declaration from current message to XPath expression" );
495 }
496
497 public void actionPerformed( ActionEvent arg0 )
498 {
499 try
500 {
501 String content = getAssertable().getAssertableContent();
502 if( content != null && content.trim().length() > 0 )
503 {
504 pathArea.setText( XmlUtils.declareXPathNamespaces( content ) + pathArea.getText() );
505 }
506 else if( UISupport.confirm( "Declare namespaces from schema instead?", "Missing Response" ) )
507 {
508 pathArea
509 .setText( XmlUtils.declareXPathNamespaces( getAssertable().getInterface() )
510 + pathArea.getText() );
511 }
512 }
513 catch( Exception e )
514 {
515 log.error( e.getMessage() );
516 }
517 }
518 }
519
520 public class TestPathAction extends AbstractAction
521 {
522 public TestPathAction()
523 {
524 super( "Test" );
525 putValue( Action.SHORT_DESCRIPTION,
526 "Tests the XPath expression for the current message against the Expected Content field" );
527 }
528
529 public void actionPerformed( ActionEvent arg0 )
530 {
531 String oldPath = getPath();
532 String oldContent = getExpectedContent();
533 boolean oldAllowWildcards = isAllowWildcards();
534
535 setPath( pathArea.getText().trim() );
536 setContent( contentArea.getText() );
537 setAllowWildcards( allowWildcardsCheckBox.isSelected() );
538
539 try
540 {
541 String msg = assertContent( getAssertable().getAssertableContent(), new WsdlTestRunContext( getAssertable()
542 .getTestStep() ), "Response" );
543 UISupport.showInfoMessage( msg, "Success" );
544 }
545 catch( AssertionException e )
546 {
547 UISupport.showErrorMessage( e.getMessage() );
548 }
549
550 setPath( oldPath );
551 setContent( oldContent );
552 setAllowWildcards( oldAllowWildcards );
553 }
554 }
555
556 public class SelectFromCurrentAction extends AbstractAction
557 {
558 public SelectFromCurrentAction()
559 {
560 super( "Select from current" );
561 putValue( Action.SHORT_DESCRIPTION,
562 "Selects the XPath expression from the current message into the Expected Content field" );
563 }
564
565 public void actionPerformed( ActionEvent arg0 )
566 {
567 selectFromCurrent();
568 }
569 }
570
571 @Override
572 protected String internalAssertRequest( WsdlMessageExchange messageExchange, SubmitContext context )
573 throws AssertionException
574 {
575 if( !messageExchange.hasRequest( true ) )
576 return "Missing Request";
577 else
578 return assertContent( messageExchange.getRequestContent(), context, "Request" );
579 }
580
581 public JTextArea getContentArea()
582 {
583 return contentArea;
584 }
585
586 public JTextArea getPathArea()
587 {
588 return pathArea;
589 }
590 }