Values over-escaped by FormTester
---------------------------------
Key: WICKET-1094
URL: https://issues.apache.org/jira/browse/WICKET-1094
Project: Wicket
Issue Type: Bug
Affects Versions: 1.3.0-beta4
Reporter: David Shepherdson
FormTester's constructor contains code that visits all components on the form
and calls setFormComponentValue(formComponent, formComponent.getValue()) (or
variations thereof), to store the component's value in the servlet request
parameters. However, by default, FormComponent's getValue() method uses
Strings.escapeMarkup(...) to encode any special characters, such as angle
brackets. This is fine in a 'real' environment, where a Web browser will be
responsible for displaying the escaped characters with their proper values, and
so the proper value will be the one that comes through when the form is
submitted; however, in the Wicket test environment, there isn't anything to do
that extra level of 'un-escaping', meaning there's a danger of the form
components being given escaped values when the form is submitted.
For example, we have a form that contains a text area, whose value contains a
URI enclosed in angle brackets, like so:
<http://test.com/>
When we submit the form with a Web browser, the value set on the model is
exactly that string -- '<http://test.com/>'. However, when we test our page
with FormTester, the FormTester constructor calls getValue() on the component,
which by default returns the escaped form:
<http://test.com/>
When the form is submitted, this is the value set on the model, and so
comparisons to the original string fail.
However, if FormTester were to call setEscapeModelStrings(false) on the form
component before calling getValue() (and then restore the original escaping
setting afterwards), then the value that ends up being provided to the
component at the end would be the correct (unescaped) value, matching the
behaviour when using the page in a browser.
We have worked around this issue by overriding FormTester with a class that
performs a second traversal of the form component hierarchy after calling the
FormTester constructor, replacing the incorrectly escaped values with the
proper ones (changes marked with '// O-P'):
public OurFormTester(String path, Form workingForm, BaseWicketTester
wicketTester, boolean fillBlankString) {
super(path, workingForm, wicketTester, fillBlankString);
fixFormParameterValues(workingForm, fillBlankString);
}
protected void fixFormParameterValues(Form workingForm, final boolean
fillBlankString) {
workingForm.visitFormComponents(new FormComponent.AbstractVisitor()
{
public void onFormComponent(final FormComponent formComponent)
{
// do nothing for invisible component
if (!formComponent.isVisibleInHierarchy())
{
return;
}
// O-P Preserve old escaping value, then turn escaping off
// so that values aren't escaped unnecessarily.
boolean oldEscaping = formComponent.getEscapeModelStrings();
formComponent.setEscapeModelStrings(false);
// if component is text field and do not have exist value, fill
// blank String if required
if (formComponent instanceof AbstractTextComponent)
{
if (Strings.isEmpty(formComponent.getValue()))
{
if (fillBlankString)
{
setFormComponentValue(formComponent, "");
}
}
else
{
setFormComponentValue(formComponent,
formComponent.getValue());
}
}
else if ( (formComponent instanceof DropDownChoice) ||
(formComponent instanceof RadioChoice) ||
(formComponent instanceof CheckBox))
{
setFormComponentValue(formComponent,
formComponent.getValue());
}
else if (formComponent instanceof ListMultipleChoice)
{
final String[] modelValues =
formComponent.getValue().split(FormComponent.VALUE_SEPARATOR);
for (int i = 0; i < modelValues.length; i++)
{
addFormComponentValue(formComponent, modelValues[i]);
}
}
else if (formComponent instanceof CheckGroup)
{
final Collection checkGroupValues = (Collection)
formComponent.getModelObject();
formComponent.visitChildren(Check.class, new IVisitor()
{
public Object component(Component component)
{
if
(checkGroupValues.contains(component.getModelObject()))
{
// O-P Preserve old escaping value, then turn
escaping off
// so that values aren't escaped unnecessarily.
boolean oldEscaping =
component.getEscapeModelStrings();
component.setEscapeModelStrings(false);
addFormComponentValue(formComponent, ((Check)
component).getValue());
// O-P Restore the previous escaping setting.
component.setEscapeModelStrings(oldEscaping);
}
return CONTINUE_TRAVERSAL;
}
});
}
// O-P Restore the previous escaping setting.
formComponent.setEscapeModelStrings(oldEscaping);
}
});
}
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.