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