001 // Copyright 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.form; 016 017 import org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.hivemind.Location; 019 import org.apache.tapestry.*; 020 import org.apache.tapestry.engine.ILink; 021 import org.apache.tapestry.event.BrowserEvent; 022 import org.apache.tapestry.event.EventTarget; 023 import org.apache.tapestry.internal.event.impl.ComponentEventInvoker; 024 import org.apache.tapestry.listener.ListenerInvoker; 025 import org.apache.tapestry.services.ResponseBuilder; 026 import org.apache.tapestry.util.IdAllocator; 027 import org.apache.tapestry.valid.IValidationDelegate; 028 import org.easymock.EasyMock; 029 import static org.easymock.EasyMock.*; 030 import org.easymock.IAnswer; 031 import org.testng.annotations.Test; 032 033 import java.util.HashMap; 034 import java.util.Map; 035 036 /** 037 * Tests for {@link org.apache.tapestry.form.FormSupportImpl}. 038 * 039 * @author Howard M. Lewis Ship 040 * @since 4.0 041 */ 042 @Test(sequential = true) 043 public class FormSupportTest extends BaseComponentTestCase { 044 045 protected FormSupport newFormSupport(IMarkupWriter writer, IRequestCycle cycle, IForm form) 046 { 047 return new FormSupportImpl(writer, cycle, form); 048 } 049 050 private IRender newComponentRenderBody(final FormSupport fs, final IFormComponent component, 051 final IMarkupWriter nested) 052 { 053 return newComponentsRenderBody(fs, new IFormComponent[] {component}, nested); 054 } 055 056 private IRender newComponentsRenderBody(final FormSupport fs, final IFormComponent[] component, 057 final IMarkupWriter nested) 058 { 059 return new IRender() { 060 public void render(IMarkupWriter writer, IRequestCycle cycle) 061 { 062 assertEquals(nested, writer); 063 064 for (int i = 0; i < component.length; i++) 065 fs.getElementId(component[i]); 066 } 067 }; 068 } 069 070 private IValidationDelegate newDelegate() 071 { 072 return newMock(IValidationDelegate.class); 073 } 074 075 protected IEngine newEngine() 076 { 077 return newMock(IEngine.class); 078 } 079 080 private IFormComponent newField() 081 { 082 return newMock(IFormComponent.class); 083 } 084 085 private IFormComponent newFormComponent(String id, String name) 086 { 087 IFormComponent component = newMock(IFormComponent.class); 088 checkOrder(component, false); 089 090 expect(component.getSpecifiedId()).andReturn(id); 091 092 component.setName(name); 093 component.setClientId(name); 094 095 return component; 096 } 097 098 private IFormComponent newFormComponent(String id, String extendedId, Location location) 099 { 100 IFormComponent component = newMock(IFormComponent.class); 101 102 expect(component.getSpecifiedId()).andReturn(id); 103 104 trainGetExtendedId(component, extendedId); 105 trainGetLocation(component, location); 106 107 return component; 108 } 109 110 protected void trainCycleSeedEncoding(IRequestCycle cycle) 111 { 112 expect(cycle.encodeIdState()).andReturn("ENCODED").anyTimes(); 113 114 expect(cycle.getUniqueId(isA(String.class))).andAnswer(new UniqueIdAnswer()).anyTimes(); 115 } 116 117 public class UniqueIdAnswer implements IAnswer<String> { 118 119 IdAllocator _allocator = new IdAllocator(); 120 121 public String answer() 122 throws Throwable 123 { 124 return _allocator.allocateId((String) EasyMock.getCurrentArguments()[0]); 125 } 126 } 127 128 public void test_Cancel_Rewind() 129 { 130 IMarkupWriter writer = newWriter(); 131 IRequestCycle cycle = newCycle(); 132 IValidationDelegate delegate = newDelegate(); 133 MockForm form = new MockForm(delegate); 134 135 trainIsRewound(cycle, form, true); 136 137 trainGetPageRenderSupport(cycle, null); 138 139 replay(); 140 141 final FormSupport fs = newFormSupport(writer, cycle, form); 142 143 verify(); 144 145 delegate.clear(); 146 147 trainGetParameter(cycle, FormSupportImpl.SUBMIT_MODE, "cancel"); 148 149 // Create a body, just to provie it doesn't get invoked. 150 151 IRender body = newMock(IRender.class); 152 153 form.setBody(body); 154 155 replay(); 156 157 assertEquals(FormConstants.SUBMIT_CANCEL, fs.rewind()); 158 159 verify(); 160 } 161 162 public void test_Complex_Render() 163 { 164 IMarkupWriter writer = newWriter(); 165 NestedMarkupWriter nested = newNestedWriter(); 166 IRequestCycle cycle = newCycle(); 167 ResponseBuilder builder = newMock(ResponseBuilder.class); 168 IValidationDelegate delegate = newDelegate(); 169 ILink link = newLink(); 170 IRender render = newRender(); 171 MockForm form = new MockForm(delegate); 172 173 trainIsRewound(cycle, form, false); 174 175 PageRenderSupport support = newPageRenderSupport(); 176 177 trainGetPageRenderSupport(cycle, support); 178 179 replay(); 180 181 final FormSupport fs = newFormSupport(writer, cycle, form); 182 183 verify(); 184 185 trainCycleSeedEncoding(cycle); 186 187 final IFormComponent barney1 = newFormComponent("barney", "barney"); 188 final IFormComponent wilma = newFormComponent("wilma", "wilma"); 189 final IFormComponent barney2 = newFormComponent("barney", "barney_0"); 190 191 IRender body = newComponentsRenderBody(fs, new IFormComponent[] {barney1, wilma, barney2}, nested); 192 193 form.setBody(body); 194 195 trainRegister(support, form, "myform"); 196 197 trainGetParameterNames(link, new String[] {"service"}); 198 trainGetParameterValues(link, "service", new String[] {"fred"}); 199 200 trainGetNestedWriter(writer, nested); 201 202 trainGetURL(link, null, "/app"); 203 204 writer.begin("form"); 205 writer.attribute("method", "post"); 206 writer.attribute("action", "/app"); 207 208 writer.attribute("id", "myform"); 209 210 render.render(writer, cycle); 211 212 writer.println(); 213 214 trainHiddenBlock(cycle, builder, writer, form, "fred", "barney,wilma,barney_0"); 215 216 nested.close(); 217 218 writer.end(); 219 220 trainGetFocusField(delegate, "wilma"); 221 222 expect(cycle.isFocusDisabled()).andReturn(false); 223 224 // effectively means someone else has already claimed focus 225 226 trainGetFieldFocus(cycle, null); 227 228 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 229 support.addScriptAfterInitialization(form, "tapestry.form.focusField('wilma');"); 230 cycle.setAttribute(FormSupportImpl.FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE); 231 232 replay(); 233 234 fs.render("post", render, link, null, null); 235 236 verify(); 237 } 238 239 public void test_Complex_Rewind() 240 { 241 IMarkupWriter writer = newWriter(); 242 IRequestCycle cycle = newCycle(); 243 IValidationDelegate delegate = newDelegate(); 244 MockForm form = new MockForm(delegate); 245 ListenerInvoker listenerInvoker = newMock(ListenerInvoker.class); 246 247 ComponentEventInvoker invoker = new ComponentEventInvoker(); 248 invoker.setInvoker(listenerInvoker); 249 250 trainIsRewound(cycle, form, true); 251 trainGetPageRenderSupport(cycle, null); 252 253 replay(); 254 255 final FormSupport fs = newFormSupport(writer, cycle, form); 256 257 verify(); 258 259 delegate.clear(); 260 261 trainCycleForRewind(cycle, "barney,wilma,barney_0", null); 262 trainCycleSeedEncoding(cycle); 263 264 final IFormComponent barney1 = newFormComponent("barney", "barney"); 265 final IFormComponent wilma = newFormComponent("wilma", "wilma"); 266 final IFormComponent barney2 = newFormComponent("barney", "barney_0"); 267 268 IRender body = newComponentsRenderBody(fs, new IFormComponent[] {barney1, wilma, barney2}, writer); 269 270 form.setBody(body); 271 form.setEventInvoker(invoker); 272 273 trainExtractBrowserEvent(cycle); 274 275 replay(); 276 277 Map props = new HashMap(); 278 props.put("id", "bsId"); 279 BrowserEvent event = new BrowserEvent("onclick", new EventTarget(props)); 280 281 invoker.invokeFormListeners(fs, cycle, event); 282 283 assertEquals(FormConstants.SUBMIT_NORMAL, fs.rewind()); 284 285 verify(); 286 } 287 288 public void test_Complex_Submit_Event_Handler() 289 { 290 IMarkupWriter writer = newWriter(); 291 NestedMarkupWriter nested = newNestedWriter(); 292 IRequestCycle cycle = newCycle(); 293 ResponseBuilder builder = newMock(ResponseBuilder.class); 294 IValidationDelegate delegate = newDelegate(); 295 PageRenderSupport support = newPageRenderSupport(); 296 ILink link = newLink(); 297 IRender render = newRender(); 298 299 MockForm form = new MockForm(delegate); 300 301 trainIsRewound(cycle, form, false); 302 trainGetPageRenderSupport(cycle, support); 303 304 replay(); 305 306 final FormSupport fs = newFormSupport(writer, cycle, form); 307 308 verify(); 309 310 trainCycleSeedEncoding(cycle); 311 312 form.setBody(new IRender() { 313 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 314 { 315 fs.addEventHandler(FormEventType.SUBMIT, "mySubmit1"); 316 fs.addEventHandler(FormEventType.SUBMIT, "mySubmit2"); 317 fs.addEventHandler(FormEventType.SUBMIT, "mySubmit3"); 318 } 319 }); 320 321 trainRegister(support, form, "myform"); 322 trainGetParameterNames(link, new String[] {"service"}); 323 trainGetParameterValues(link, "service", new String[] {"fred"}); 324 325 trainGetNestedWriter(writer, nested); 326 327 trainGetURL(link, null, "/app"); 328 329 writer.begin("form"); 330 writer.attribute("method", "post"); 331 writer.attribute("action", "/app"); 332 333 writer.attribute("id", "myform"); 334 335 support.addInitializationScript(form, "Tapestry.onsubmit('myform', function (event)" 336 + "\n{\n mySubmit1();\n mySubmit2();\n mySubmit3();\n});\n"); 337 338 render.render(writer, cycle); 339 writer.println(); 340 341 trainHiddenBlock(cycle, builder, writer, form, "fred", ""); 342 343 nested.close(); 344 writer.end(); 345 346 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 347 348 // Side test: what if no focus field? 349 350 trainGetFocusField(delegate, null); 351 352 expect(cycle.isFocusDisabled()).andReturn(false); 353 354 replay(); 355 356 fs.render("post", render, link, null, null); 357 358 verify(); 359 } 360 361 public void test_Encoding_Type() 362 { 363 IMarkupWriter writer = newWriter(); 364 NestedMarkupWriter nested = newNestedWriter(); 365 IRequestCycle cycle = newCycle(); 366 ResponseBuilder builder = newMock(ResponseBuilder.class); 367 IValidationDelegate delegate = newDelegate(); 368 PageRenderSupport support = newPageRenderSupport(); 369 ILink link = newLink(); 370 IRender render = newRender(); 371 372 MockForm form = new MockForm(delegate); 373 374 trainIsRewound(cycle, form, false); 375 376 trainGetPageRenderSupport(cycle, support); 377 378 replay(); 379 380 final FormSupport fs = newFormSupport(writer, cycle, form); 381 382 verify(); 383 384 trainCycleSeedEncoding(cycle); 385 386 form.setBody(new IRender() { 387 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 388 { 389 fs.setEncodingType("foo/bar"); 390 } 391 }); 392 393 trainRegister(support, form, "myform"); 394 395 trainGetParameterNames(link, new String[] {"service"}); 396 trainGetParameterValues(link, "service", new String[] {"fred"}); 397 398 trainGetNestedWriter(writer, nested); 399 400 trainGetURL(link, null, "/app"); 401 402 writer.begin("form"); 403 writer.attribute("method", "post"); 404 writer.attribute("action", "/app"); 405 406 writer.attribute("id", "myform"); 407 writer.attribute("enctype", "foo/bar"); 408 409 render.render(writer, cycle); 410 411 writer.println(); 412 413 trainHiddenBlock(cycle, builder, writer, form, "fred", ""); 414 415 nested.close(); 416 417 writer.end(); 418 419 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 420 421 trainGetFocusField(delegate, null); 422 423 expect(cycle.isFocusDisabled()).andReturn(false); 424 425 replay(); 426 427 fs.render("post", render, link, null, null); 428 429 verify(); 430 } 431 432 public void test_Field_Prerender_Twice() 433 { 434 IFormComponent field = newField(); 435 IMarkupWriter writer = newWriter(); 436 NestedMarkupWriter nested = newNestedWriter(); 437 IRequestCycle cycle = newCycle(); 438 Location l = newLocation(); 439 440 ResponseBuilder builder = newMock(ResponseBuilder.class); 441 442 trainGetExtendedId(field, "foo.bar"); 443 trainGetNestedWriter(writer, nested); 444 445 expect(cycle.getAttribute(TapestryUtils.FIELD_PRERENDER)).andReturn(null); 446 cycle.setAttribute(TapestryUtils.FIELD_PRERENDER, field); 447 448 expect(cycle.getResponseBuilder()).andReturn(builder); 449 450 builder.render(nested, field, cycle); 451 452 cycle.removeAttribute(TapestryUtils.FIELD_PRERENDER); 453 454 expect(nested.getBuffer()).andReturn("NESTED CONTENT"); 455 456 replay(); 457 458 FormSupport fs = new FormSupportImpl(cycle); 459 460 fs.prerenderField(writer, field, l); 461 462 verify(); 463 464 trainGetExtendedId(field, "foo.bar"); 465 466 replay(); 467 468 try 469 { 470 fs.prerenderField(writer, field, l); 471 unreachable(); 472 } 473 catch (ApplicationRuntimeException ex) { 474 assertEquals( 475 "Field EasyMock for interface org.apache.tapestry.form.IFormComponent has already been pre-rendered. " 476 + "This exception may indicate that a FieldLabel rendered, but the corresponding field did not.", 477 ex.getMessage()); 478 479 assertSame(l, ex.getLocation()); 480 assertSame(field, ex.getComponent()); 481 } 482 483 verify(); 484 485 } 486 487 public void test_Hidden_Values() 488 { 489 IMarkupWriter writer = newWriter(); 490 NestedMarkupWriter nested = newNestedWriter(); 491 IRequestCycle cycle = newCycle(); 492 ResponseBuilder builder = newMock(ResponseBuilder.class); 493 IValidationDelegate delegate = newDelegate(); 494 PageRenderSupport support = newPageRenderSupport(); 495 ILink link = newLink(); 496 IRender render = newRender(); 497 498 MockForm form = new MockForm(delegate); 499 500 trainIsRewound(cycle, form, false); 501 trainGetPageRenderSupport(cycle, support); 502 503 replay(); 504 505 final FormSupport fs = newFormSupport(writer, cycle, form); 506 507 verify(); 508 509 trainCycleSeedEncoding(cycle); 510 511 form.setBody(new IRender() { 512 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 513 { 514 fs.addHiddenValue("hidden1", "value1"); 515 fs.addHiddenValue("hidden2", "id2", "value2"); 516 } 517 }); 518 519 trainRegister(support, form, "myform"); 520 trainGetParameterNames(link, new String[]{"service"}); 521 trainGetParameterValues(link, "service", new String[] {"fred"}); 522 523 trainGetNestedWriter(writer, nested); 524 525 trainGetURL(link, null, "/app"); 526 527 writer.begin("form"); 528 writer.attribute("method", "post"); 529 writer.attribute("action", "/app"); 530 531 writer.attribute("id", "myform"); 532 533 render.render(writer, cycle); 534 535 writer.println(); 536 537 expect(cycle.getResponseBuilder()).andReturn(builder); 538 539 expect(builder.contains(form)).andReturn(false); 540 541 trainDiv(writer, form); 542 543 544 trainHidden(writer, "formids", ""); 545 trainHidden(writer, "seedids", "ENCODED"); 546 trainHidden(writer, "service", "fred"); 547 trainHidden(writer, "submitmode", ""); 548 trainHidden(writer, FormConstants.SUBMIT_NAME_PARAMETER, ""); 549 trainHidden(writer, "hidden1", "value1"); 550 trainHidden(writer, "hidden2", "id2", "value2"); 551 552 writer.end(); 553 554 nested.close(); 555 556 writer.end(); 557 558 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 559 560 trainGetFocusField(delegate, null); 561 562 expect(cycle.isFocusDisabled()).andReturn(false); 563 564 replay(); 565 566 fs.render("post", render, link, null, null); 567 568 verify(); 569 } 570 571 public void test_Invalid_Encoding_Type() 572 { 573 IMarkupWriter writer = newWriter(); 574 NestedMarkupWriter nested = newNestedWriter(); 575 IRequestCycle cycle = newCycle(); 576 IValidationDelegate delegate = newDelegate(); 577 PageRenderSupport support = newPageRenderSupport(); 578 ILink link = newLink(); 579 IRender render = newRender(); 580 581 MockForm form = new MockForm(delegate); 582 583 trainIsRewound(cycle, form, false); 584 585 trainGetPageRenderSupport(cycle, support); 586 587 replay(); 588 589 final FormSupport fs = newFormSupport(writer, cycle, form); 590 591 verify(); 592 593 trainCycleSeedEncoding(cycle); 594 595 form.setBody(new IRender() { 596 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 597 { 598 fs.setEncodingType("foo/bar"); 599 fs.setEncodingType("zip/zap"); 600 } 601 }); 602 603 trainRegister(support, form, "myform"); 604 605 trainGetParameterNames(link, new String[]{"service"}); 606 trainGetParameterValues(link, "service", new String[]{"fred"}); 607 608 trainGetNestedWriter(writer, nested); 609 610 replay(); 611 612 try { 613 fs.render("post", render, link, null, null); 614 unreachable(); 615 } 616 catch (ApplicationRuntimeException ex) { 617 assertEquals( 618 "Components within form SomePage/myform have requested conflicting encoding types 'foo/bar' and 'zip/zap'.", 619 ex.getMessage()); 620 assertSame(form, ex.getComponent()); 621 } 622 623 verify(); 624 } 625 626 public void test_Refresh_Rewind() 627 { 628 IMarkupWriter writer = newWriter(); 629 IRequestCycle cycle = newCycle(); 630 IValidationDelegate delegate = newDelegate(); 631 MockForm form = new MockForm(delegate); 632 ComponentEventInvoker invoker = org.easymock.classextension.EasyMock.createMock(ComponentEventInvoker.class); 633 634 trainIsRewound(cycle, form, true); 635 trainGetPageRenderSupport(cycle, null); 636 637 replay(); 638 639 final FormSupport fs = newFormSupport(writer, cycle, form); 640 641 verify(); 642 643 trainCycleSeedEncoding(cycle); 644 delegate.clear(); 645 646 trainCycleForRewind(cycle, "refresh", "barney", null); 647 648 final IFormComponent component = newFormComponent("barney", "barney"); 649 650 IRender body = newComponentRenderBody(fs, component, writer); 651 652 form.setBody(body); 653 form.setEventInvoker(invoker); 654 655 trainExtractBrowserEvent(cycle); 656 657 invoker.invokeFormListeners(eq(fs), eq(cycle), isA(BrowserEvent.class)); 658 659 delegate.clearErrors(); 660 661 replay(); 662 663 assertEquals(FormConstants.SUBMIT_REFRESH, fs.rewind()); 664 665 verify(); 666 } 667 668 public void test_Render_Extra_Reserved_Ids() 669 { 670 IMarkupWriter writer = newWriter(); 671 NestedMarkupWriter nested = newNestedWriter(); 672 IRequestCycle cycle = newCycle(); 673 ResponseBuilder builder = newMock(ResponseBuilder.class); 674 IValidationDelegate delegate = newDelegate(); 675 PageRenderSupport support = newPageRenderSupport(); 676 ILink link = newLink(); 677 IRender render = newRender(); 678 679 MockForm form = new MockForm(delegate); 680 681 trainIsRewound(cycle, form, false); 682 683 trainGetPageRenderSupport(cycle, support); 684 685 replay(); 686 687 final FormSupport fs = newFormSupport(writer, cycle, form); 688 689 verify(); 690 691 trainCycleSeedEncoding(cycle); 692 693 final IFormComponent component = newFormComponent("action", "action_0"); 694 695 IRender body = newComponentRenderBody(fs, component, nested); 696 697 form.setBody(body); 698 699 trainRegister(support, form, "myform"); 700 trainGetParameterNames(link, new String[]{"action"}); 701 trainGetParameterValues(link, "action", new String[] {"fred"}); 702 703 trainGetNestedWriter(writer, nested); 704 705 trainGetURL(link, null, "/app"); 706 707 writer.begin("form"); 708 writer.attribute("method", "post"); 709 writer.attribute("action", "/app"); 710 711 writer.attribute("id", "myform"); 712 713 render.render(writer, cycle); 714 715 writer.println(); 716 717 expect(cycle.getResponseBuilder()).andReturn(builder); 718 expect(builder.contains(form)).andReturn(false); 719 720 trainDiv(writer, form); 721 722 trainHidden(writer, "formids", "action_0"); 723 trainHidden(writer, "seedids", "ENCODED"); 724 trainHidden(writer, "action", "fred"); 725 trainHidden(writer, "reservedids", "action"); 726 trainHidden(writer, "submitmode", ""); 727 trainHidden(writer, FormConstants.SUBMIT_NAME_PARAMETER, ""); 728 729 writer.end(); 730 nested.close(); 731 writer.end(); 732 733 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 734 735 trainGetFocusField(delegate, null); 736 737 expect(cycle.isFocusDisabled()).andReturn(false); 738 739 replay(); 740 741 fs.render("post", render, link, null, null); 742 743 verify(); 744 } 745 746 public void test_Reset_Event_Handler() 747 { 748 IMarkupWriter writer = newWriter(); 749 NestedMarkupWriter nested = newNestedWriter(); 750 IRequestCycle cycle = newCycle(); 751 ResponseBuilder builder = newMock(ResponseBuilder.class); 752 753 IValidationDelegate delegate = newDelegate(); 754 PageRenderSupport support = newPageRenderSupport(); 755 ILink link = newLink(); 756 IRender render = newRender(); 757 758 MockForm form = new MockForm(delegate); 759 760 trainIsRewound(cycle, form, false); 761 762 trainGetPageRenderSupport(cycle, support); 763 764 replay(); 765 766 final FormSupport fs = newFormSupport(writer, cycle, form); 767 768 verify(); 769 770 trainCycleSeedEncoding(cycle); 771 form.setBody(new IRender() { 772 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 773 { 774 fs.addEventHandler(FormEventType.RESET, "myReset1"); 775 fs.addEventHandler(FormEventType.RESET, "myReset2"); 776 } 777 }); 778 779 trainRegister(support, form, "myform"); 780 781 trainGetParameterNames(link, new String[] {"service"}); 782 trainGetParameterValues(link, "service", new String[] {"fred"}); 783 784 trainGetNestedWriter(writer, nested); 785 786 trainGetURL(link, null, "/app"); 787 788 writer.begin("form"); 789 writer.attribute("method", "post"); 790 writer.attribute("action", "/app"); 791 792 writer.attribute("id", "myform"); 793 794 support.addInitializationScript(form, "Tapestry.onreset('myform', function (event)" 795 + "\n{\n myReset1();\n myReset2();\n});\n"); 796 797 render.render(writer, cycle); 798 799 writer.println(); 800 801 trainHiddenBlock(cycle, builder, writer, form, "fred", ""); 802 803 nested.close(); 804 writer.end(); 805 806 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 807 808 trainGetFocusField(delegate, null); 809 810 expect(cycle.isFocusDisabled()).andReturn(false); 811 812 replay(); 813 814 fs.render("post", render, link, null, null); 815 816 verify(); 817 } 818 819 public void test_Rewind_Extra_Reserved_Ids() 820 { 821 IMarkupWriter writer = newWriter(); 822 IRequestCycle cycle = newCycle(); 823 IValidationDelegate delegate = newDelegate(); 824 ComponentEventInvoker invoker = 825 org.easymock.classextension.EasyMock.createMock(ComponentEventInvoker.class); 826 827 MockForm form = new MockForm(delegate); 828 829 trainIsRewound(cycle, form, true); 830 831 trainGetPageRenderSupport(cycle, null); 832 833 replay(); 834 835 final FormSupport fs = newFormSupport(writer, cycle, form); 836 837 verify(); 838 839 trainCycleSeedEncoding(cycle); 840 delegate.clear(); 841 842 trainCycleForRewind(cycle, "action_0", "action"); 843 844 final IFormComponent component = newFormComponent("action", "action_0"); 845 846 IRender body = newComponentRenderBody(fs, component, writer); 847 848 form.setBody(body); 849 form.setEventInvoker(invoker); 850 851 trainExtractBrowserEvent(cycle); 852 853 invoker.invokeFormListeners(eq(fs), eq(cycle), isA(BrowserEvent.class)); 854 855 replay(); 856 857 assertEquals(FormConstants.SUBMIT_NORMAL, fs.rewind()); 858 859 verify(); 860 } 861 862 public void test_Rewind_Mismatch() 863 { 864 IMarkupWriter writer = newWriter(); 865 IRequestCycle cycle = newCycle(); 866 IValidationDelegate delegate = newDelegate(); 867 868 MockForm form = new MockForm(delegate); 869 870 trainIsRewound(cycle, form, true); 871 trainGetPageRenderSupport(cycle, null); 872 873 replay(); 874 875 final FormSupport fs = newFormSupport(writer, cycle, form); 876 877 verify(); 878 879 trainCycleSeedEncoding(cycle); 880 Location l = newLocation(); 881 882 delegate.clear(); 883 884 // So, the scenario here is that component "pebbles" was inside 885 // some kind of conditional that evaluated to true during the render, 886 // but is now false on the rewind. 887 888 trainCycleForRewind(cycle, "barney,wilma,pebbles,barney_0", null); 889 890 final IFormComponent barney1 = newFormComponent("barney", "barney"); 891 final IFormComponent wilma = newFormComponent("wilma", "wilma"); 892 final IFormComponent barney2 = newFormComponent("barney", "SomePage/barney", l); 893 894 IRender body = newComponentsRenderBody(fs, new IFormComponent[] 895 {barney1, wilma, barney2}, writer); 896 897 form.setBody(body); 898 899 replay(); 900 901 try { 902 fs.rewind(); 903 unreachable(); 904 } 905 catch (StaleLinkException ex) { 906 assertEquals( 907 "Rewind of form SomePage/myform expected allocated id #3 to be 'pebbles', but was 'barney_0' (requested by component SomePage/barney).", 908 ex.getMessage()); 909 assertSame(barney2, ex.getComponent()); 910 assertSame(l, ex.getLocation()); 911 } 912 913 verify(); 914 } 915 916 public void test_Rewind_Too_Long() 917 { 918 IMarkupWriter writer = newWriter(); 919 IRequestCycle cycle = newCycle(); 920 IValidationDelegate delegate = newDelegate(); 921 922 MockForm form = new MockForm(delegate); 923 924 trainIsRewound(cycle, form, true); 925 trainGetPageRenderSupport(cycle, null); 926 927 replay(); 928 929 final FormSupport fs = newFormSupport(writer, cycle, form); 930 931 verify(); 932 933 trainCycleSeedEncoding(cycle); 934 935 Location l = newLocation(); 936 delegate.clear(); 937 938 // So, the scenario here is that component "barney" was inside 939 // some kind of loop that executed once on the render, but twice 940 // on the rewind (i.e., an additional object was added in between). 941 942 trainCycleForRewind(cycle, "barney,wilma", null); 943 944 final IFormComponent barney1 = newFormComponent("barney", "barney"); 945 final IFormComponent wilma = newFormComponent("wilma", "wilma"); 946 final IFormComponent barney2 = newFormComponent("barney", "SomePage/barney", l); 947 948 IRender body = newComponentsRenderBody(fs, new IFormComponent[] 949 {barney1, wilma, barney2}, writer); 950 951 form.setBody(body); 952 953 replay(); 954 955 try { 956 fs.rewind(); 957 unreachable(); 958 } 959 catch (StaleLinkException ex) { 960 assertEquals( 961 "Rewind of form SomePage/myform expected only 2 form elements, but an additional id was requested by component SomePage/barney.", 962 ex.getMessage()); 963 assertSame(barney2, ex.getComponent()); 964 assertSame(l, ex.getLocation()); 965 } 966 967 verify(); 968 } 969 970 public void test_Rewind_Too_Short() 971 { 972 Location l = newLocation(); 973 IMarkupWriter writer = newWriter(); 974 IRequestCycle cycle = newCycle(); 975 IValidationDelegate delegate = newDelegate(); 976 ComponentEventInvoker invoker = org.easymock.classextension.EasyMock.createMock(ComponentEventInvoker.class); 977 978 MockForm form = new MockForm(delegate, l); 979 980 trainIsRewound(cycle, form, true); 981 trainGetPageRenderSupport(cycle, null); 982 983 replay(); 984 985 final FormSupport fs = newFormSupport(writer, cycle, form); 986 987 verify(); 988 989 trainCycleSeedEncoding(cycle); 990 delegate.clear(); 991 992 // So, the scenario here is that component "barney" was inside 993 // some kind of loop that executed twice on the render, but only once 994 // on the rewind (i.e., the object was deleted in between). 995 996 trainCycleForRewind(cycle, "barney,wilma,barney$0", null); 997 998 final IFormComponent barney1 = newFormComponent("barney", "barney"); 999 final IFormComponent wilma = newFormComponent("wilma", "wilma"); 1000 1001 IRender body = newComponentsRenderBody(fs, new IFormComponent[]{barney1, wilma}, writer); 1002 1003 form.setBody(body); 1004 form.setEventInvoker(invoker); 1005 1006 trainExtractBrowserEvent(cycle); 1007 1008 invoker.invokeFormListeners(eq(fs), eq(cycle), isA(BrowserEvent.class)); 1009 1010 replay(); 1011 1012 try { 1013 fs.rewind(); 1014 unreachable(); 1015 } 1016 catch (StaleLinkException ex) { 1017 assertEquals( 1018 "Rewind of form SomePage/myform expected 1 more form elements, starting with id 'barney$0'.", 1019 ex.getMessage()); 1020 assertSame(form, ex.getComponent()); 1021 assertSame(l, ex.getLocation()); 1022 } 1023 1024 verify(); 1025 } 1026 1027 public void test_Simple_Render() 1028 { 1029 IMarkupWriter writer = newWriter(); 1030 NestedMarkupWriter nested = newNestedWriter(); 1031 IRequestCycle cycle = newCycle(); 1032 ResponseBuilder builder = newMock(ResponseBuilder.class); 1033 IValidationDelegate delegate = newDelegate(); 1034 ILink link = newLink(); 1035 IRender render = newRender(); 1036 1037 MockForm form = new MockForm(delegate); 1038 1039 trainIsRewound(cycle, form, false); 1040 1041 PageRenderSupport support = newPageRenderSupport(); 1042 1043 trainGetPageRenderSupport(cycle, support); 1044 1045 replay(); 1046 1047 final FormSupport fs = newFormSupport(writer, cycle, form); 1048 1049 verify(); 1050 1051 trainCycleSeedEncoding(cycle); 1052 final IFormComponent component = newFormComponent("barney", "barney"); 1053 1054 IRender body = newComponentRenderBody(fs, component, nested); 1055 1056 form.setBody(body); 1057 1058 trainRegister(support, form, "myform"); 1059 1060 trainGetParameterNames(link, new String[] 1061 {"service"}); 1062 trainGetParameterValues(link, "service", new String[] 1063 {"fred"}); 1064 1065 trainGetNestedWriter(writer, nested); 1066 1067 trainGetURL(link, null, "/app"); 1068 1069 writer.begin("form"); 1070 writer.attribute("method", "post"); 1071 writer.attribute("action", "/app"); 1072 1073 writer.attribute("id", "myform"); 1074 1075 render.render(writer, cycle); 1076 1077 writer.println(); 1078 1079 trainHiddenBlock(cycle, builder, writer, form, "fred", "barney"); 1080 1081 nested.close(); 1082 1083 writer.end(); 1084 1085 trainGetFocusField(delegate, "barney"); 1086 1087 expect(cycle.isFocusDisabled()).andReturn(false); 1088 1089 // Side test: check for another form already grabbing focus 1090 1091 trainGetFieldFocus(cycle, null); 1092 1093 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 1094 support.addScriptAfterInitialization(form, "tapestry.form.focusField('barney');"); 1095 cycle.setAttribute(FormSupportImpl.FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE); 1096 1097 replay(); 1098 1099 fs.render("post", render, link, null, null); 1100 1101 verify(); 1102 } 1103 1104 public void test_Simple_Render_With_Deferred_Runnable() 1105 { 1106 IMarkupWriter writer = newWriter(); 1107 NestedMarkupWriter nested = newNestedWriter(); 1108 IRequestCycle cycle = newCycle(); 1109 ResponseBuilder builder = newMock(ResponseBuilder.class); 1110 IValidationDelegate delegate = newDelegate(); 1111 ILink link = newLink(); 1112 IRender render = newRender(); 1113 1114 MockForm form = new MockForm(delegate); 1115 1116 trainIsRewound(cycle, form, false); 1117 1118 PageRenderSupport support = newPageRenderSupport(); 1119 1120 trainGetPageRenderSupport(cycle, support); 1121 1122 replay(); 1123 1124 final FormSupport fs = newFormSupport(writer, cycle, form); 1125 1126 verify(); 1127 1128 trainCycleSeedEncoding(cycle); 1129 IRender body = new IRender() { 1130 public void render(final IMarkupWriter pwriter, IRequestCycle pcycle) 1131 { 1132 fs.addDeferredRunnable(new Runnable() { 1133 1134 public void run() 1135 { 1136 pwriter.print("DEFERRED"); 1137 } 1138 1139 }); 1140 } 1141 1142 }; 1143 1144 form.setBody(body); 1145 1146 trainRegister(support, form, "myform"); 1147 1148 trainGetParameterNames(link, new String[] {"service"}); 1149 trainGetParameterValues(link, "service", new String[]{"fred"}); 1150 1151 trainGetNestedWriter(writer, nested); 1152 1153 nested.print("DEFERRED"); 1154 1155 trainGetURL(link, null, "/app"); 1156 1157 writer.begin("form"); 1158 writer.attribute("method", "post"); 1159 writer.attribute("action", "/app"); 1160 1161 writer.attribute("id", "myform"); 1162 1163 render.render(writer, cycle); 1164 1165 writer.println(); 1166 1167 trainHiddenBlock(cycle, builder, writer, form, "fred", ""); 1168 1169 nested.close(); 1170 1171 writer.end(); 1172 1173 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 1174 1175 trainGetFocusField(delegate, null); 1176 1177 expect(cycle.isFocusDisabled()).andReturn(false); 1178 1179 replay(); 1180 1181 fs.render("post", render, link, null, null); 1182 1183 verify(); 1184 } 1185 1186 public void test_Simple_Render_With_Scheme() 1187 { 1188 IMarkupWriter writer = newWriter(); 1189 NestedMarkupWriter nested = newNestedWriter(); 1190 IRequestCycle cycle = newCycle(); 1191 ResponseBuilder builder = newMock(ResponseBuilder.class); 1192 IValidationDelegate delegate = newDelegate(); 1193 ILink link = newLink(); 1194 IRender render = newRender(); 1195 1196 MockForm form = new MockForm(delegate); 1197 1198 trainIsRewound(cycle, form, false); 1199 1200 PageRenderSupport support = newPageRenderSupport(); 1201 1202 trainGetPageRenderSupport(cycle, support); 1203 1204 replay(); 1205 1206 final FormSupport fs = newFormSupport(writer, cycle, form); 1207 1208 verify(); 1209 1210 trainCycleSeedEncoding(cycle); 1211 final IFormComponent component = newFormComponent("barney", "barney"); 1212 1213 IRender body = newComponentRenderBody(fs, component, nested); 1214 1215 form.setBody(body); 1216 1217 trainRegister(support, form, "myform"); 1218 1219 trainGetParameterNames(link, new String[] 1220 {"service"}); 1221 trainGetParameterValues(link, "service", new String[] 1222 {"fred"}); 1223 1224 trainGetNestedWriter(writer, nested); 1225 1226 trainGetURL(link, "https", "https://foo.bar/app", 443); 1227 1228 writer.begin("form"); 1229 writer.attribute("method", "post"); 1230 writer.attribute("action", "https://foo.bar/app"); 1231 1232 writer.attribute("id", "myform"); 1233 1234 render.render(writer, cycle); 1235 1236 writer.println(); 1237 1238 trainHiddenBlock(cycle, builder, writer, form, "fred", "barney"); 1239 1240 nested.close(); 1241 1242 writer.end(); 1243 1244 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 1245 1246 trainGetFocusField(delegate, "barney"); 1247 1248 expect(cycle.isFocusDisabled()).andReturn(false); 1249 1250 // Side test: check for another form already grabbing focus 1251 1252 trainGetFieldFocus(cycle, Boolean.TRUE); 1253 1254 // support.addInitializationScript(form, "tapestry.form.focusField('barney');"); 1255 1256 // cycle.setAttribute(FormSupportImpl.FIELD_FOCUS_ATTRIBUTE, true); 1257 1258 replay(); 1259 1260 fs.render("post", render, link, "https", new Integer(443)); 1261 1262 verify(); 1263 } 1264 1265 public void test_Simple_Rewind() 1266 { 1267 IMarkupWriter writer = newWriter(); 1268 IRequestCycle cycle = newCycle(); 1269 IValidationDelegate delegate = newDelegate(); 1270 MockForm form = new MockForm(delegate); 1271 ComponentEventInvoker invoker = 1272 org.easymock.classextension.EasyMock.createMock(ComponentEventInvoker.class); 1273 1274 trainIsRewound(cycle, form, true); 1275 1276 trainGetPageRenderSupport(cycle, null); 1277 1278 replay(); 1279 1280 final FormSupport fs = newFormSupport(writer, cycle, form); 1281 1282 verify(); 1283 1284 trainCycleSeedEncoding(cycle); 1285 delegate.clear(); 1286 1287 trainCycleForRewind(cycle, "barney", null); 1288 1289 final IFormComponent component = newFormComponent("barney", "barney"); 1290 1291 IRender body = newComponentRenderBody(fs, component, writer); 1292 1293 form.setBody(body); 1294 form.setEventInvoker(invoker); 1295 1296 trainExtractBrowserEvent(cycle); 1297 1298 invoker.invokeFormListeners(eq(fs), eq(cycle), isA(BrowserEvent.class)); 1299 1300 replay(); 1301 1302 assertEquals(FormConstants.SUBMIT_NORMAL, fs.rewind()); 1303 1304 verify(); 1305 } 1306 1307 public void test_Simple_Rewind_With_Deferred_Runnable() 1308 { 1309 IMarkupWriter writer = newWriter(); 1310 IRequestCycle cycle = newCycle(); 1311 IValidationDelegate delegate = newDelegate(); 1312 MockForm form = new MockForm(delegate); 1313 ComponentEventInvoker invoker = 1314 org.easymock.classextension.EasyMock.createMock(ComponentEventInvoker.class); 1315 1316 trainIsRewound(cycle, form, true); 1317 1318 trainGetPageRenderSupport(cycle, null); 1319 1320 replay(); 1321 1322 final FormSupport fs = newFormSupport(writer, cycle, form); 1323 1324 verify(); 1325 1326 trainCycleSeedEncoding(cycle); 1327 delegate.clear(); 1328 1329 trainCycleForRewind(cycle, "", null); 1330 trainExtractBrowserEvent(cycle); 1331 1332 writer.print("DEFERRED"); 1333 1334 invoker.invokeFormListeners(eq(fs), eq(cycle), isA(BrowserEvent.class)); 1335 1336 replay(); 1337 1338 IRender body = new IRender() { 1339 1340 public void render(final IMarkupWriter pwriter, IRequestCycle pcycle) 1341 { 1342 fs.addDeferredRunnable(new Runnable() { 1343 public void run() 1344 { 1345 pwriter.print("DEFERRED"); 1346 } 1347 1348 }); 1349 } 1350 1351 }; 1352 1353 form.setBody(body); 1354 form.setEventInvoker(invoker); 1355 1356 assertEquals(FormConstants.SUBMIT_NORMAL, fs.rewind()); 1357 1358 verify(); 1359 } 1360 1361 public void test_Simple_Submit_Event_Handler() 1362 { 1363 IMarkupWriter writer = newWriter(); 1364 NestedMarkupWriter nested = newNestedWriter(); 1365 IRequestCycle cycle = newCycle(); 1366 ResponseBuilder builder = newMock(ResponseBuilder.class); 1367 IValidationDelegate delegate = newDelegate(); 1368 ILink link = newLink(); 1369 IRender render = newRender(); 1370 1371 MockForm form = new MockForm(delegate); 1372 1373 trainIsRewound(cycle, form, false); 1374 1375 PageRenderSupport support = newPageRenderSupport(); 1376 1377 trainGetPageRenderSupport(cycle, support); 1378 1379 replay(); 1380 1381 final FormSupport fs = newFormSupport(writer, cycle, form); 1382 1383 verify(); 1384 1385 trainCycleSeedEncoding(cycle); 1386 trainRegister(support, form, "myform"); 1387 1388 trainGetParameterNames(link, new String[] 1389 {"service"}); 1390 trainGetParameterValues(link, "service", new String[] 1391 {"fred"}); 1392 1393 trainGetNestedWriter(writer, nested); 1394 1395 trainGetURL(link, null, "/app"); 1396 1397 writer.begin("form"); 1398 writer.attribute("method", "post"); 1399 writer.attribute("action", "/app"); 1400 1401 writer.attribute("id", "myform"); 1402 1403 form.setBody(new IRender() { 1404 public void render(IMarkupWriter pwriter, IRequestCycle pcycle) 1405 { 1406 fs.addEventHandler(FormEventType.SUBMIT, "mySubmit()"); 1407 } 1408 }); 1409 1410 support.addInitializationScript(form, "Tapestry.onsubmit('myform', function (event)" 1411 + "\n{\n mySubmit();\n});\n"); 1412 1413 render.render(writer, cycle); 1414 1415 writer.println(); 1416 1417 trainHiddenBlock(cycle, builder, writer, form, "fred", ""); 1418 1419 nested.close(); 1420 1421 writer.end(); 1422 1423 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");"); 1424 1425 trainGetFocusField(delegate, null); 1426 1427 expect(cycle.isFocusDisabled()).andReturn(false); 1428 1429 replay(); 1430 1431 fs.render("post", render, link, null, null); 1432 1433 verify(); 1434 } 1435 1436 private void trainCycleForRewind(IRequestCycle cycle, String allocatedIds, String reservedIds) 1437 { 1438 trainCycleForRewind(cycle, "submit", allocatedIds, reservedIds); 1439 } 1440 1441 private void trainCycleForRewind(IRequestCycle cycle, String submitMode, String allocatedIds, 1442 String reservedIds) 1443 { 1444 trainGetParameter(cycle, FormSupportImpl.SEED_IDS, ""); 1445 cycle.initializeIdState(""); 1446 1447 trainGetParameter(cycle, FormSupportImpl.SUBMIT_MODE, submitMode); 1448 trainGetParameter(cycle, FormSupportImpl.FORM_IDS, allocatedIds); 1449 trainGetParameter(cycle, FormSupportImpl.RESERVED_FORM_IDS, reservedIds); 1450 } 1451 1452 protected void trainDiv(IMarkupWriter writer, IForm form) 1453 { 1454 writer.begin("div"); 1455 writer.attribute("style", "display:none;"); 1456 writer.attribute("id", form.getName() + "hidden"); 1457 } 1458 1459 private void trainGetFieldFocus(IRequestCycle cycle, Object value) 1460 { 1461 expect(cycle.getAttribute(FormSupportImpl.FIELD_FOCUS_ATTRIBUTE)).andReturn(value); 1462 } 1463 1464 private void trainGetFocusField(IValidationDelegate delegate, String fieldName) 1465 { 1466 expect(delegate.getFocusField()).andReturn(fieldName); 1467 } 1468 1469 private void trainGetURL(ILink link, String scheme, String URL, int port) 1470 { 1471 // This will change shortly, with the new scheme parameter passed into FormSupport.render() 1472 1473 expect(link.getURL(scheme, null, port, null, false)).andReturn(URL); 1474 } 1475 1476 private void trainGetURL(ILink link, String scheme, String URL) 1477 { 1478 trainGetURL(link, scheme, URL, 0); 1479 } 1480 1481 private void trainHidden(IMarkupWriter writer, String name, String value) 1482 { 1483 writer.beginEmpty("input"); 1484 writer.attribute("type", "hidden"); 1485 writer.attribute("name", name); 1486 writer.attribute("value", value); 1487 writer.println(); 1488 } 1489 1490 private void trainHidden(IMarkupWriter writer, String name, String id, String value) 1491 { 1492 writer.beginEmpty("input"); 1493 writer.attribute("type", "hidden"); 1494 writer.attribute("name", name); 1495 writer.attribute("id", id); 1496 writer.attribute("value", value); 1497 writer.println(); 1498 } 1499 1500 protected void trainHiddenBlock(IRequestCycle cycle, ResponseBuilder builder, 1501 IMarkupWriter writer, IForm form, 1502 String serviceName, String formIds) 1503 { 1504 expect(cycle.getResponseBuilder()).andReturn(builder); 1505 1506 expect(builder.contains(form)).andReturn(false); 1507 1508 trainDiv(writer, form); 1509 1510 trainHidden(writer, "formids", formIds); 1511 trainHidden(writer, "seedids", "ENCODED"); 1512 trainHidden(writer, "service", serviceName); 1513 trainHidden(writer, "submitmode", ""); 1514 trainHidden(writer, FormConstants.SUBMIT_NAME_PARAMETER, ""); 1515 1516 writer.end(); 1517 } 1518 1519 protected void trainIsRewound(IRequestCycle cycle, IForm form, boolean isRewound) 1520 { 1521 expect(cycle.isRewound(form)).andReturn(isRewound); 1522 } 1523 1524 private void trainRegister(PageRenderSupport support, IForm form, String formId) 1525 { 1526 support.addInitializationScript(form, "dojo.require(\"tapestry.form\");" 1527 + "tapestry.form.registerForm(\"" + formId + "\");"); 1528 } 1529 }