001    // Copyright May 9, 2006 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    package org.apache.tapestry.services.impl;
015    
016    import org.apache.hivemind.Location;
017    import org.apache.tapestry.*;
018    import org.apache.tapestry.asset.AssetFactory;
019    import org.apache.tapestry.engine.IEngineService;
020    import org.apache.tapestry.engine.ILink;
021    import org.apache.tapestry.engine.NullWriter;
022    import org.apache.tapestry.markup.MarkupFilter;
023    import org.apache.tapestry.markup.MarkupWriterImpl;
024    import org.apache.tapestry.markup.MarkupWriterSource;
025    import org.apache.tapestry.markup.UTFMarkupFilter;
026    import org.apache.tapestry.services.Infrastructure;
027    import org.apache.tapestry.services.RequestLocaleManager;
028    import org.apache.tapestry.services.ResponseBuilder;
029    import org.apache.tapestry.services.ServiceConstants;
030    import org.apache.tapestry.util.ContentType;
031    import org.apache.tapestry.web.WebResponse;
032    import static org.easymock.EasyMock.*;
033    import org.testng.annotations.AfterClass;
034    import org.testng.annotations.Test;
035    
036    import java.io.CharArrayWriter;
037    import java.io.IOException;
038    import java.io.PrintWriter;
039    import java.util.ArrayList;
040    import java.util.Collections;
041    import java.util.List;
042    
043    
044    /**
045     * Tests functionality of {@link DojoAjaxResponseBuilder}.
046     */
047    @SuppressWarnings("cast")
048    @Test(sequential=true)
049    public class DojoAjaxResponseBuilderTest extends BaseComponentTestCase
050    {
051    
052        private static CharArrayWriter _writer;
053    
054        private PrintWriter newPrintWriter()
055        {
056            _writer = new CharArrayWriter();
057    
058            return new PrintWriter(_writer);
059        }
060    
061        @AfterClass
062        protected void cleanup() throws Exception
063        {
064            _writer = null;
065        }
066    
067        private void assertOutput(String expected)
068        {
069            assertEquals(_writer.toString(), expected);
070    
071            _writer.reset();
072        }
073    
074        public void test_Null_Render()
075        {
076            IRender render = newMock(IRender.class);
077            IRequestCycle cycle = newMock(IRequestCycle.class);
078            IMarkupWriter writer = newWriter();
079    
080            ResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, null);
081    
082            render.render(NullWriter.getSharedInstance(), cycle);
083    
084            replay();
085    
086            builder.render(writer, render, cycle);
087    
088            verify();
089    
090            assertSame(builder.getWriter(), writer);
091        }
092    
093        public void test_Normal_Render()
094        {
095            IRender render = newMock(IRender.class);
096            IRequestCycle cycle = newMock(IRequestCycle.class);
097            IMarkupWriter writer = newMock(IMarkupWriter.class);
098    
099            ResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, null);
100    
101            render.render(NullWriter.getSharedInstance(), cycle);
102    
103            replay();
104    
105            builder.render(null, render, cycle);
106    
107            verify();
108    
109            assertSame(builder.getWriter(), writer);
110        }
111    
112        public void test_Null_Contains()
113        {
114            IRequestCycle cycle = newMock(IRequestCycle.class);
115            IMarkupWriter writer = newWriter();
116    
117            ResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, null);
118    
119            replay();
120    
121            builder.isBodyScriptAllowed(null);
122    
123            verify();
124        }
125    
126        public void test_Partial_Render()
127        {
128            IRender render = newMock(IRender.class);
129    
130            IComponent comp1 = newMock(IComponent.class);
131            IRequestCycle cycle = newMock(IRequestCycle.class);
132            IMarkupWriter writer = newMock(IMarkupWriter.class);
133            NestedMarkupWriter nested = newMock(NestedMarkupWriter.class);
134    
135            Infrastructure infra = newMock(Infrastructure.class);
136    
137            List parts = new ArrayList();
138            parts.add("id1");
139    
140            DojoAjaxResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, parts);
141    
142            render.render(NullWriter.getSharedInstance(), cycle);
143    
144            expect(comp1.getClientId()).andReturn("id1").anyTimes();
145            expect(comp1.peekClientId()).andReturn("id1").anyTimes();
146            expect(cycle.getInfrastructure()).andReturn(infra);
147            expect(infra.getOutputEncoding()).andReturn("UTF-8");
148    
149            writer.printRaw("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
150            writer.printRaw("<!DOCTYPE html "
151                            + "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
152                            + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE
153                            + "<!ENTITY nbsp '&#160;'>" + NEWLINE
154                            + "]>" + NEWLINE);
155            writer.printRaw("<ajax-response>");
156    
157            expect(writer.getNestedWriter()).andReturn(nested);
158    
159            nested.begin("response");
160            nested.attribute("id", "id1");
161            nested.attribute("type", ResponseBuilder.ELEMENT_TYPE);
162    
163            comp1.render(nested, cycle);
164    
165            replay();
166    
167            builder.render(null, render, cycle);
168    
169            assertTrue(builder.contains(comp1));
170            assertTrue(IComponent.class.isInstance(comp1));
171    
172            builder.render(null, comp1, cycle);
173    
174            verify();
175    
176            assertSame(builder.getWriter(), writer);
177        }
178    
179        public void test_Page_Render()
180        {
181            IPage page = newMock(IPage.class);
182            checkOrder(page, false);
183    
184            IRequestCycle cycle = newMock(IRequestCycle.class);
185            RequestLocaleManager rlm = newMock(RequestLocaleManager.class);
186    
187            MarkupWriterSource mrs = newMock(MarkupWriterSource.class);
188            WebResponse resp = newMock(WebResponse.class);
189            AssetFactory assetFactory = newMock(AssetFactory.class);
190            IEngineService pageService = newEngineService();
191    
192            List errorPages = new ArrayList();
193    
194            List parts = new ArrayList();
195            parts.add("id1");
196    
197            expect(page.getPageName()).andReturn("RequestPage").anyTimes();
198            expect(cycle.getParameter(ServiceConstants.PAGE)).andReturn("RequestPage").anyTimes();
199            expect(page.peekClientId()).andReturn("pageId");
200    
201            expect(cycle.renderStackIterator()).andReturn(Collections.EMPTY_LIST.iterator());
202    
203            page.render(NullWriter.getSharedInstance(), cycle);
204    
205            replay();
206    
207            DojoAjaxResponseBuilder builder =
208              new DojoAjaxResponseBuilder(cycle, rlm, mrs, resp, errorPages, assetFactory, "", pageService);
209    
210            builder.render(null, page, cycle);
211    
212            verify();
213        }
214    
215        public void test_New_Page_Render()
216        {
217            IPage page = newMock(IPage.class);
218            checkOrder(page, false);
219    
220            IRequestCycle cycle = newMock(IRequestCycle.class);
221            IMarkupWriter writer = newMock(IMarkupWriter.class);
222            NestedMarkupWriter nwriter = newNestedWriter();
223    
224            ILink link = newMock(ILink.class);
225    
226            RequestLocaleManager rlm = newMock(RequestLocaleManager.class);
227            MarkupWriterSource mrs = newMock(MarkupWriterSource.class);
228            WebResponse resp = newMock(WebResponse.class);
229            AssetFactory assetFactory = newMock(AssetFactory.class);
230            IEngineService pageService = newEngineService();
231    
232            List errorPages = new ArrayList();
233    
234            List parts = new ArrayList();
235            parts.add("id1");
236    
237            expect(page.getPageName()).andReturn("RequestPage").anyTimes();
238            expect(cycle.getParameter(ServiceConstants.PAGE)).andReturn("anotherPage").anyTimes();
239    
240            expect(writer.getNestedWriter()).andReturn(nwriter);
241            nwriter.begin("response");
242            nwriter.attribute("type", ResponseBuilder.PAGE_TYPE);
243    
244            expect(pageService.getLink(true, "RequestPage")).andReturn(link);
245            expect(link.getAbsoluteURL()).andReturn("/new/url");
246    
247            nwriter.attribute("url", "/new/url");
248    
249            replay();
250    
251            DojoAjaxResponseBuilder builder =  new DojoAjaxResponseBuilder(cycle, rlm, mrs, resp, errorPages, assetFactory, "", pageService);
252            builder.setWriter(writer);
253    
254            builder.render(null, page, cycle);
255    
256            verify();
257        }
258    
259        private static final String NEWLINE = System.getProperty("line.separator");
260    
261        public void test_Render_Response_Already_Started()
262          throws IOException
263        {
264            IPage page = newMock(IPage.class);
265            checkOrder(page, false);
266            
267            IRequestCycle cycle = newMock(IRequestCycle.class);
268            Infrastructure infra = newMock(Infrastructure.class);
269            IMarkupWriter writer = newBufferWriter();
270    
271            Location l = newLocation();
272    
273            RequestLocaleManager rlm = newMock(RequestLocaleManager.class);
274            MarkupWriterSource mrs = newMock(MarkupWriterSource.class);
275            WebResponse resp = newMock(WebResponse.class);
276            AssetFactory assetFactory = newMock(AssetFactory.class);
277            IEngineService pageService = newEngineService();
278    
279            List errorPages = new ArrayList();
280    
281            List parts = new ArrayList();
282            parts.add("id1");
283    
284            PrintWriter pw = newPrintWriter();
285            
286            rlm.persistLocale();
287            expect(cycle.getInfrastructure()).andReturn(infra).anyTimes();
288            expect(infra.getOutputEncoding()).andReturn(("UTF-8")).anyTimes();
289            expect(cycle.getParameters("updateParts")).andReturn((String[])parts.toArray(new String[parts.size()]));
290            expect(resp.getPrintWriter(isA(ContentType.class))).andReturn(pw);
291            expect(mrs.newMarkupWriter(eq(pw), isA(ContentType.class))).andReturn(writer);
292    
293            expect(cycle.getAttribute(TapestryUtils.PAGE_RENDER_SUPPORT_ATTRIBUTE)).andReturn(null);
294            cycle.setAttribute(eq(TapestryUtils.PAGE_RENDER_SUPPORT_ATTRIBUTE), isA(PageRenderSupport.class));
295    
296            cycle.renderPage(isA(DojoAjaxResponseBuilder.class));
297            
298            // only done to simulate a caught internal stale link / other
299            // exception that would cause a new renderPage() request
300            
301            expectLastCall().andThrow(new RedirectException("redir"));
302    
303            cycle.renderPage(isA(DojoAjaxResponseBuilder.class));
304            cycle.removeAttribute(TapestryUtils.PAGE_RENDER_SUPPORT_ATTRIBUTE);
305    
306            replay();
307    
308            DojoAjaxResponseBuilder builder =  new DojoAjaxResponseBuilder(cycle, rlm, mrs, resp, errorPages, assetFactory, "", pageService);
309    
310            try
311            {
312                builder.renderResponse(cycle);
313                unreachable();
314            } catch (RedirectException e)
315            {
316                IMarkupWriter fakeWriter = builder.getComponentWriter("fakeComp");
317                fakeWriter.beginEmpty("div");
318                fakeWriter.attribute("id", "fakeComp");
319    
320                builder.renderResponse(cycle);
321            }
322    
323            verify();
324    
325            assertBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " +
326                         "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE +
327                         "<!ENTITY nbsp '&#160;'>" + NEWLINE +
328                         "]>" + NEWLINE +
329                         "<ajax-response></ajax-response>");
330        }
331    
332        public void test_Allowed_Scripts()
333        {
334            IRequestCycle cycle = newMock(IRequestCycle.class);
335            IComponent comp = newMock(IComponent.class);
336            IMarkupWriter writer = newWriter();
337    
338            List parts = new ArrayList();
339            parts.add("comp1");
340    
341            ResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, parts);
342    
343            expect(comp.getClientId()).andReturn("comp");
344    
345            expect(cycle.renderStackIterator()).andReturn(Collections.EMPTY_LIST.iterator());
346    
347            expect(comp.getClientId()).andReturn("comp1");
348            expect(comp.getClientId()).andReturn("comp");
349    
350            expect(cycle.renderStackIterator()).andReturn(Collections.EMPTY_LIST.iterator());
351    
352            replay();
353    
354            assertFalse(builder.isBodyScriptAllowed(comp));
355            assertTrue(builder.isExternalScriptAllowed(comp));
356            assertFalse(builder.isInitializationScriptAllowed(comp));
357    
358            verify();
359        }
360    
361        public void test_Script_Contains_Stack()
362        {
363            IRequestCycle cycle = newMock(IRequestCycle.class);
364            IComponent comp = newMock(IComponent.class);
365            checkOrder(comp, false);
366    
367            IMarkupWriter writer = newWriter();
368    
369            List parts = new ArrayList();
370            parts.add("comp1");
371    
372            List stack = new ArrayList();
373            stack.add(comp);
374    
375            ResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, writer, parts);
376    
377            expect(comp.getClientId()).andReturn("comp").anyTimes();
378    
379            expect(cycle.renderStackIterator()).andReturn(stack.iterator()).anyTimes();
380    
381            replay();
382    
383            assertFalse(builder.isBodyScriptAllowed(comp));
384            assertFalse(builder.isExternalScriptAllowed(comp));
385            assertFalse(builder.isInitializationScriptAllowed(comp));
386    
387            verify();
388        }
389    
390        public void test_Write_Body_Script()
391        {
392            MarkupFilter filter = new UTFMarkupFilter();
393            PrintWriter writer = newPrintWriter();
394            IRequestCycle cycle = newMock(IRequestCycle.class);
395            Infrastructure inf = newMock(Infrastructure.class);
396    
397            replay();
398    
399            IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter);
400            DojoAjaxResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, mw, null);
401    
402            String bscript = "var e=4;";
403            String imageInit = "image initializations";
404            String preload = "preloadedvarname";
405    
406            verify();
407    
408            expect(cycle.getInfrastructure()).andReturn(inf);
409            expect(inf.getOutputEncoding()).andReturn("UTF-8");
410    
411            replay();
412    
413            builder.beginResponse();
414            builder.beginBodyScript(mw, cycle);
415            builder.writeImageInitializations(mw, imageInit, preload, cycle);
416            builder.writeBodyScript(mw, bscript, cycle);
417            builder.endBodyScript(mw, cycle);
418            builder.endResponse();
419    
420            assertOutput("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE +
421                         "<!ENTITY nbsp \'&#160;\'>" + NEWLINE +
422                         "]>" + NEWLINE +
423                         "<ajax-response><response id=\"bodyscript\" type=\"script\"><script>" + NEWLINE +
424                         "//<![CDATA[" + NEWLINE +
425                         NEWLINE +
426                         "preloadedvarname = [];" + NEWLINE +
427                         "if (document.images) {" + NEWLINE +
428                         "image initializations}" + NEWLINE +
429                         "var e=4;" + NEWLINE +
430                         "//]]>" + NEWLINE +
431                         "</script></response></ajax-response>");
432    
433            verify();
434        }
435    
436        public void test_Write_External_Scripts()
437        {
438            MarkupFilter filter = new UTFMarkupFilter();
439            PrintWriter writer = newPrintWriter();
440            IRequestCycle cycle = newMock(IRequestCycle.class);
441            Infrastructure inf = newMock(Infrastructure.class);
442    
443            replay();
444    
445            IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter);
446            DojoAjaxResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, mw, null);
447    
448            String script1 = "http://noname/js/package.js";
449            String script2 = "http://noname/js/package2.js";
450    
451            verify();
452    
453            expect(cycle.getInfrastructure()).andReturn(inf);
454            expect(inf.getOutputEncoding()).andReturn("UTF-8");
455    
456            replay();
457    
458            builder.beginResponse();
459            builder.writeExternalScript(mw, script1, cycle);
460            builder.writeExternalScript(mw, script2, cycle);
461            builder.endResponse();
462    
463            assertOutput("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE +
464                         "<!ENTITY nbsp \'&#160;\'>" + NEWLINE +
465                         "]>" + NEWLINE +
466                         "<ajax-response><response id=\"includescript\" type=\"script\">"
467                         + "<include url=\"http://noname/js/package.js\" />"
468                         + "<include url=\"http://noname/js/package2.js\" /></response></ajax-response>");
469    
470            verify();
471        }
472    
473        public void test_Write_Initialization_Script()
474        {
475            IRequestCycle cycle = newMock(IRequestCycle.class);
476            MarkupFilter filter = new UTFMarkupFilter();
477            PrintWriter writer = newPrintWriter();
478            Infrastructure inf = newMock(Infrastructure.class);
479    
480            replay();
481    
482            IMarkupWriter mw = new MarkupWriterImpl("text/html", writer, filter);
483            DojoAjaxResponseBuilder builder = new DojoAjaxResponseBuilder(cycle, mw, null);
484    
485            String script = "doThisInInit();";
486    
487            verify();
488    
489            expect(cycle.getInfrastructure()).andReturn(inf);
490            expect(inf.getOutputEncoding()).andReturn("UTF-8");
491    
492            replay();
493    
494            builder.beginResponse();
495            builder.writeInitializationScript(mw, script);
496            builder.endResponse();
497    
498            assertOutput("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" [" + NEWLINE +
499                         "<!ENTITY nbsp \'&#160;\'>" + NEWLINE +
500                         "]>" + NEWLINE +
501                         "<ajax-response><response id=\"initializationscript\" type=\"script\"><script>" + NEWLINE +
502                         "//<![CDATA[" + NEWLINE +
503                         "doThisInInit();" + NEWLINE +
504                         "//]]>" + NEWLINE +
505                         "</script></response></ajax-response>");
506    
507            verify();
508        }
509    }