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.markup; 016 017 import org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.tapestry.BaseComponentTestCase; 019 import org.apache.tapestry.IMarkupWriter; 020 import org.testng.annotations.AfterClass; 021 import org.testng.annotations.Test; 022 023 import java.io.CharArrayWriter; 024 import java.io.PrintWriter; 025 026 /** 027 * Tests for {@link org.apache.tapestry.markup.MarkupWriterImpl}. 028 * 029 * @author Howard M. Lewis Ship 030 * @since 4.0 031 */ 032 @Test(sequential=true) 033 public class TestMarkupWriter extends BaseComponentTestCase 034 { 035 private static CharArrayWriter _writer; 036 037 private static final String NEWLINE = System.getProperty("line.separator"); 038 039 private static class EchoMarkupFilter implements MarkupFilter 040 { 041 public void print(PrintWriter writer, char[] data, int offset, int length, 042 boolean escapeQuotes) 043 { 044 writer.print('{'); 045 writer.write(data, offset, length); 046 writer.print('}'); 047 } 048 } 049 050 public static class PrintWriterFixture extends PrintWriter 051 { 052 053 public PrintWriterFixture() 054 { 055 super(_writer); 056 } 057 } 058 059 private MarkupFilter newFilter() 060 { 061 return newMock(MarkupFilter.class); 062 } 063 064 private PrintWriter newPrintWriter() 065 { 066 _writer = new CharArrayWriter(); 067 068 return new PrintWriter(_writer); 069 } 070 071 @AfterClass 072 protected void tearDown() throws Exception 073 { 074 _writer = null; 075 } 076 077 private void assertOutput(String expected) 078 { 079 assertEquals(_writer.toString(), expected); 080 081 _writer.reset(); 082 } 083 084 public void test_Int_Attribute() 085 { 086 MarkupFilter filter = new EchoMarkupFilter(); 087 PrintWriter writer = newPrintWriter(); 088 089 replay(); 090 091 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 092 093 mw.begin("span"); 094 mw.attribute("width", 5); 095 mw.end(); 096 097 verify(); 098 099 assertOutput("<span width=\"{5}\"></span>"); 100 } 101 102 public void test_Int_Attribute_Requires_Tag() 103 { 104 MarkupFilter filter = newFilter(); 105 PrintWriter writer = newPrintWriter(); 106 107 replay(); 108 109 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 110 111 try 112 { 113 mw.attribute("width", 5); 114 unreachable(); 115 } 116 catch (IllegalStateException ex) 117 { 118 // Expected. 119 } 120 121 verify(); 122 } 123 124 public void test_Boolean_Attribute() 125 { 126 MarkupFilter filter = new EchoMarkupFilter(); 127 PrintWriter writer = newPrintWriter(); 128 129 replay(); 130 131 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 132 133 mw.begin("div"); 134 mw.attribute("true", true); 135 mw.attribute("false", false); 136 137 mw.end(); 138 139 verify(); 140 141 assertOutput("<div true=\"{true}\" false=\"{false}\"></div>"); 142 } 143 144 public void test_Boolean_Attribute_Requires_Tag() 145 { 146 MarkupFilter filter = newFilter(); 147 PrintWriter writer = newPrintWriter(); 148 149 replay(); 150 151 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 152 153 try 154 { 155 mw.attribute("true", true); 156 unreachable(); 157 } 158 catch (IllegalStateException ex) 159 { 160 // Expected. 161 } 162 163 verify(); 164 } 165 166 public void test_Attribute() 167 { 168 MarkupFilter filter = new EchoMarkupFilter(); 169 PrintWriter writer = newPrintWriter(); 170 171 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 172 173 mw.begin("span"); 174 mw.attribute("width", "100%"); 175 mw.end(); 176 177 // Braces added by EchoMarkupFilter, to prove its there. 178 179 assertOutput("<span width=\"{100%}\"></span>"); 180 } 181 182 public void test_Attribute_Null() 183 { 184 MarkupFilter filter = new EchoMarkupFilter(); 185 PrintWriter writer = newPrintWriter(); 186 187 replay(); 188 189 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 190 191 mw.begin("span"); 192 mw.attribute("width", null); 193 mw.end(); 194 195 // Braces added by EchoMarkupFilter, to prove its there. 196 197 assertOutput("<span width=\"\"></span>"); 198 199 verify(); 200 } 201 202 public void test_Duplicate_Attributes() 203 { 204 MarkupFilter filter = new EchoMarkupFilter(); 205 PrintWriter writer = newPrintWriter(); 206 207 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 208 209 mw.begin("span"); 210 mw.attribute("width", "100%"); 211 mw.attribute("width", "80%"); 212 mw.end(); 213 214 assertOutput("<span width=\"{80%}\"></span>"); 215 } 216 217 public void test_Attribute_Requires_Tag() 218 { 219 MarkupFilter filter = newFilter(); 220 PrintWriter writer = newPrintWriter(); 221 222 replay(); 223 224 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 225 226 try 227 { 228 mw.attribute("attribute", "value"); 229 unreachable(); 230 } 231 catch (IllegalStateException ex) 232 { 233 // Expected. 234 } 235 236 verify(); 237 } 238 239 public void test_Append_Attribute() 240 { 241 MarkupFilter filter = new EchoMarkupFilter(); 242 PrintWriter writer = newPrintWriter(); 243 244 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 245 246 mw.begin("span"); 247 mw.appendAttribute("class", "fred"); 248 mw.appendAttribute("class", "barney"); 249 mw.appendAttribute("type", false); 250 mw.end(); 251 252 assertOutput("<span class=\"{fred barney}\" type=\"{false}\"></span>"); 253 } 254 255 public void test_Append_Attribute_Null() 256 { 257 MarkupFilter filter = new EchoMarkupFilter(); 258 PrintWriter writer = newPrintWriter(); 259 260 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 261 262 mw.begin("span"); 263 mw.appendAttribute("class", "fred"); 264 mw.appendAttribute("class", null); 265 mw.end(); 266 267 assertOutput("<span class=\"{fred}\"></span>"); 268 } 269 270 public void test_Append_Attribute_Raw() 271 { 272 MarkupFilter filter = new EchoMarkupFilter(); 273 PrintWriter writer = newPrintWriter(); 274 275 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 276 277 mw.begin("span"); 278 mw.appendAttributeRaw("class", null); 279 mw.appendAttributeRaw("type", "<>"); 280 mw.end(); 281 282 assertOutput("<span class=\"\" type=\"<>\"></span>"); 283 } 284 285 public void test_Get_Attribute() 286 { 287 MarkupFilter filter = new EchoMarkupFilter(); 288 PrintWriter writer = newPrintWriter(); 289 290 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 291 292 mw.begin("span"); 293 mw.appendAttribute("class", "fred"); 294 295 assertNotNull(mw.getAttribute("class")); 296 assertEquals(mw.getAttribute("class").toString(), "fred"); 297 298 mw.end(); 299 300 assertOutput("<span class=\"{fred}\"></span>"); 301 } 302 303 public void test_Has_Attribute() 304 { 305 MarkupFilter filter = new EchoMarkupFilter(); 306 PrintWriter writer = newPrintWriter(); 307 308 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 309 310 mw.begin("span"); 311 mw.appendAttribute("class", "fred"); 312 313 assertTrue(mw.hasAttribute("class")); 314 assertEquals(mw.getAttribute("class").toString(), "fred"); 315 316 mw.end(); 317 318 assertOutput("<span class=\"{fred}\"></span>"); 319 } 320 321 public void test_Remove_Attribute() 322 { 323 MarkupFilter filter = new EchoMarkupFilter(); 324 PrintWriter writer = newPrintWriter(); 325 326 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 327 328 mw.begin("span"); 329 mw.appendAttribute("class", "fred"); 330 331 assertTrue(mw.hasAttribute("class")); 332 333 assertEquals(mw.removeAttribute("class").toString(), "fred"); 334 335 assertFalse(mw.hasAttribute("class")); 336 337 mw.end(); 338 339 assertOutput("<span></span>"); 340 } 341 342 public void test_Clear_Attributes() 343 { 344 MarkupFilter filter = new EchoMarkupFilter(); 345 PrintWriter writer = newPrintWriter(); 346 347 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 348 349 mw.begin("span"); 350 mw.attribute("class", "fred"); 351 mw.attribute("barney", "bam bam"); 352 353 assertTrue(mw.hasAttribute("barney")); 354 mw.clearAttributes(); 355 356 assertFalse(mw.hasAttribute("barney")); 357 assertFalse(mw.hasAttribute("class")); 358 359 mw.end(); 360 361 assertOutput("<span></span>"); 362 } 363 364 public void testEnd() 365 { 366 MarkupFilter filter = new EchoMarkupFilter(); 367 PrintWriter writer = newPrintWriter(); 368 369 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 370 371 mw.begin("div"); 372 mw.attribute("width", "100%"); 373 mw.end(); 374 375 assertOutput("<div width=\"{100%}\"></div>"); 376 } 377 378 public void testPrint() 379 { 380 MarkupFilter filter = new EchoMarkupFilter(); 381 PrintWriter writer = newPrintWriter(); 382 383 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 384 385 mw.print("Hello"); 386 387 assertOutput("{Hello}"); 388 } 389 390 public void testPrintWithRawFlag() 391 { 392 MarkupFilter filter = new EchoMarkupFilter(); 393 PrintWriter writer = newPrintWriter(); 394 395 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 396 397 mw.print("Filtered", false); 398 399 assertOutput("{Filtered}"); 400 401 mw.print("Raw", true); 402 403 assertOutput("Raw"); 404 } 405 406 public void testPrintClosesCurrentTag() 407 { 408 MarkupFilter filter = new EchoMarkupFilter(); 409 PrintWriter writer = newPrintWriter(); 410 411 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 412 413 mw.begin("div"); 414 mw.print("Hello"); 415 mw.end(); 416 417 assertOutput("<div>{Hello}</div>"); 418 } 419 420 public void testPrintNull() 421 { 422 MarkupFilter filter = newFilter(); 423 PrintWriter writer = newPrintWriter(); 424 425 replay(); 426 427 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 428 429 mw.begin("span"); 430 mw.print(null); 431 mw.end(); 432 433 assertOutput("<span></span>"); 434 435 verify(); 436 } 437 438 public void testCloseTag() 439 { 440 MarkupFilter filter = newFilter(); 441 PrintWriter writer = newPrintWriter(); 442 443 replay(); 444 445 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 446 447 mw.begin("div"); 448 mw.closeTag(); 449 450 assertOutput("<div>"); 451 452 mw.beginEmpty("img"); 453 mw.closeTag(); 454 455 assertOutput("<img />"); 456 457 verify(); 458 } 459 460 public void testNestedEnd() 461 { 462 MarkupFilter filter = newFilter(); 463 PrintWriter writer = newPrintWriter(); 464 465 replay(); 466 467 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 468 469 mw.begin("h1"); 470 mw.begin("b"); 471 mw.beginEmpty("img"); 472 mw.begin("span"); 473 mw.closeTag(); 474 475 assertOutput("<h1><b><img /><span>"); 476 477 mw.end(); 478 479 assertOutput("</span>"); 480 481 mw.end(); 482 483 // Note: skipping the empty <img> tag 484 485 assertOutput("</b>"); 486 487 mw.end(); 488 489 assertOutput("</h1>"); 490 491 verify(); 492 } 493 494 public void testEndNamed() 495 { 496 MarkupFilter filter = newFilter(); 497 PrintWriter writer = newPrintWriter(); 498 499 replay(); 500 501 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 502 503 mw.begin("h1"); 504 mw.begin("b"); 505 mw.beginEmpty("img"); 506 mw.begin("span"); 507 mw.closeTag(); 508 509 assertOutput("<h1><b><img /><span>"); 510 511 // Uses the stack to close elements. 512 513 mw.end("h1"); 514 515 assertOutput("</span></b></h1>"); 516 517 verify(); 518 } 519 520 public void testClose() 521 { 522 MarkupFilter filter = new EchoMarkupFilter(); 523 PrintWriter writer = newPrintWriter(); 524 525 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 526 527 mw.begin("span"); 528 mw.begin("div"); 529 mw.print("text"); 530 531 assertOutput("<span><div>{text}"); 532 533 mw.close(); 534 535 assertOutput("</div></span>"); 536 537 assertEquals(false, writer.checkError()); 538 539 writer.println(); 540 541 assertEquals(true, writer.checkError()); 542 } 543 544 public void testComment() 545 { 546 MarkupFilter filter = newFilter(); 547 PrintWriter writer = newPrintWriter(); 548 549 replay(); 550 551 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 552 553 mw.comment("Tapestry Rocks!"); 554 555 // Note: not filtered 556 557 assertOutput("<!-- Tapestry Rocks! -->" + NEWLINE); 558 559 verify(); 560 } 561 562 public void testCommentClosesTag() 563 { 564 MarkupFilter filter = newFilter(); 565 PrintWriter writer = newPrintWriter(); 566 567 replay(); 568 569 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 570 571 mw.begin("div"); 572 mw.comment("Tapestry Rocks!"); 573 574 // Note: not filtered 575 576 assertOutput("<div><!-- Tapestry Rocks! -->" + NEWLINE); 577 578 verify(); 579 } 580 581 public void testFlush() 582 { 583 _writer = new CharArrayWriter(); 584 585 MarkupFilter filter = newFilter(); 586 PrintWriter writer = org.easymock.classextension.EasyMock.createMock(PrintWriterFixture.class); 587 588 writer.flush(); 589 590 replay(); 591 org.easymock.classextension.EasyMock.replay(writer); 592 593 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 594 595 mw.flush(); 596 597 verify(); 598 org.easymock.classextension.EasyMock.verify(writer); 599 } 600 601 public void testPrintCharArray() 602 { 603 MarkupFilter filter = new EchoMarkupFilter(); 604 PrintWriter writer = newPrintWriter(); 605 606 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 607 608 mw.print(new char[] 609 { 'A', 'j', 'a', 'x', 'i', 'a', 'n' }, 1, 3); 610 611 assertOutput("{jax}"); 612 } 613 614 public void testPrintChar() 615 { 616 MarkupFilter filter = new EchoMarkupFilter(); 617 PrintWriter writer = newPrintWriter(); 618 619 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 620 621 mw.print('x'); 622 623 assertOutput("{x}"); 624 } 625 626 public void testPrintInt() 627 { 628 MarkupFilter filter = new EchoMarkupFilter(); 629 PrintWriter writer = newPrintWriter(); 630 631 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 632 633 mw.print(123); 634 635 assertOutput("123"); 636 } 637 638 public void testPrintIntClosesTag() 639 { 640 MarkupFilter filter = new EchoMarkupFilter(); 641 PrintWriter writer = newPrintWriter(); 642 643 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 644 645 mw.begin("span"); 646 647 assertOutput("<span"); 648 649 mw.print(123); 650 651 assertOutput(">123"); 652 } 653 654 public void testPrintLn() 655 { 656 MarkupFilter filter = newFilter(); 657 PrintWriter writer = newPrintWriter(); 658 659 replay(); 660 661 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 662 663 mw.println(); 664 665 assertOutput(NEWLINE); 666 667 verify(); 668 } 669 670 public void testPrintLnClosesTag() 671 { 672 MarkupFilter filter = newFilter(); 673 PrintWriter writer = newPrintWriter(); 674 675 replay(); 676 677 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 678 679 mw.begin("p"); 680 681 assertOutput("<p"); 682 683 mw.println(); 684 685 assertOutput(">" + NEWLINE); 686 687 verify(); 688 } 689 690 public void testPrintRawCharArray() 691 { 692 MarkupFilter filter = newFilter(); 693 PrintWriter writer = newPrintWriter(); 694 695 replay(); 696 697 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 698 699 mw.begin("p"); 700 701 assertOutput("<p"); 702 703 mw.printRaw(new char[] 704 { 'a', 'b', 'c', 'd' }, 1, 2); 705 706 assertOutput(">bc"); 707 708 verify(); 709 } 710 711 public void testPrintRawString() 712 { 713 MarkupFilter filter = newFilter(); 714 PrintWriter writer = newPrintWriter(); 715 716 replay(); 717 718 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 719 720 mw.begin("p"); 721 722 assertOutput("<p"); 723 724 mw.print("Fred", true); 725 726 assertOutput(">Fred"); 727 728 verify(); 729 } 730 731 public void testNestedWriter() 732 { 733 MarkupFilter filter = new EchoMarkupFilter(); 734 PrintWriter writer = newPrintWriter(); 735 736 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 737 738 mw.begin("div"); 739 740 IMarkupWriter nested = mw.getNestedWriter(); 741 742 assertEquals("text/html", nested.getContentType()); 743 744 nested.begin("span"); 745 nested.attribute("class", "inner"); 746 nested.print("nested content"); 747 748 mw.attribute("class", "outer"); 749 750 nested.close(); 751 752 // Close the <div>, then comes the inner/nested content. 753 754 mw.print("after content"); 755 mw.end(); 756 757 assertOutput("<div class=\"{outer}\"><span class=\"{inner}\">{nested content}</span>{after content}</div>"); 758 } 759 760 public void testRepeatCloseOnNestedWriter() 761 { 762 MarkupFilter filter = new EchoMarkupFilter(); 763 PrintWriter writer = newPrintWriter(); 764 765 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 766 767 IMarkupWriter nested = mw.getNestedWriter(); 768 769 nested.close(); 770 771 try 772 { 773 nested.close(); 774 } 775 catch (IllegalStateException ex) 776 { 777 // Expected. 778 } 779 } 780 781 public void testEndNamedNotOnStack() 782 { 783 MarkupFilter filter = new EchoMarkupFilter(); 784 PrintWriter writer = newPrintWriter(); 785 786 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 787 788 mw.begin("div"); 789 mw.begin("span"); 790 mw.begin("b"); 791 792 try 793 { 794 mw.end("table"); 795 unreachable(); 796 } 797 catch (ApplicationRuntimeException ex) 798 { 799 assertEquals( 800 "Can not close to element 'table', because no such element is on the active elements stack (div, span, b).", 801 ex.getMessage()); 802 } 803 } 804 805 public void testEndWithEmptyStack() 806 { 807 MarkupFilter filter = new EchoMarkupFilter(); 808 PrintWriter writer = newPrintWriter(); 809 810 IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter); 811 812 try 813 { 814 mw.end(); 815 unreachable(); 816 } 817 catch (ApplicationRuntimeException ex) 818 { 819 assertEquals( 820 "Can not end most recent element because the stack of active elements is empty.", 821 ex.getMessage()); 822 } 823 } 824 }