[Webtest] Patch for com.canoo.webtest.steps.request.ClickButton

Marc Guillemot webtest@lists.canoo.com
Sun, 31 Aug 2003 19:05:06 +0200


This is a multi-part message in MIME format.
--------------080504080004090708010708
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Hi,

in com.canoo.webtest.steps.request.ClickButton, image button and normal submit 
buttons are not handled the same way as they should:

----------------------------------------
if (isImageButton())
{
   WebRequest req = form.getRequest(button, fClickPositionX, fClickPositionY);
   setIntermediateResponse(context.getWebConversation().getResponse(req));
}
else
{
   button.click();
   setIntermediateResponse(context.getWebConversation().getCurrentPage());
}
----------------------------------------


should be for instance:
----------------------------------------
if (isImageButton())
{
   WebRequest req = form.getRequest(button, fClickPositionX, fClickPositionY);
   setIntermediateResponse(context.getWebConversation().getResponse(req));
}
else
{
   WebRequest req = form.getRequest(button);
   setIntermediateResponse(context.getWebConversation().getCurrentPage());
}
----------------------------------------

Indeed context.getWebConversation().getCurrentPage() doesn't necessary represent 
the result of the form submission but "the current top page in the main window". 
This may be different when the website beeing tested contains frames / windows.

Btw, the class could be improved for extension as it contains private methods 
(isImageButton()) and no accessors for usefull members.

Attached is my fixed version of the class.

Marc.

--------------080504080004090708010708
Content-Type: text/plain;
 name="ClickButton.java"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="ClickButton.java"

package com.canoo.webtest.steps.request;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.xml.sax.SAXException;

import com.canoo.webtest.engine.Context;
import com.canoo.webtest.engine.StepExecutionException;
import com.canoo.webtest.engine.StepFailedException;
import com.canoo.webtest.interfaces.IButtonLocator;
import com.canoo.webtest.interfaces.IFormLocator;
import com.canoo.webtest.interfaces.IIndexLocator;
import com.canoo.webtest.self.Block;
import com.canoo.webtest.steps.ParameterHolder;
import com.canoo.webtest.steps.Step;
import com.canoo.webtest.steps.locator.ButtonByNameAndValueLocator;
import com.canoo.webtest.steps.locator.ButtonByNameLocator;
import com.canoo.webtest.steps.locator.ButtonByValueLocator;
import com.canoo.webtest.steps.locator.FormLocator;
import com.canoo.webtest.steps.locator.IndexLocator;
import com.canoo.webtest.steps.locator.LocatorError;
import com.meterware.httpunit.Button;
import com.meterware.httpunit.IllegalRequestParameterException;
import com.meterware.httpunit.SubmitButton;
import com.meterware.httpunit.WebForm;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;

public class ClickButton extends LabelTestStepSpecification
{
	private static final int NOT_SET = -1;
	private int fClickPositionX = NOT_SET;
	private int fClickPositionY = NOT_SET;
	IFormLocator fFormLocator = null;
	IIndexLocator fIndexLocator = null;

	/**
	 * C'tor used for instance creation as nested element by ant
	 */
	public ClickButton()
	{
		super();
	}

	/**
	 * Gotcha: Use the implementation and *not* the interface for FormLocator
	 * or a "strange" ant introspection exception will occur ...
	 */
	public void addForm(FormLocator locator)
	{
		fFormLocator = locator;
	}

	/**
	 * Gotcha: Use the implementation and *not* the interface for IndexLocator
	 * or a "strange" ant exception will occur ...
	 */
	public void addIndex(IndexLocator locator)
	{
		fIndexLocator = locator;
	}

	/**
	 * Add a new multivalue parameter to the request. Add the new values to the existing ones if the parameter
	 * exists already in the request.
	 *
	 * @param form The WebForm to which the parameter shall be added
	 * @param parameterHolder The parameterHolder representing the parameter
	 */
	private void addParameter(WebForm form, ParameterHolder parameterHolder)
	{
		List parameterValues = parameterHolder.getValueList();
		if (parameterHolder.isPreserveExistingValue())
		{
			String[] existingParameters = form.getParameterValues(parameterHolder.getName());
			parameterValues.addAll(Arrays.asList(existingParameters));
		}
		form.setParameter(parameterHolder.getName(),
			(String[]) parameterValues.toArray(new String[parameterValues.size()]));
	}

	/**
	 * Use the locator mechanism to get to the required request by using the configured
	 * locators (if any). Configure the generated request with parameters.
	 *
	 * @throws com.canoo.webtest.engine.StepFailedException if the locator has a problem (e.g. no button found, multiple
	 * buttons found, ...) or if a problem with parameters exists.
	 */
	public void doExecute(Context context) throws Exception
	{
		verifyParameters();
		super.doExecute(context);

		try
		{
			SubmitButton button = getLocator(getName(), getLabel(), fFormLocator, fIndexLocator).locateButton(context);
			WebForm form = findFormFor(context.getLastResponse(), button);
			setParameters(context, form);
			gotoTarget(context, button, form);
		}
		catch (LocatorError error)
		{
			throw new StepFailedException(error.getMessage(), this);
		}
	}

	protected WebForm findFormFor(WebResponse resp, Button button) throws SAXException
	{
		WebForm result = null;
		for (int i = 0; result == null && i < resp.getForms().length; i++)
		{
			WebForm form = resp.getForms()[i];
			for (int j = 0; j < form.getButtons().length; j++)
			{
				Button candidate = form.getButtons()[j];
				if (candidate.equals(button)) result = form;
			}
		}
		return result;
	}

	/**
	 * Choose the appropriate locator depending on whta was provided (name and/or value).
	 *
	 * @param name The name of the button to locate or null
	 * @param value The value of the button to locate or null
	 * @param formLocator The form locator to use by the chosen button locator or null
	 * @param indexLocator The index locator to use by the chosen button locator or null
	 *
	 * @return A locator appropriate for the provided parameters
	 */
	protected IButtonLocator getLocator(String name, String value, IFormLocator formLocator, IIndexLocator indexLocator)
	{
		if (name != null && value != null)
		{
			return new ButtonByNameAndValueLocator(name, value, formLocator, indexLocator);
		}
		else if (name != null)
		{
			return new ButtonByNameLocator(name, formLocator, indexLocator);
		}
		else if (value != null)
		{
			return new ButtonByValueLocator(value, formLocator, indexLocator);
		}

		throw new StepExecutionException("Either name or value must be specified!");
	}

	/**
	 * Collect parameters for reporting. Our our parameters to the ones obtained
	 * from super. Add click position only if it is different from the initial
	 * values.
	 *
	 * FIXME: What is a good initial value for click positions?
	 *
	 * @return a HashMap containing paramter names (key) and their associated
	 * values
	 */
	public Map getParameterDictionary()
	{
		Map map = super.getParameterDictionary();
		if (fClickPositionX != NOT_SET)
		{
			map.put("x", "" + fClickPositionX);
		}
		if (fClickPositionY != NOT_SET)
		{
			map.put("y", "" + fClickPositionY);
		}
		return map;
	}

	public int getX()
	{
		return fClickPositionX;
	}

	public int getY()
	{
		return fClickPositionY;
	}


	protected WebResponse gotoTarget(final Context context, final SubmitButton button, final WebForm form) throws Exception
	{
		final Step origin = this;
		return protectedGoto(context, "click button", new Block(){
			public void call() throws Exception
			{
				logText(context, "-> gotoTarget(by Button): name=" + button.getName() + " value=" + button.getValue());
				prepareConversation(context);

				WebRequest req = null;
				if (isImageButton())
				{
					req =
						form.getRequest(
							button,
							getX(),
							getY());
				}
				else
				{
					req = form.getRequest(button);
				}

				setIntermediateResponse(
					context.getWebConversation().getResponse(req));
			}
		});
	}

	protected boolean isImageButton()
	{
		return fClickPositionX != NOT_SET && fClickPositionY != NOT_SET;
	}

	/**
	 * Add all parameters collected in the nextParameters HashMap to the request and remove
	 * all parameters from the request that are listed in the resetNextParameters list.
	 * Set also the click position if coordinates are configured.
	 *
	 * @param context The current test context
	 * @param form The form to add/remove parameters from
	 *
	 * @throws com.canoo.webtest.engine.StepFailedException if a parameter to remove is not present in the request,
	 * if a parameter is not available in the form
	 * or if click positions are configured and target button is no image button
	 */
	protected void setParameters(Context context, WebForm form) throws StepFailedException
	{
		for (Iterator i = context.getNextParameters().entrySet().iterator(); i.hasNext();)
		{
			ParameterHolder parameter = (ParameterHolder) ((Map.Entry) i.next()).getValue();
			try
			{
				addParameter(form, parameter);
			}
			catch (IllegalRequestParameterException irpe)
			{
				throw new StepFailedException("No such parameter in form: " + parameter.getName(), this);
			}
		}

		for (Iterator i = context.getNextResetParameters().iterator(); i.hasNext();)
		{

			String resetParameterName = (String) i.next();
			if (form.getParameterValues(resetParameterName).length > 0)
			{
				form.removeParameter(resetParameterName);
			}
			else
			{
				throw new StepFailedException("Could not remove parameter from request: " + resetParameterName, this);
			}
		}
	}

	public void setX(int clickPositionX)
	{
		fClickPositionX = clickPositionX;
	}

	public void setY(int clickPositionY)
	{
		fClickPositionY = clickPositionY;
	}

	/**
	 * If one click position is set, the other must be set as well.
	 * Either name or label or both must be specified.
	 *
	 * @throws com.canoo.webtest.engine.StepExecutionException if an invalid parameter setting is detected
	 */
	protected void verifyParameters()
	{

		if ((fClickPositionX != NOT_SET && fClickPositionY == NOT_SET) ||
			(fClickPositionX == NOT_SET && fClickPositionY != NOT_SET))
		{
			throw new StepExecutionException("X and Y values must be set for click button support!");
		}

		if (getLabel() == null && getName() == null)
			throw new StepExecutionException("Required parameter label or name must be set!");
	}
}

--------------080504080004090708010708--