View Javadoc

1   /*
2    *  soapui, copyright (C) 2005 Ole Matzura / eviware.com 
3    *
4    *  SoapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
7    *
8    *  SoapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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.JButton;
22  import javax.swing.JDialog;
23  import javax.swing.JLabel;
24  import javax.swing.JOptionPane;
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.XMLAssert;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Node;
38  
39  import com.eviware.soapui.SoapUI;
40  import com.eviware.soapui.config.RequestAssertionConfig;
41  import com.eviware.soapui.impl.wsdl.teststeps.WsdlAssertion;
42  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
43  import com.eviware.soapui.support.UISupport;
44  import com.eviware.soapui.support.XmlUtils;
45  import com.jgoodies.forms.builder.ButtonBarBuilder;
46  
47  /***
48   * Assertion that matches a specified XPath expression and its expected result against
49   * the associated WsdlTestRequests response message
50   * 
51   * @author Ole.Matzura
52   */
53  
54  public class XPathContainsAssertion extends WsdlAssertion
55  {
56  	private final static Logger log = Logger.getLogger( XPathContainsAssertion.class );
57  	private String content;
58     private String path;
59  	private JDialog configurationDialog;
60  	private JTextArea pathArea;
61  	private JTextArea contentArea;
62  
63     public XPathContainsAssertion(RequestAssertionConfig assertionConfig, WsdlTestRequest request)
64     {
65        super(assertionConfig, request);
66        
67        XmlObject[] paths = getConfiguration().selectPath( "$this/path" );
68        if( paths.length == 1 ) 
69        	path = paths[0].newCursor().getTextValue();
70        
71        paths = getConfiguration().selectPath( "$this/content" );
72        if( paths.length == 1 ) 
73        	content = paths[0].newCursor().getTextValue();
74     }
75     
76     public String getContent()
77  	{
78  		return content;
79  	}
80  
81  	public void setContent(String content)
82  	{
83  		this.content = content;
84  	}
85  
86  	public String getPath()
87  	{
88  		return path;
89  	}
90  
91  	public void setPath(String path)
92  	{
93  		this.path = path;
94  	}
95  
96  	public String assertRequest(WsdlTestRequest request) throws AssertionException
97     {
98  		return assertResponse( request.getResponseContent() );
99     }
100 	
101 	public String assertResponse( String response ) throws AssertionException
102 	{
103       try
104 		{
105       	if( path == null ) return "Missing path for XPath assertion";
106       	if( content == null ) return "Missing content for XPath assertion";
107       	
108       	XmlObject xml = XmlObject.Factory.parse( response );
109       	XmlObject[] items = xml.selectPath( path );
110       	
111       	XmlObject contentObj = null;
112       	
113       	try
114 			{
115 				contentObj = XmlObject.Factory.parse(content);
116 			}
117 			catch (Exception e)
118 			{
119 				// this is ok.. it just means that the content to match is not xml but
120 				// (hopefully) just a string
121 			}
122       	
123       	if( items.length == 0 )
124       		throw new Exception( "Missing content for xpath [" + path + "]" );
125       	
126       	XmlOptions options = new XmlOptions();
127    		options.setSavePrettyPrint();
128    		options.setSaveOuter();
129    		
130       	for( int c = 0; c < items.length; c++ )
131       	{
132       		try
133 				{
134 					if (contentObj == null)
135 					{
136 						if( items[c] instanceof XmlAnySimpleType )
137 						{
138 							XMLAssert.assertEquals(content, ((XmlAnySimpleType)items[c]).getStringValue() );
139 						}
140 						else
141 						{
142 							Node domNode = items[c].getDomNode();
143 							if (domNode.getNodeType() == Node.ELEMENT_NODE)
144 							{
145 								XMLAssert.assertEquals(content, XmlUtils.getElementText((Element) domNode));
146 							}
147 							else
148 							{
149 								XMLAssert.assertEquals(content, domNode.getNodeValue());
150 							}
151 						}
152 					}
153 					else
154 					{
155 						XMLAssert.assertXMLEqual(contentObj.xmlText(options), items[c].xmlText(options));
156 					}
157 					
158 					break;
159 				}
160 				catch (Throwable e)
161 				{
162 					if( c == items.length-1 )
163 						throw e;
164 				}
165       	}
166 		}
167 		catch (Throwable e)
168 		{
169 			String msg = "XPathContains assertion failed for path [" + path + 
170 					"] : " + e.getClass().getSimpleName() + ":" + e.getMessage();
171 			
172 			throw new AssertionException( new AssertionError(msg) );
173 		}
174       
175       return "Response matches content for [" + path + "]";
176    }
177 
178    public void configure()
179    {
180    	if( configurationDialog == null )
181    		buildConfigurationDialog();
182    	
183    	pathArea.setText( path );
184    	contentArea.setText( content );
185    	
186    	UISupport.showDialog( configurationDialog );
187    }
188 
189    private void buildConfigurationDialog()
190 	{
191       configurationDialog = new JDialog( SoapUI.getInstance().getFrame() );
192 		configurationDialog.setTitle("XPath Match configuration" );
193       
194       JPanel contentPanel = new JPanel( new BorderLayout() );
195       JLabel label = new JLabel( "<html><b>Specify xpath expression and matching content below</b><br>" +
196       		"declare namespaces with <code>declare namespace &lt;prefix&gt;='&lt;namespace&gt;';</code></html>" );
197       label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
198       contentPanel.add( label, BorderLayout.NORTH );
199       
200       JSplitPane splitPane = UISupport.createVerticalSplit();
201       pathArea = new JTextArea();
202 		splitPane.setTopComponent( new JScrollPane( pathArea ));
203       
204       contentArea = new JTextArea();
205 		splitPane.setBottomComponent( new JScrollPane( contentArea ));
206 		splitPane.setDividerLocation( 100 );
207       splitPane.setBorder(BorderFactory.createEmptyBorder( 0, 3, 0, 3 ));
208 		
209       contentPanel.add( splitPane, BorderLayout.CENTER );
210       
211       ButtonBarBuilder builder = new ButtonBarBuilder();
212       
213       builder.addFixed( new JButton( new SelectFromCurrentAction() ));
214       builder.addRelatedGap();
215       builder.addFixed( new JButton( new TestPathAction() ));
216       builder.addRelatedGap();
217       builder.addFixed( new JButton( new DeclareNamespacesFromCurrentAction()));
218       
219       builder.addGlue();
220       builder.addFixed( new JButton( new OkAction() ));
221       builder.addRelatedGap();
222       builder.addFixed( new JButton( new CancelAction() ));
223       builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
224       
225       contentPanel.add( builder.getPanel(), BorderLayout.SOUTH );
226       
227       configurationDialog.setContentPane( contentPanel );
228       configurationDialog.setSize(500, 400);
229       
230       configurationDialog.setModal( false );
231 	}
232 
233 	public XmlObject createConfiguration()
234    {
235       XmlObject config = XmlObject.Factory.newInstance();
236       XmlCursor cursor = config.newCursor();
237       cursor.toNextToken();
238       cursor.insertElementWithText( "path", path );
239       cursor.insertElementWithText( "content", content );
240       cursor.dispose();
241       return config;
242    }
243 
244    public boolean isConfigurable()
245    {
246       return true;
247    }
248    
249    public class OkAction extends AbstractAction
250 	{
251 		public OkAction()
252 		{
253 			super( "Save" );
254 		}
255 
256 		public void actionPerformed(ActionEvent arg0)
257 		{
258 			setPath( pathArea.getText().trim() );
259 			setContent( contentArea.getText().trim() );
260 			setConfiguration( createConfiguration() );
261 			
262 			configurationDialog.setVisible( false );
263 		}
264 	}
265 
266    public class CancelAction extends AbstractAction
267    {
268    	public CancelAction()
269    	{
270    		super( "Cancel" );
271    	}
272    	
273 		public void actionPerformed(ActionEvent arg0)
274 		{
275 			configurationDialog.setVisible( false );
276 		}
277    }
278    
279    public class DeclareNamespacesFromCurrentAction extends AbstractAction
280    {
281    	public DeclareNamespacesFromCurrentAction()
282    	{
283    		super( "Declare" );
284    		putValue( Action.SHORT_DESCRIPTION, "Add namespace declaration from current response to xpath expression");
285    	}
286    	
287 		public void actionPerformed(ActionEvent arg0)
288 		{
289 			try
290 			{
291 				String responseContent = ((WsdlTestRequest) getRequest()).getResponseContent();
292 				if( responseContent == null || responseContent.trim().length() == 0 )
293 					return;
294 				
295 				pathArea.setText( XmlUtils.declareXPathNamespaces(responseContent ) + pathArea.getText() );
296 			}
297 			catch (Exception e)
298 			{
299 				log.error( e.getMessage() );
300 			}
301 	
302 		}
303    }
304    
305    public class TestPathAction extends AbstractAction
306 	{
307    	public TestPathAction()
308    	{
309    		super( "Test" );
310    	}
311    	
312 		public void actionPerformed(ActionEvent arg0)
313 		{
314 			String oldPath = getPath();
315 			String oldContent = getContent();
316 			
317 			setPath( pathArea.getText().trim() );
318 			setContent( contentArea.getText().trim() );
319 			
320 			try
321 			{
322 				String msg = assertRequest( ((WsdlTestRequest) getRequest()) );
323 				JOptionPane.showMessageDialog( SoapUI.getInstance().getFrame(), 
324 						 msg, "Success", JOptionPane.INFORMATION_MESSAGE );
325 			}
326 			catch (AssertionException e)
327 			{
328 				JOptionPane.showMessageDialog( SoapUI.getInstance().getFrame(), 
329 						 e.getMessage(), "Assertion Failed", JOptionPane.ERROR_MESSAGE );
330 			}
331 
332 			setPath( oldPath );
333 			setContent( oldContent );
334 		}
335 	}
336    
337    public class SelectFromCurrentAction extends AbstractAction
338 	{
339       private XmlOptions options;
340 
341 		public SelectFromCurrentAction()
342       {
343       	super( "Select from current" );
344       	
345       	options = new XmlOptions();
346 			options.setSavePrettyPrint();
347    		options.setSaveOuter();
348    		options.setSaveAggressiveNamespaces();
349       }
350    	
351 		public void actionPerformed(ActionEvent arg0)
352 		{
353 			try
354 			{
355 				XmlObject xml = XmlObject.Factory
356 						.parse(((WsdlTestRequest) getRequest()).getResponseContent());
357 				
358 				String txt = pathArea.getSelectedText();
359 				if( txt == null ) txt = pathArea.getText();
360 				
361 				XmlObject[] items = xml.selectPath(txt.trim()); 
362 				
363 				contentArea.setText("");
364 				
365 				if( items.length == 0 )
366 				{
367 					JOptionPane.showMessageDialog( SoapUI.getInstance().getFrame(), 
368 							"No match in current response", "Error", JOptionPane.ERROR_MESSAGE );
369 				}
370 				else if( items.length > 1 )
371 				{
372 					JOptionPane.showMessageDialog( SoapUI.getInstance().getFrame(), 
373 							"More than one match in current response", "Error", JOptionPane.ERROR_MESSAGE );
374 				}
375 				else
376 				{
377 					if( items[0] instanceof XmlAnySimpleType )
378 					{
379 						contentArea.setText( ((XmlAnySimpleType)items[0]).getStringValue() );
380 					}
381 					else
382 					{
383 						Node domNode = items[0].getDomNode();
384 						if (domNode.getNodeType() == Node.ELEMENT_NODE)
385 						{
386 							contentArea.setText( items[0].xmlText(options));
387 						}
388 						else
389 						{
390 							contentArea.setText( domNode.getNodeValue());
391 						}
392 					}
393 				}
394 			}
395 			catch (Throwable e)
396 			{
397 				JOptionPane.showMessageDialog( SoapUI.getInstance().getFrame(), 
398 						 e.getMessage(), "Error", JOptionPane.OK_OPTION );
399 			}			
400 		}
401 
402 	}
403 }