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