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