1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.Framework
.Tests
.Helpers
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Collections
.Specialized
;
21 using System
.Globalization
;
23 using System
.Reflection
;
24 using System
.Threading
;
25 using Castle
.Core
.Interceptor
;
26 using Castle
.DynamicProxy
;
27 using Castle
.MonoRail
.Framework
.Helpers
;
28 using Castle
.MonoRail
.Framework
.Tests
.Controllers
;
29 using NUnit
.Framework
;
32 public class FormHelperTestCase
34 private FormHelper helper
;
35 private Product product
;
36 private SimpleUser user
;
37 private Subscription subscription
;
38 private Month
[] months
;
43 CultureInfo en
= CultureInfo
.CreateSpecificCulture("en");
45 Thread
.CurrentThread
.CurrentCulture
= en
;
46 Thread
.CurrentThread
.CurrentUICulture
= en
;
48 helper
= new FormHelper();
50 subscription
= new Subscription();
51 months
= new Month
[] { new Month(1, "January"), new Month(1, "February") }
;
52 product
= new Product("memory card", 10, (decimal)12.30);
54 user
= new SimpleUser();
56 HomeController controller
= new HomeController();
57 ControllerContext context
= new ControllerContext();
59 context
.PropertyBag
.Add("product", product
);
60 context
.PropertyBag
.Add("user", user
);
61 context
.PropertyBag
.Add("roles", new Role
[] { new Role(1, "a"), new Role(2, "b"), new Role(3, "c") }
);
62 context
.PropertyBag
.Add("sendemail", true);
63 context
.PropertyBag
.Add("sendemailstringtrue", "true");
64 context
.PropertyBag
.Add("sendemailstringfalse", "false");
65 context
.PropertyBag
.Add("confirmation", "abc");
66 context
.PropertyBag
.Add("fileaccess", FileAccess
.Read
);
67 context
.PropertyBag
.Add("subscription", subscription
);
68 context
.PropertyBag
.Add("months", months
);
70 helper
.SetController(controller
, context
);
74 public void FormTagDoesNotMixUrlParametersWithFormElementParameters()
76 // No solution here. Id is ambiguous
77 // helper.FormTag(DictHelper.Create("noaction=true"));
81 public void OverridingElementId()
83 Assert
.AreEqual("<input type=\"text\" id=\"something\" name=\"product.name\" value=\"memory card\" />",
84 helper
.TextField("product.name", DictHelper
.Create("id=something")));
86 Assert
.AreEqual("<input type=\"password\" id=\"something\" name=\"product.name\" value=\"memory card\" />",
87 helper
.PasswordField("product.name", DictHelper
.Create("id=something")));
89 Assert
.AreEqual("<input type=\"hidden\" id=\"something\" name=\"product.name\" value=\"memory card\" />",
90 helper
.HiddenField("product.name", DictHelper
.Create("id=something")));
92 product
.IsAvailable
= false;
94 Assert
.AreEqual("<input type=\"checkbox\" id=\"something\" name=\"product.isavailable\" value=\"true\" />" +
95 "<input type=\"hidden\" id=\"somethingH\" name=\"product.isavailable\" value=\"false\" />",
96 helper
.CheckboxField("product.isavailable", DictHelper
.Create("id=something")));
101 "<input type=\"radio\" id=\"something\" name=\"user.isactive\" value=\"True\" checked=\"checked\" />",
102 helper
.RadioField("user.isactive", true, DictHelper
.Create("id=something")));
104 Assert
.AreEqual("<label for=\"something\">Name:</label>",
105 helper
.LabelFor("product.name", "Name:", DictHelper
.Create("id=something")));
107 ArrayList list
= new ArrayList();
112 Assert
.AreEqual("<select id=\"something\" name=\"product.category.id\" >" + Environment
.NewLine
+
113 "<option value=\"cat1\">cat1</option>" + Environment
.NewLine
+ "<option value=\"cat2\">cat2</option>" +
114 Environment
.NewLine
+ "</select>",
115 helper
.Select("product.category.id", list
, DictHelper
.Create("id=something")));
119 public void TextField()
121 Assert
.AreEqual("<input type=\"text\" id=\"product_name\" name=\"product.name\" value=\"memory card\" />",
122 helper
.TextField("product.name"));
123 Assert
.AreEqual("<input type=\"text\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" />",
124 helper
.TextField("product.quantity"));
128 public void TextFieldWithAutoCompletion()
130 Assert
.AreEqual("<input type=\"text\" id=\"product_name\" name=\"product.name\" value=\"memory card\" autocomplete=\"off\" /><div id=\"product_nameautocomplete\" class=\"auto_complete\"></div><script type=\"text/javascript\">new Ajax.Autocompleter('product_name', 'product_nameautocomplete', 'someurl',{})</script>",
131 helper
.TextFieldAutoComplete("product.name", "someurl", DictHelper
.Create(), DictHelper
.Create()));
132 Assert
.AreEqual("<input type=\"text\" id=\"product_name\" name=\"product.name\" value=\"memory card\" autocomplete=\"on\" /><div id=\"product_nameautocomplete\" class=\"auto_complete\"></div><script type=\"text/javascript\">new Ajax.Autocompleter('product_name', 'product_nameautocomplete', 'someurl',{})</script>",
133 helper
.TextFieldAutoComplete("product.name", "someurl", DictHelper
.Create("autocomplete=on"), DictHelper
.Create()));
134 Assert
.IsTrue(helper
.TextFieldAutoComplete("product.name", "someurl", DictHelper
.Create(), DictHelper
.Create()).Contains(helper
.TextField("product.name", DictHelper
.Create("autocomplete=off"))));
138 public void NumberField()
141 "<input type=\"text\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" onkeypress=\"return monorail_formhelper_numberonly(event, [], []);\" />",
142 helper
.NumberField("product.quantity"));
144 "<input type=\"text\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" onkeypress=\"return monorail_formhelper_numberonly(event, [1], []);\" />",
145 helper
.NumberField("product.quantity", DictHelper
.Create("exceptions=1")));
147 "<input type=\"text\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" onkeypress=\"return monorail_formhelper_numberonly(event, [1,2], []);\" />",
148 helper
.NumberField("product.quantity", DictHelper
.Create("exceptions=1,2")));
152 public void MaskedNumberField()
155 "<input type=\"text\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" onkeypress=\"return monorail_formhelper_numberonly(event, [], []);\" " +
156 "onblur=\"javascript:void(0);return monorail_formhelper_mask(event,this,'2,5','-');\" onkeyup=\"javascript:void(0);return monorail_formhelper_mask(event,this,'2,5','-');\" />",
157 helper
.NumberField("product.quantity", DictHelper
.Create("mask=2,5")));
161 public void PasswordField()
163 Assert
.AreEqual("<input type=\"password\" id=\"product_name\" name=\"product.name\" value=\"memory card\" />",
164 helper
.PasswordField("product.name"));
165 Assert
.AreEqual("<input type=\"password\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" />",
166 helper
.PasswordField("product.quantity"));
167 Assert
.AreEqual("<input type=\"password\" id=\"confirmation\" name=\"confirmation\" value=\"abc\" />",
168 helper
.PasswordField("confirmation"));
172 public void TextFieldValue()
174 Assert
.AreEqual("<input type=\"text\" id=\"product_price\" name=\"product.price\" value=\"$12.30\" />",
175 helper
.TextFieldValue("product.price", product
.Price
.ToString("C")));
179 public void TextFieldFormat()
181 Assert
.AreEqual("<input type=\"text\" id=\"product_price\" name=\"product.price\" value=\"$12.30\" />",
182 helper
.TextField("product.price", DictHelper
.Create("textformat=C")));
186 public void HiddenField()
188 Assert
.AreEqual("<input type=\"hidden\" id=\"product_name\" name=\"product.name\" value=\"memory card\" />",
189 helper
.HiddenField("product.name"));
190 Assert
.AreEqual("<input type=\"hidden\" id=\"product_quantity\" name=\"product.quantity\" value=\"10\" />",
191 helper
.HiddenField("product.quantity"));
195 public void CheckboxField()
197 product
.IsAvailable
= false;
200 "<input type=\"checkbox\" id=\"product_isavailable\" name=\"product.isavailable\" value=\"true\" />" +
201 "<input type=\"hidden\" id=\"product_isavailableH\" name=\"product.isavailable\" value=\"false\" />",
202 helper
.CheckboxField("product.isavailable"));
204 product
.IsAvailable
= true;
207 "<input type=\"checkbox\" id=\"product_isavailable\" name=\"product.isavailable\" value=\"true\" checked=\"checked\" />" +
208 "<input type=\"hidden\" id=\"product_isavailableH\" name=\"product.isavailable\" value=\"false\" />",
209 helper
.CheckboxField("product.isavailable"));
212 "<input type=\"checkbox\" id=\"sendemail\" name=\"sendemail\" value=\"true\" checked=\"checked\" />" +
213 "<input type=\"hidden\" id=\"sendemailH\" name=\"sendemail\" value=\"false\" />",
214 helper
.CheckboxField("sendemail"));
217 "<input type=\"checkbox\" id=\"sendemail\" name=\"sendemail\" value=\"true\" checked=\"checked\" />" +
218 "<input type=\"hidden\" id=\"sendemailH\" name=\"sendemail\" value=\"0\" />",
219 helper
.CheckboxField("sendemail", new DictHelper().CreateDict("falseValue=0")));
221 // Checkbox values are actually added as string values to the Flash
222 // NameValueCollection of the Controller. Therefore a string "false"
223 // must render an unchecked checkbox.
224 Assert
.AreEqual("<input type=\"checkbox\" id=\"sendemailstringtrue\" name=\"sendemailstringtrue\" value=\"true\" checked=\"checked\" />" +
225 "<input type=\"hidden\" id=\"sendemailstringtrueH\" name=\"sendemailstringtrue\" value=\"false\" />",
226 helper
.CheckboxField("sendemailstringtrue"));
228 Assert
.AreEqual("<input type=\"checkbox\" id=\"sendemailstringfalse\" name=\"sendemailstringfalse\" value=\"true\" />" +
229 "<input type=\"hidden\" id=\"sendemailstringfalseH\" name=\"sendemailstringfalse\" value=\"false\" />",
230 helper
.CheckboxField("sendemailstringfalse"));
235 public void RadioField()
237 user
.IsActive
= true;
240 "<input type=\"radio\" id=\"user_isactive\" name=\"user.isactive\" value=\"True\" checked=\"checked\" />",
241 helper
.RadioField("user.isactive", true));
243 user
.IsActive
= false;
245 Assert
.AreEqual("<input type=\"radio\" id=\"user_isactive\" name=\"user.isactive\" value=\"True\" />",
246 helper
.RadioField("user.isactive", true));
250 public void RadioFieldWithEnums()
252 Assert
.AreEqual("<input type=\"radio\" id=\"fileaccess\" name=\"fileaccess\" value=\"Read\" checked=\"checked\" />",
253 helper
.RadioField("fileaccess", FileAccess
.Read
));
255 Assert
.AreEqual("<input type=\"radio\" id=\"fileaccess\" name=\"fileaccess\" value=\"Read\" checked=\"checked\" />",
256 helper
.RadioField("fileaccess", "Read"));
258 Assert
.AreEqual("<input type=\"radio\" id=\"fileaccess\" name=\"fileaccess\" value=\"Write\" />",
259 helper
.RadioField("fileaccess", FileAccess
.Write
));
261 Assert
.AreEqual("<input type=\"radio\" id=\"fileaccess\" name=\"fileaccess\" value=\"Write\" />",
262 helper
.RadioField("fileaccess", "Write"));
266 public void LabelFor()
268 Assert
.AreEqual("<label for=\"product_name\">Name:</label>",
269 helper
.LabelFor("product.name", "Name:"));
273 public void LabelForAttributed()
275 IDictionary attrs
= new ListDictionary();
276 attrs
.Add("class", "cssclass");
277 Assert
.AreEqual("<label for=\"product_name\" class=\"cssclass\" >Name:</label>",
278 helper
.LabelFor("product.name", "Name:", attrs
));
282 public void TextFieldWithIndex()
284 Assert
.AreEqual("<input type=\"text\" id=\"roles_0_Id\" name=\"roles[0].Id\" value=\"1\" />",
285 helper
.TextField("roles[0].Id"));
287 Assert
.AreEqual("<input type=\"text\" id=\"roles_1_Name\" name=\"roles[1].Name\" value=\"b\" />",
288 helper
.TextField("roles[1].Name"));
292 public void TextFieldWithNestedIndex()
294 user
.Roles
.Add(new Role(1, "role1"));
295 user
.Roles
.Add(new Role(2, "role2"));
297 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_0_Id\" name=\"user.roles[0].Id\" value=\"1\" />",
298 helper
.TextField("user.roles[0].Id"));
300 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_0_Name\" name=\"user.roles[0].Name\" value=\"role1\" />",
301 helper
.TextField("user.roles[0].Name"));
303 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_1_Name\" name=\"user.roles[1].Name\" value=\"role2\" />",
304 helper
.TextField("user.roles[1].Name"));
308 public void TextFieldWithNestedIndexAndNullValues1()
312 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_0_Id\" name=\"user.roles[0].Id\" value=\"\" />",
313 helper
.TextField("user.roles[0].Id"));
315 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_0_Name\" name=\"user.roles[0].Name\" value=\"\" />",
316 helper
.TextField("user.roles[0].Name"));
318 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_1_Name\" name=\"user.roles[1].Name\" value=\"\" />",
319 helper
.TextField("user.roles[1].Name"));
323 public void TextFieldWithNestedIndexAndNullValues2()
325 user
.Roles
.Add(new Role(1, null));
326 user
.Roles
.Add(new Role(2, null));
328 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_0_Name\" name=\"user.roles[0].Name\" value=\"\" />",
329 helper
.TextField("user.roles[0].Name"));
331 Assert
.AreEqual("<input type=\"text\" id=\"user_roles_1_Name\" name=\"user.roles[1].Name\" value=\"\" />",
332 helper
.TextField("user.roles[1].Name"));
336 public void IndexedValueTextFieldInDotNet2()
338 subscription
.Months4
.Clear();
339 subscription
.Months4
.Add(new Month(1, "Jan"));
340 subscription
.Months4
.Add(new Month(2, "Feb"));
343 "<input type=\"text\" id=\"subscription_months4_0_id\" name=\"subscription.months4[0].id\" value=\"1\" />",
344 helper
.TextField("subscription.months4[0].id"));
346 "<input type=\"text\" id=\"subscription_months4_0_name\" name=\"subscription.months4[0].name\" value=\"Jan\" />",
347 helper
.TextField("subscription.months4[0].name"));
350 [Test
, ExpectedException(typeof(MonoRailException
))]
351 public void InvalidIndex1()
353 helper
.TextField("roles[a].Id");
356 [Test
, ExpectedException(typeof(MonoRailException
))]
357 public void InvalidIndex2()
359 helper
.TextField("roles[1a].Id");
362 [Test
, ExpectedException(typeof(MonoRailException
)), Ignore("The behavior for array access has changed")]
363 public void InvalidIndex3()
365 helper
.TextField("roles[10].Id");
369 public void ShouldDiscoverRootTypeOnCollectionWhenIndexedRootInPropertyBag()
371 FormHelperEx sut
= new FormHelperEx();
372 sut
.SetController(new HomeController(), new ControllerContext());
374 //this is what current code requires to discover
375 sut
.ControllerContext
.PropertyBag
["items[0]type"] = typeof(Product
);
376 PropertyInfo prop
= sut
.ObtainTargetProperty(RequestContext
.PropertyBag
, "items[0].Name");
377 PropertyInfo expect
= typeof(Product
).GetProperty("Name");
378 Assert
.AreEqual(expect
, prop
);
382 public void ShouldDiscoverRootTypeOnCollection()
384 FormHelperEx sut
= new FormHelperEx();
385 sut
.SetController(new HomeController(), new ControllerContext());
387 sut
.ControllerContext
.PropertyBag
["itemstype"] = typeof(Product
); //no need to pass indexer
388 PropertyInfo prop
= sut
.ObtainTargetProperty(RequestContext
.PropertyBag
, "items[0].Name");
389 PropertyInfo expect
= typeof(Product
).GetProperty("Name");
390 Assert
.AreEqual(expect
, prop
);
394 public void ShouldFailToDiscoverRootTypeOnCollectionWhenNoTypeInPropertyBag()
396 FormHelperEx sut
= new FormHelperEx();
397 sut
.SetController(new HomeController(), new ControllerContext());
399 PropertyInfo prop
= sut
.ObtainTargetProperty(RequestContext
.PropertyBag
, "items[0].Name");
404 public void TargetValueCanBeObtainedForOverridenProperties()
406 helper
.ControllerContext
.PropertyBag
["december"] = new December();
407 helper
.TextField("december.Name");
411 // public void TargetValueCanBeObtainedForOverridenProxiedProperties()
413 // ProxyGenerator generator = new ProxyGenerator();
414 // object proxy = generator.CreateClassProxy(typeof(Month), new StandardInterceptor(), 12, "December");
415 // helper.ControllerContext.PropertyBag["december"] = proxy;
416 // helper.TextField("december.Name");
420 public void TargetValueCanBeObtainedForOverridenGenericProperties()
422 ClassThatOverridesGenericProperty mr424
= new ClassThatOverridesGenericProperty();
423 helper
.ControllerContext
.PropertyBag
.Add("mr424", mr424
);
426 Assert
.AreEqual("<input type=\"text\" id=\"mr424_prop\" name=\"mr424.prop\" value=\"(unknown)\" />",
427 helper
.TextField("mr424.prop"));
429 mr424
.Prop
= "propvalue";
430 Assert
.AreEqual("<input type=\"text\" id=\"mr424_prop\" name=\"mr424.prop\" value=\"propvalue\" />",
431 helper
.TextField("mr424.prop"));
434 public class FormHelperEx
: FormHelper
436 public PropertyInfo
ObtainTargetProperty(RequestContext context
, string target
)
438 return ObtainTargetProperty(context
, target
, null);
444 #region Classes skeletons
446 public class BaseClassWithGenericProperty
<T
>
450 public virtual T Prop
453 set { prop = value; }
457 public class ClassThatOverridesGenericProperty
: BaseClassWithGenericProperty
<string>
459 public override string Prop
461 get { return base.Prop ?? "(unknown)"; }
462 set { base.Prop = value; }
470 public Month(int id
, string name
)
476 public virtual int Id
482 public virtual string Name
485 set { name = value; }
489 // public class NullInterceptor : IInterceptor
491 // public object Intercept(IInvocation invocation, params object[] args)
493 // return invocation.Proceed(args);
497 public class December
: Month
499 public December() : base(12, "December")
503 public override string Name
505 get { return base.Name; }
506 set { throw new InvalidOperationException(); }
510 public class Subscription
512 private int[] months
;
513 private IList months2
= new ArrayList();
514 private Month
[] months3
;
515 private IList
<Month
> months4
= new CustomList
<Month
>();
519 get { return months; }
520 set { months = value; }
525 get { return months2; }
526 set { months2 = value; }
529 public Month
[] Months3
531 get { return months3; }
532 set { months3 = value; }
535 public IList
<Month
> Months4
537 get { return months4; }
538 set { months4 = value; }
545 private int quantity
;
546 private bool isAvailable
;
547 private decimal price
;
548 private ProductCategory category
= new ProductCategory();
554 public Product(string name
, int quantity
, decimal price
)
557 this.quantity
= quantity
;
561 public virtual string Name
564 set { name = value; }
567 public virtual int Quantity
569 get { return quantity; }
570 set { quantity = value; }
573 public virtual decimal Price
575 get { return price; }
576 set { price = value; }
579 public virtual bool IsAvailable
581 get { return isAvailable; }
582 set { isAvailable = value; }
585 public virtual ProductCategory Category
587 get { return category; }
588 set { category = value; }
592 public class ProductCategory
597 public ProductCategory()
601 public ProductCategory(int id
, String name
)
607 public virtual int Id
613 public virtual String Name
616 set { name = value; }
625 public Role(int id
, string name
)
631 public virtual int Id
637 public virtual String Name
640 set { name = value; }
643 public override bool Equals(object obj
)
645 Role other
= obj
as Role
;
649 return other
.Id
== Id
;
655 public override int GetHashCode()
660 public override string ToString()
668 private int identification
;
671 public Role2(int id
, string name
)
677 public virtual int Identification
679 get { return identification; }
680 set { identification = value; }
683 public virtual String Name
686 set { name = value; }
690 public class SimpleUser
692 public enum RegistrationEnum
701 private ArrayList roles
= new ArrayList();
702 private bool isActive
;
703 private RegistrationEnum registration
= RegistrationEnum
.registered
;
709 public SimpleUser(int id
, bool isActive
)
712 this.isActive
= isActive
;
723 get { return isActive; }
724 set { isActive = value; }
730 set { name = value; }
733 public RegistrationEnum Registration
735 get { return registration; }
736 set { registration = value; }
739 public Role
[] RolesAsArray
741 get { return (Role[])roles.ToArray(typeof(Role)); }
744 public ArrayList Roles
746 get { return roles; }
747 set { roles = value; }
753 private Month dobMonth
;
755 public virtual Month DobMonth
757 get { return dobMonth; }
758 set { dobMonth = value; }
762 public class CustomList
<T
> : IList
<T
>
764 readonly List
<T
> innerList
= new List
<T
>();
766 public int IndexOf(T item
)
768 return innerList
.IndexOf(item
);
771 public void Insert(int index
, T item
)
773 innerList
.Insert(index
, item
);
776 public void RemoveAt(int index
)
778 innerList
.RemoveAt(index
);
781 public T
this[int index
]
783 get { return innerList[index]; }
784 set { innerList[index] = value; }
787 public void Add(T item
)
797 public bool Contains(T item
)
799 return innerList
.Contains(item
);
802 public void CopyTo(T
[] array
, int arrayIndex
)
804 throw new NotImplementedException();
807 public bool Remove(T item
)
809 throw new NotImplementedException();
814 get { return innerList.Count; }
817 public bool IsReadOnly
819 get { throw new NotImplementedException(); }
822 IEnumerator
<T
> IEnumerable
<T
>.GetEnumerator()
824 throw new NotImplementedException();
827 public IEnumerator
GetEnumerator()
829 return innerList
.GetEnumerator();
833 public interface IInterfacedList
836 string Name { get; set; }
839 public class InterfacedClassA
: IInterfacedList
844 public InterfacedClassA(int id
, string name
)
850 #region IInterfacedList Members
861 set { name = value; }
867 public class InterfacedClassB
: IInterfacedList
872 public InterfacedClassB(int id
, string name
)
878 #region IInterfacedList Members
889 set { name = value; }
911 public class ClassWithCompositKey
916 public ClassWithCompositKey(int id
, string name
)
925 set { name = value; }