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.link;
016    
017    import static org.easymock.EasyMock.expect;
018    
019    import org.apache.hivemind.ApplicationRuntimeException;
020    import org.apache.hivemind.Location;
021    import org.apache.tapestry.BaseComponentTestCase;
022    import org.apache.tapestry.IMarkupWriter;
023    import org.apache.tapestry.IRequestCycle;
024    import org.apache.tapestry.NestedMarkupWriter;
025    import org.apache.tapestry.Tapestry;
026    import org.apache.tapestry.components.ILinkComponent;
027    import org.apache.tapestry.engine.ILink;
028    import org.testng.annotations.Test;
029    
030    /**
031     * Tests for {@link org.apache.tapestry.link.DefaultLinkRenderer}.
032     * 
033     * @author Howard M. Lewis Ship
034     * @since 4.0
035     */
036    @Test
037    public class DefaultLinkRendererTest extends BaseComponentTestCase
038    {
039        class RendererFixture extends DefaultLinkRenderer
040        {
041            private IMarkupWriter _writer;
042    
043            private IRequestCycle _cycle;
044    
045            private ILinkComponent _component;
046    
047            private String _element;
048    
049            private boolean _hasBody;
050    
051            private String _targetAttribute;
052    
053            private String _urlAttribute;
054    
055            public RendererFixture(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent component,
056                    String element, boolean body, String targetAttribute, String urlAttribute)
057            {
058                _writer = writer;
059                _cycle = cycle;
060                _component = component;
061                _element = element;
062                _hasBody = body;
063                _targetAttribute = targetAttribute;
064                _urlAttribute = urlAttribute;
065            }
066    
067            @Override
068            protected void afterBodyRender(IMarkupWriter writer, IRequestCycle cycle,
069                    ILinkComponent link)
070            {
071                assertSame(_writer, writer);
072                assertSame(_cycle, cycle);
073                assertSame(_component, link);
074    
075                writer.print("AFTER-BODY-RENDER");
076            }
077    
078            @Override
079            protected void beforeBodyRender(IMarkupWriter writer, IRequestCycle cycle,
080                    ILinkComponent link)
081            {
082                assertSame(_writer, writer);
083                assertSame(_cycle, cycle);
084                assertSame(_component, link);
085    
086                writer.print("BEFORE-BODY-RENDER");
087            }
088    
089            @Override
090            protected String getElement()
091            {
092                return _element;
093            }
094    
095            @Override
096            protected boolean getHasBody()
097            {
098                return _hasBody;
099            }
100    
101            @Override
102            protected String getTargetAttribute()
103            {
104    
105                return _targetAttribute;
106            }
107    
108            @Override
109            protected String getUrlAttribute()
110            {
111                return _urlAttribute;
112            }
113    
114        }
115    
116        protected ILinkComponent newComponent()
117        {
118            return newMock(ILinkComponent.class);
119        }
120    
121        public void testNoNesting()
122        {
123            IMarkupWriter writer = newWriter();
124            IRequestCycle cycle = newCycle();
125            ILinkComponent existing = newComponent();
126            ILinkComponent active = newComponent();
127            Location l = newLocation();
128    
129            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, existing);
130    
131            trainGetLocation(active, l);
132    
133            replay();
134    
135            try
136            {
137                new DefaultLinkRenderer().renderLink(writer, cycle, active);
138                unreachable();
139            }
140            catch (ApplicationRuntimeException ex)
141            {
142                assertEquals(LinkMessages.noNesting(), ex.getMessage());
143                assertSame(active, ex.getComponent());
144                assertSame(l, ex.getLocation());
145            }
146    
147            verify();
148        }
149    
150        public void testStandardNotDisabled()
151        {
152            IMarkupWriter writer = newWriter();
153            NestedMarkupWriter nested = newNestedWriter();
154            IRequestCycle cycle = newCycle();
155            ILinkComponent component = newComponent();
156            ILink link = newLink();
157    
158            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
159            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
160    
161            trainIsDisabled(component, false);
162            trainIsRewinding(cycle, false);
163    
164            writer.begin("a");
165            
166            component.renderAdditionalAttributes(writer, cycle);
167            
168            trainGetLink(component, cycle, link);
169    
170            trainGetScheme(component, null);
171            trainGetPort(component, null);
172            trainGetAnchor(component, null);
173    
174            trainGetURL(link, null, null, "/foo/bar.baz");
175    
176            writer.attribute("href", "/foo/bar.baz");
177    
178            trainGetTarget(component, null);
179    
180            trainGetNestedWriter(writer, nested);
181    
182            component.renderBody(nested, cycle);
183    
184            nested.close();
185    
186            writer.end();
187    
188            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
189    
190            replay();
191    
192            new DefaultLinkRenderer().renderLink(writer, cycle, component);
193    
194            verify();
195        }
196    
197        protected void trainGetScheme(ILinkComponent component, String scheme)
198        {
199            expect(component.getScheme()).andReturn(scheme);
200        }
201        
202        protected void trainGetPort(ILinkComponent component, Integer port)
203        {
204            expect(component.getPort()).andReturn(port);
205        }
206        
207        public void testStandardWithSchemaAnchorAndTarget()
208        {
209            IMarkupWriter writer = newWriter();
210            NestedMarkupWriter nested = newNestedWriter();
211            IRequestCycle cycle = newCycle();
212            ILinkComponent component = newComponent();
213            ILink link = newLink();
214    
215            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
216            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
217    
218            trainIsDisabled(component, false);
219            trainIsRewinding(cycle, false);
220    
221            writer.begin("a");
222            
223            component.renderAdditionalAttributes(writer, cycle);
224            
225            trainGetLink(component, cycle, link);
226    
227            trainGetScheme(component, "https");
228    
229            trainGetPort(component, null);
230            
231            trainGetAnchor(component, "my-anchor");
232    
233            trainGetURL(link, "https", "my-anchor", "http://zap.com/foo/bar.baz#my-anchor");
234    
235            writer.attribute("href", "http://zap.com/foo/bar.baz#my-anchor");
236    
237            trainGetTarget(component, "some-target");
238    
239            writer.attribute("target", "some-target");
240    
241            trainGetNestedWriter(writer, nested);
242    
243            component.renderBody(nested, cycle);
244            
245            nested.close();
246            
247            writer.end();
248            
249            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
250    
251            replay();
252    
253            new DefaultLinkRenderer().renderLink(writer, cycle, component);
254    
255            verify();
256        }
257    
258        public void testDisabled()
259        {
260            IMarkupWriter writer = newWriter();
261            IRequestCycle cycle = newCycle();
262            ILinkComponent component = newComponent();
263    
264            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
265            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
266    
267            trainIsDisabled(component, true);
268    
269            component.renderBody(writer, cycle);
270    
271            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
272    
273            replay();
274    
275            new DefaultLinkRenderer().renderLink(writer, cycle, component);
276    
277            verify();
278        }
279    
280        public void testRewinding()
281        {
282            IMarkupWriter writer = newWriter();
283            IRequestCycle cycle = newCycle();
284            ILinkComponent component = newComponent();
285    
286            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
287            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
288    
289            trainIsDisabled(component, false);
290            trainIsRewinding(cycle, true);
291    
292            component.renderBody(writer, cycle);
293    
294            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
295    
296            replay();
297    
298            new DefaultLinkRenderer().renderLink(writer, cycle, component);
299    
300            verify();
301        }
302    
303        public void testWithSubclass()
304        {
305            IMarkupWriter writer = newWriter();
306            NestedMarkupWriter nested = newNestedWriter();
307            IRequestCycle cycle = newCycle();
308            ILinkComponent component = newComponent();
309            ILink link = newLink();
310    
311            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
312            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
313    
314            trainIsDisabled(component, false);
315            trainIsRewinding(cycle, false);
316    
317            writer.begin("xlink");
318            
319            component.renderAdditionalAttributes(writer, cycle);
320            
321            trainGetLink(component, cycle, link);
322    
323            trainGetScheme(component, null);
324    
325            trainGetPort(component, null);
326            
327            trainGetAnchor(component, "my-anchor");
328    
329            trainGetURL(link, null, "my-anchor", "/foo/bar.baz#my-anchor");
330    
331            writer.attribute("xurl", "/foo/bar.baz#my-anchor");
332    
333            trainGetTarget(component, "some-target");
334    
335            writer.attribute("xtarget", "some-target");
336    
337            writer.print("BEFORE-BODY-RENDER");
338    
339            trainGetNestedWriter(writer, nested);
340    
341            component.renderBody(nested, cycle);
342    
343            writer.print("AFTER-BODY-RENDER");
344    
345            nested.close();
346    
347            writer.end();
348    
349            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
350    
351            replay();
352    
353            new RendererFixture(writer, cycle, component, "xlink", true, "xtarget", "xurl").renderLink(
354                    writer,
355                    cycle,
356                    component);
357    
358            verify();
359        }
360    
361        public void testWithSubclassNoBody()
362        {
363            IMarkupWriter writer = newWriter();
364            NestedMarkupWriter nested = newNestedWriter();
365    
366            IRequestCycle cycle = newCycle();
367            ILinkComponent component = newComponent();
368            ILink link = newLink();
369    
370            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
371            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
372    
373            trainIsDisabled(component, false);
374            trainIsRewinding(cycle, false);
375    
376            writer.beginEmpty("xlink");
377            
378            component.renderAdditionalAttributes(writer, cycle);
379            
380            trainGetLink(component, cycle, link);
381    
382            trainGetScheme(component, null);
383    
384            trainGetPort(component, null);
385            
386            trainGetAnchor(component, "my-anchor");
387    
388            trainGetURL(link, null, "my-anchor", "/foo/bar.baz#my-anchor");
389    
390            writer.attribute("xurl", "/foo/bar.baz#my-anchor");
391    
392            trainGetTarget(component, "some-target");
393    
394            writer.attribute("xtarget", "some-target");
395    
396            writer.print("BEFORE-BODY-RENDER");
397    
398            trainGetNestedWriter(writer, nested);
399    
400            writer.print("AFTER-BODY-RENDER");
401            
402            writer.closeTag();
403            
404            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
405    
406            replay();
407    
408            new RendererFixture(writer, cycle, component, "xlink", false, "xtarget", "xurl")
409                    .renderLink(writer, cycle, component);
410    
411            verify();
412        }
413    
414        public void testWithSubclassDisabled()
415        {
416            IMarkupWriter writer = newWriter();
417            IRequestCycle cycle = newCycle();
418            ILinkComponent component = newComponent();
419    
420            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
421            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
422    
423            trainIsDisabled(component, true);
424    
425            component.renderBody(writer, cycle);
426    
427            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
428    
429            replay();
430    
431            new RendererFixture(writer, cycle, component, "xlink", true, "xtarget", "xurl").renderLink(
432                    writer,
433                    cycle,
434                    component);
435    
436            verify();
437        }
438    
439        public void testWithSubclassDisabledNoBody()
440        {
441            IMarkupWriter writer = newWriter();
442            IRequestCycle cycle = newCycle();
443            ILinkComponent component = newComponent();
444    
445            trainGetAttribute(cycle, Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, null);
446            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, component);
447    
448            trainIsDisabled(component, true);
449    
450            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
451    
452            replay();
453    
454            new RendererFixture(writer, cycle, component, "xlink", false, "xtarget", "xurl")
455                    .renderLink(writer, cycle, component);
456    
457            verify();
458        }
459    
460        protected void trainGetAnchor(ILinkComponent component, String anchor)
461        {
462            expect(component.getAnchor()).andReturn(anchor);
463        }
464    
465        protected void trainGetTarget(ILinkComponent component, String target)
466        {
467            expect(component.getTarget()).andReturn(target);
468        }
469    
470        protected void trainIsDisabled(ILinkComponent component, boolean isDisabled)
471        {
472            expect(component.isDisabled()).andReturn(isDisabled);
473        }
474    }