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 ' '>" + 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 ' '>" + 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 \' \'>" + 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 \' \'>" + 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 \' \'>" + 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 }