/* * Copyright 2004-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.webflow.action; import junit.framework.TestCase; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import org.springframework.webflow.core.collection.LocalParameterMap; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.ScopeType; import org.springframework.webflow.test.MockExternalContext; import org.springframework.webflow.test.MockParameterMap; import org.springframework.webflow.test.MockRequestContext; /** * Unit test for the {@link FormAction} class. * * @author Erwin Vervaet */ public class FormActionTests extends TestCase { private static class TestBean { private String prop; public TestBean() { } public String getProp() { return prop; } public void setProp(String prop) { this.prop = prop; } } private static class OtherTestBean { } public static class TestBeanValidator implements Validator { private boolean invoked; public boolean getInvoked() { return invoked; } public boolean supports(Class<?> clazz) { return TestBean.class.equals(clazz); } public void validate(Object formObject, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "prop", "Prop cannot be empty"); invoked = true; } public void validateTestBean(TestBean formObject, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "prop", "Prop cannot be empty"); invoked = true; } } private FormAction action; protected void setUp() throws Exception { action = createFormAction("test"); } public void testSetupForm() throws Exception { MockRequestContext context = new MockRequestContext(); // setupForm() should initialize the form object and the Errors // instance, but no bind & validate should happen since bindOnSetupForm // is not set assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertFalse(getErrors(context).hasErrors()); assertNull(getFormObject(context).getProp()); } protected LocalParameterMap parameters() { MockParameterMap map = new MockParameterMap(); map.put("prop", "value"); return map; } protected LocalParameterMap blankParameters() { MockParameterMap map = new MockParameterMap(); map.put("prop", ""); return map; } public void testSetupFormWithExistingFormObject() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); Errors errors = getErrors(context); errors.reject("dummy"); TestBean formObject = getFormObject(context); formObject.setProp("bla"); // setupForm() should leave the existing form object and Errors instance // untouched, at least when no bind & validate is done (bindOnSetupForm // == false) assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertSame(errors, getErrors(context)); assertSame(formObject, getFormObject(context)); assertTrue(getErrors(context).hasErrors()); assertEquals("bla", getFormObject(context).getProp()); } public void testBindAndValidate() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); // bindAndValidate() should setup a new form object and errors instance // and do a bind & validate context.setAttribute("validatorMethod", "validateTestBean"); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.bindAndValidate(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertFalse(getErrors(context).hasErrors()); assertEquals("value", getFormObject(context).getProp()); } public void testBindAndValidateFailure() throws Exception { MockRequestContext context = new MockRequestContext(); // bindAndValidate() should setup a new form object and errors instance // and do a bind & validate, which fails because the provided value is // empty assertEquals(action.getEventFactorySupport().getErrorEventId(), action.bindAndValidate(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertTrue(getErrors(context).hasErrors()); assertNull(getFormObject(context).getProp()); } public void testBindAndValidateWithExistingFormObject() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); Errors errors = getErrors(context); errors.reject("dummy"); TestBean formObject = getFormObject(context); formObject.setProp("bla"); // bindAndValidate() should leave the existing form object untouched // but should setup a new Errors instance during bind & validate assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.bindAndValidate(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertNotSame(errors, getErrors(context)); assertSame(formObject, getFormObject(context)); assertFalse(getErrors(context).hasErrors()); assertEquals("value", getFormObject(context).getProp()); } // this is what happens in a 'form state' public void testBindAndValidateFailureThenSetupForm() throws Exception { MockRequestContext context = new MockRequestContext(blankParameters()); // setup existing form object & errors assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); TestBean formObject = getFormObject(context); formObject.setProp("bla"); assertEquals(action.getEventFactorySupport().getErrorEventId(), action.bindAndValidate(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertSame(formObject, getFormObject(context)); assertTrue(getErrors(context).hasErrors()); assertEquals("", getFormObject(context).getProp()); Errors errors = getErrors(context); // the setupForm() should leave the form object and error info setup by // the // bind & validate untouched assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); assertEquals(2, context.getRequestScope().size()); assertEquals(2, context.getFlowScope().size()); assertSame(errors, getErrors(context)); assertSame(formObject, getFormObject(context)); assertTrue(getErrors(context).hasErrors()); assertEquals("", getFormObject(context).getProp()); } public void testMultipleFormObjectsInOneFlow() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); FormAction otherAction = createFormAction("otherTest"); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), otherAction.setupForm(context).getId()); assertEquals(3, context.getRequestScope().size()); assertEquals(3, context.getFlowScope().size()); assertNotSame(getErrors(context), getErrors(context, "otherTest")); assertNotSame(getFormObject(context), getFormObject(context, "otherTest")); assertFalse(getErrors(context).hasErrors()); assertFalse(getErrors(context, "otherTest").hasErrors()); assertNull(getFormObject(context).getProp()); assertNull(getFormObject(context, "otherTest").getProp()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.bindAndValidate(context).getId()); assertEquals(3, context.getRequestScope().size()); assertEquals(3, context.getFlowScope().size()); assertNotSame(getErrors(context), getErrors(context, "otherTest")); assertNotSame(getFormObject(context), getFormObject(context, "otherTest")); assertFalse(getErrors(context).hasErrors()); assertFalse(getErrors(context, "otherTest").hasErrors()); assertEquals("value", getFormObject(context).getProp()); assertNull(getFormObject(context, "otherTest").getProp()); context.setExternalContext(new MockExternalContext(blankParameters())); assertEquals(action.getEventFactorySupport().getErrorEventId(), otherAction.bindAndValidate(context).getId()); assertEquals(3, context.getRequestScope().size()); assertEquals(3, context.getFlowScope().size()); assertNotSame(getErrors(context), getErrors(context, "otherTest")); assertNotSame(getFormObject(context), getFormObject(context, "otherTest")); assertFalse(getErrors(context).hasErrors()); assertTrue(getErrors(context, "otherTest").hasErrors()); assertEquals("value", getFormObject(context).getProp()); assertEquals("", getFormObject(context, "otherTest").getProp()); } public void testGetFormObject() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); FormAction action = createFormAction("test"); TestBean formObject = (TestBean) action.getFormObject(context); assertNotNull(formObject); formObject = new TestBean(); TestBean testBean = formObject; new FormObjectAccessor(context).putFormObject(formObject, action.getFormObjectName(), action.getFormObjectScope()); formObject = (TestBean) action.getFormObject(context); assertSame(formObject, testBean); } public void testGetFormErrors() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); FormAction action = createFormAction("test"); action.setupForm(context); Errors errors = action.getFormErrors(context); assertNotNull(errors); assertTrue(!errors.hasErrors()); errors = new BindException(getFormObject(context), "test"); Errors testErrors = errors; new FormObjectAccessor(context).putFormErrors(errors, action.getFormErrorsScope()); errors = action.getFormErrors(context); assertSame(errors, testErrors); } public void testFormObjectAccessUsingAlias() throws Exception { MockRequestContext context = new MockRequestContext(blankParameters()); FormAction otherAction = createFormAction("otherTest"); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); assertSame(getFormObject(context), new FormObjectAccessor(context).getCurrentFormObject()); assertSame(getErrors(context), new FormObjectAccessor(context).getCurrentFormErrors()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), otherAction.setupForm(context).getId()); assertSame(getFormObject(context, "otherTest"), new FormObjectAccessor(context).getCurrentFormObject()); assertSame(getErrors(context, "otherTest"), new FormObjectAccessor(context).getCurrentFormErrors()); assertEquals(action.getEventFactorySupport().getErrorEventId(), action.bindAndValidate(context).getId()); assertSame(getFormObject(context), new FormObjectAccessor(context).getCurrentFormObject()); assertSame(getErrors(context), new FormObjectAccessor(context).getCurrentFormErrors()); context.setExternalContext(new MockExternalContext(parameters())); assertEquals(action.getEventFactorySupport().getSuccessEventId(), otherAction.bindAndValidate(context).getId()); assertSame(getFormObject(context, "otherTest"), new FormObjectAccessor(context).getCurrentFormObject()); assertSame(getErrors(context, "otherTest"), new FormObjectAccessor(context).getCurrentFormErrors()); } // as reported in SWF-4 public void testInconsistentFormObjectAndErrors() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); assertEquals(action.getEventFactorySupport().getSuccessEventId(), action.setupForm(context).getId()); Object formObject = getFormObject(context); BindingResult errors = (BindingResult) getErrors(context); assertTrue(formObject instanceof TestBean); assertTrue(errors.getTarget() instanceof TestBean); assertSame(formObject, errors.getTarget()); context = new MockRequestContext(); context.setCurrentEvent(new Event(this, "start")); OtherTestBean freshBean = new OtherTestBean(); context.getFlowScope().put("test", freshBean); context.getRequestScope().put(BindingResult.MODEL_KEY_PREFIX + "test", errors); FormAction otherAction = createFormAction("test"); otherAction.setFormObjectClass(OtherTestBean.class); assertEquals(action.getEventFactorySupport().getSuccessEventId(), otherAction.setupForm(context).getId()); formObject = context.getFlowScope().get("test"); errors = (BindingResult) getErrors(context); assertTrue(formObject instanceof OtherTestBean); assertSame(freshBean, formObject); assertTrue("Expected OtherTestBean, but was " + errors.getTarget().getClass(), errors.getTarget() instanceof OtherTestBean); assertSame(formObject, errors.getTarget()); } public void testMultipleFormObjects() throws Exception { MockRequestContext context = new MockRequestContext(parameters()); FormAction action1 = createFormAction("test1"); action1.setupForm(context); TestBean test1 = (TestBean) context.getFlowScope().get("test1"); assertNotNull(test1); assertSame(test1, new FormObjectAccessor(context).getCurrentFormObject()); FormAction action2 = createFormAction("test2"); action2.setupForm(context); TestBean test2 = (TestBean) context.getFlowScope().get("test2"); assertNotNull(test2); assertSame(test2, new FormObjectAccessor(context).getCurrentFormObject()); MockParameterMap parameters = new MockParameterMap(); parameters.put("prop", "12345"); context.setExternalContext(new MockExternalContext(parameters)); action1.bindAndValidate(context); TestBean test11 = (TestBean) context.getFlowScope().get("test1"); assertSame(test1, test11); assertEquals("12345", test1.getProp()); assertSame(test1, new FormObjectAccessor(context).getCurrentFormObject()); parameters = new MockParameterMap(); parameters.put("prop", "123456"); context.setExternalContext(new MockExternalContext(parameters)); action2.bindAndValidate(context); TestBean test22 = (TestBean) context.getFlowScope().get("test2"); assertSame(test22, test2); assertEquals("123456", test2.getProp()); assertSame(test2, new FormObjectAccessor(context).getCurrentFormObject()); } public void testFormObjectAndNoErrors() throws Exception { // this typically happens with mapping from parent flow to subflow MockRequestContext context = new MockRequestContext(parameters()); TestBean testBean = new TestBean(); testBean.setProp("bla"); context.getFlowScope().put("test", testBean); action.setupForm(context); // should have created a new empty errors instance, but left the form // object alone // since we didn't to bindOnSetupForm assertSame(testBean, getFormObject(context)); assertEquals("bla", getFormObject(context).getProp()); assertNotNull(getErrors(context)); assertSame(testBean, ((BindingResult) getErrors(context)).getTarget()); assertFalse(getErrors(context).hasErrors()); } public void testSetupFormThenBindAndValidate() throws Exception { FormAction action = createFormAction("testBean"); MockRequestContext context = new MockRequestContext(); Event result = action.setupForm(context); assertEquals("success", result.getId()); Object formObject = action.getFormObject(context); assertSame(formObject, action.getFormObject(context)); assertTrue(formObject instanceof TestBean); context.putRequestParameter("prop", "foo"); context.getAttributeMap().put("validatorMethod", "validateTestBean"); result = action.bindAndValidate(context); assertEquals("success", result.getId()); assertSame(formObject, action.getFormObject(context)); assertEquals(true, ((TestBeanValidator) action.getValidator()).invoked); } public void testFormActionWithValidatorAndNoFormActionClass() throws Exception { FormAction action = new FormAction() { protected Object createFormObject(RequestContext context) throws Exception { return new TestBean(); } }; action.setValidator(new TestBeanValidator()); action.initAction(); MockRequestContext context = new MockRequestContext(); Event result = action.setupForm(context); assertEquals("success", result.getId()); context.putRequestParameter("prop", "foo"); context.getAttributeMap().put("validatorMethod", "validateTestBean"); result = action.bindAndValidate(context); } // helpers private FormAction createFormAction(String formObjectName) { FormAction res = new FormAction(); res.setFormObjectName(formObjectName); res.setFormObjectClass(TestBean.class); res.setValidator(new TestBeanValidator()); res.setFormObjectScope(ScopeType.FLOW); res.setFormErrorsScope(ScopeType.REQUEST); res.initAction(); return res; } private Errors getErrors(RequestContext context) { return getErrors(context, "test"); } private Errors getErrors(RequestContext context, String formObjectName) { return new FormObjectAccessor(context).getFormErrors(formObjectName, ScopeType.REQUEST); } private TestBean getFormObject(RequestContext context) { return getFormObject(context, "test"); } private TestBean getFormObject(RequestContext context, String formObjectName) { return (TestBean) context.getFlowScope().get(formObjectName); } }