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