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