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", "&lt;&gt;");
280            mw.end();
281            
282            assertOutput("<span class=\"\" type=\"&lt;&gt;\"></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    }