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