/* * Copyright (C) 2011 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.portal.resource; import org.exoplatform.commons.utils.PropertyManager; import org.exoplatform.component.test.AbstractKernelTest; import org.exoplatform.component.test.ConfigurationUnit; import org.exoplatform.component.test.ConfiguredBy; import org.exoplatform.component.test.ContainerScope; import org.exoplatform.component.test.web.ServletContextImpl; import org.exoplatform.component.test.web.WebAppImpl; import org.exoplatform.container.PortalContainer; import org.exoplatform.portal.resource.config.tasks.SkinConfigTask; import org.exoplatform.services.resources.Orientation; import org.exoplatform.test.mocks.servlet.MockServletRequest; import org.exoplatform.web.ControllerContext; import org.exoplatform.web.controller.QualifiedName; import org.exoplatform.web.controller.router.Router; import org.w3c.dom.Document; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.ServletContext; @ConfiguredBy({ @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/test-configuration.xml") }) public abstract class AbstractSkinServiceTest extends AbstractKernelTest { // The value must be the same with the version property in // META-INF/maven/org.gatein.portal/exo.portal.component.web.resources/pom.properties protected static final String ASSETS_VERSION = "PORTAL-VERSION"; protected SkinService skinService; protected ControllerContext controllerCtx; protected List<SkinKey> skinsToDelete = new ArrayList<SkinKey>(); private static ServletContext mockServletContext; protected static MockResourceResolver resResolver; abstract boolean isDevelopingMode(); abstract Router getRouter(); abstract boolean setUpTestEnvironment(); abstract void touchSetUp(); @Override protected void setUp() throws Exception { // Set running mode at starting up PropertyManager.setProperty(PropertyManager.DEVELOPING, String.valueOf(isDevelopingMode())); PortalContainer portalContainer = getContainer(); skinService = portalContainer.getComponentInstanceOfType(SkinService.class); controllerCtx = getControllerContext(); if (setUpTestEnvironment()) { URL base = AbstractSkinServiceTest.class.getClassLoader().getResource("mockwebapp"); File f = new File(base.toURI()); assertTrue(f.exists()); assertTrue(f.isDirectory()); mockServletContext = new ServletContextImpl(f, "/mockwebapp", "mockwebapp"); skinService.registerContext(new WebAppImpl(mockServletContext, Thread.currentThread().getContextClassLoader())); resResolver = new MockResourceResolver(); skinService.addResourceResolver(resResolver); URL url = mockServletContext.getResource("/gatein-resources.xml"); Document document = GateInResourcesSchemaValidator.validate(url); final List<SkinConfigTask> skinTasks = SkinConfigParser.parse(document); skinService.addSkins(skinTasks, mockServletContext); // touchSetUp(); } } protected void tearDown() throws Exception { resResolver.reset(); for(SkinKey key : skinsToDelete) { skinService.removeSupportedSkin(key.getName()); skinService.removeSkin(key); } skinsToDelete.clear(); skinService.reloadSkins(); } public void testInitializing() throws Exception { assertEquals(1, skinService.getAvailableSkinNames().size()); assertTrue(skinService.getAvailableSkinNames().contains("TestSkin")); assertTrue(skinService.mainResolver.resolvers.contains(resResolver)); } public void testPortalSkinAndPriority() { Collection<SkinConfig> portalSkinConfigs = skinService.getPortalSkins("TestSkin"); String contextPath = mockServletContext.getContextPath(); assertNotNull(portalSkinConfigs); assertEquals(4, portalSkinConfigs.size()); SkinConfig[] array = new SkinConfig[4]; portalSkinConfigs.toArray(array); SkinConfig portalSkin = array[0]; assertNotNull(portalSkin); assertEquals("CoreSkin", portalSkin.getModule()); assertEquals(contextPath + "/skin/core/Stylesheet.css", portalSkin.getCSSPath()); portalSkin = array[1]; assertNotNull(portalSkin); assertEquals("Module2", portalSkin.getModule()); assertEquals(contextPath + "/skin/module2/Stylesheet.css", portalSkin.getCSSPath()); portalSkin = array[2]; assertNotNull(portalSkin); assertEquals("Module3", portalSkin.getModule()); assertEquals(contextPath + "/skin/module3/Stylesheet.css", portalSkin.getCSSPath()); portalSkin = array[3]; assertNotNull(portalSkin); assertEquals("Module1", portalSkin.getModule()); assertEquals(contextPath + "/skin/module1/Stylesheet.css", portalSkin.getCSSPath()); } public void testPortalSkinVisitor() { String contextPath = mockServletContext.getContextPath(); Collection<SkinConfig> portalSkinConfigs = skinService.findSkins(new SkinVisitor() { Collection<SkinConfig> list = new HashSet<SkinConfig>(); @Override public void visitPortalSkin(Entry<SkinKey, SkinConfig> entry) { if (entry.getKey().getModule().equals("CoreSkin")) { list.add(entry.getValue()); } } @Override public void visitSkin(Entry<SkinKey, SkinConfig> entry) { } @Override public Collection<SkinConfig> getSkins() { return list; } }); assertNotNull(portalSkinConfigs); assertEquals(1, portalSkinConfigs.size()); SkinConfig[] arr = portalSkinConfigs.toArray(new SkinConfig[1]); SkinConfig portalSkin = arr[0]; assertNotNull(portalSkin); assertEquals("CoreSkin", portalSkin.getModule()); assertEquals(contextPath + "/skin/core/Stylesheet.css", portalSkin.getCSSPath()); } public void testPortletSkin() { SkinConfig portletSkin = skinService.getSkin("mockwebapp/FirstPortlet", "TestSkin"); String contextPath = mockServletContext.getContextPath(); assertNotNull(portletSkin); assertEquals(contextPath + "/skin/FirstPortlet.css", portletSkin.getCSSPath()); portletSkin = skinService.getSkin("mockwebapp/SecondPortlet", "TestSkin"); assertNotNull(portletSkin); assertEquals(contextPath + "/skin/SecondPortlet.css", portletSkin.getCSSPath()); } public void testThemes() { Map<String, Set<String>> themeStyles = skinService.getPortletThemes(); Set<String> themes = themeStyles.get("Simple"); assertNotNull(themes); assertTrue(themes.contains("SimpleBlue")); assertTrue(themes.contains("SimpleViolet")); assertNotNull(themeStyles.get("VistaStyle")); } public void testExistingCSS() throws Exception { SkinURL skinURL = newSimpleSkin(mockServletContext.getContextPath() + "/skin/Stylesheet.css").createURL(controllerCtx); String css = skinService.getCSS(newControllerContext(getRouter(), skinURL.toString()), false); assertTrue(css.length() > 0); skinURL.setOrientation(Orientation.RT); String uri = skinURL.toString(); css = skinService.getCSS(newControllerContext(getRouter(), uri), false); assertTrue(css.length() > 0); } public void testNonExistingCSS() throws Exception { SkinURL skinURL = newSimpleSkin("/non/existing/file.css").createURL(controllerCtx); String css = skinService.getCSS(newControllerContext(getRouter(), skinURL.toString()), false); assertNull(css); skinURL.setOrientation(Orientation.RT); String uri = skinURL.toString(); css = skinService.getCSS(newControllerContext(getRouter(), uri), false); assertNull(css); } public void testProcessComment() throws Exception { String resource = "/process/comment/file.css"; String css = "foo; /*background:url(bar.gif); Inline comment*/" + "/* Block comment\n" + " background:url(bar.gif);\n" + " End of block comment */"; resResolver.addResource(resource, css); // TODO: It should only ignore the comment instead of removing it // assertEquals( // "foo; /*background:url(bar.gif); Inline comment*/" + // "/* Block comment\n" + // " background:url(bar.gif);\n" + // " End of block comment */", // skinService.getCSS(controllerCtx, path, false)); SkinURL skinURL = newSimpleSkin(resource).createURL(controllerCtx); assertEquals("foo;", skinService.getCSS(newControllerContext(getRouter(), skinURL.toString()), false)); } public void testOrientation() throws Exception { String resource = "/orientation/file.css"; SkinURL skinURL = newSimpleSkin(resource).createURL(controllerCtx); String uri = skinURL.toString(); String css = "aaa;/*orientation=lt*/bbb;/*orientation=rt*/\n" + " aaa; /* orientation=lt */ bbb; /* orientation=rt */ \n" + "{aaa;bbb;/*orientation=lt*/ccc;ddd;/*orientation=rt*/}\n" + "{aaa;/*orientation=lt*/bbb;}{ccc;/*orientation=rt*/ddd;}"; resResolver.addResource(resource, css); assertEquals("aaa;\n" + "aaa;\n" + "{aaa;bbb;/*orientation=lt*/ccc;}\n" + "{aaa;/*orientation=lt*/bbb;}{ddd;}", skinService.getCSS(newControllerContext(getRouter(), uri), false)); skinURL.setOrientation(Orientation.RT); uri = skinURL.toString(); assertEquals("bbb;/*orientation=rt*/\n" + " bbb; /* orientation=rt */\n" + "{aaa;ccc;ddd;/*orientation=rt*/}\n" + "{bbb;}{ccc;/*orientation=rt*/ddd;}", skinService.getCSS(newControllerContext(getRouter(), uri), false)); } public void testBackgroundURL() throws Exception { String resource = "/background/url/file.css"; String css = "background:url(images/foo.gif);\n" + "background:url('/images/foo.gif');\n" + "aaa; background: #fff url('images/foo.gif') no-repeat center -614px; ccc;\n" + "background-image: url(images/foo.gif), url(images/bar.gif);"; resResolver.addResource(resource, css); SkinURL skinURL = newSimpleSkin(resource).createURL(controllerCtx); assertEquals("background:url(/background/url/images/foo.gif);\n" + "background:url('/images/foo.gif');\n" + "aaa; background: #fff url('/background/url/images/foo.gif') no-repeat center -614px; ccc;\n" + "background-image: url(/background/url/images/foo.gif), url(/background/url/images/bar.gif);", skinService.getCSS(newControllerContext(getRouter(), skinURL.toString()), false)); } public void testFontFaceURL() throws Exception { String resource = "/fonts/url/file.css"; String css = "src: local('foo'), url('fonts/foo.woff') format('woff'), format('truetype') url(fonts/bar.ttf);\n" +"src: url('fonts/baz.eot');\n" +"src:url('fonts/bat.svg#batFont');"; resResolver.addResource(resource, css); SkinURL skinURL = newSimpleSkin(resource).createURL(controllerCtx); assertEquals("src: local('foo'), url('/fonts/url/fonts/foo.woff') format('woff'), format('truetype') url(/fonts/url/fonts/bar.ttf);\n" +"src: url('/fonts/url/fonts/baz.eot');\n" +"src:url('/fonts/url/fonts/bat.svg#batFont');", skinService.getCSS(newControllerContext(getRouter(), skinURL.toString()), false)); } public void testCustomSkinKey() { skinService.addSkin("jcr/foo", "bar", "/path/to/customkey.css", -1, false); skinsToDelete.add(new SkinKey("jcr/foo", "bar")); SkinConfig skin = skinService.getSkin("foo", "bar"); assertNull(skin); skin = skinService.getSkin("jcr/foo", "bar"); assertNotNull(skin); assertEquals("/path/to/customkey.css", skin.getCSSPath()); Collection<SkinConfig> list = skinService.findSkins(new SkinVisitor() { Collection<SkinConfig> list = new HashSet<SkinConfig>(); @Override public void visitPortalSkin(Entry<SkinKey, SkinConfig> entry) { if (entry.getKey().getModule().startsWith("jcr/")) { list.add(entry.getValue()); } } @Override public void visitSkin(Entry<SkinKey, SkinConfig> entry) { if (entry.getKey().getModule().startsWith("jcr/")) { list.add(entry.getValue()); } } @Override public Collection<SkinConfig> getSkins() { return list; } }); assertNotNull(list); assertEquals(1, list.size()); SkinConfig next = list.iterator().next(); assertEquals("/path/to/customkey.css", next.getCSSPath()); } ControllerContext getControllerContext() { try { return newControllerContext(getRouter()); } catch (Exception e) { throw new IllegalArgumentException("The controller context is not initialized properly", e); } } public static ControllerContext newControllerContext(Router router) { return newControllerContext(router, "/portal"); } public static ControllerContext newControllerContext(Router router, String requestURI) { try { MockServletRequest request = new MockServletRequest(null, new URL("http://localhost" + requestURI), "/portal", null, false); String portalPath = request.getRequestURI().substring(request.getContextPath().length()); // @SuppressWarnings("unchecked") Iterator<Map<QualifiedName, String>> matcher = router.matcher(portalPath, request.getParameterMap()); Map<QualifiedName, String> parameters = null; if (matcher.hasNext()) { parameters = matcher.next(); } return new ControllerContext(null, router, request, null, parameters); } catch (MalformedURLException e) { return null; } } public SimpleSkin newSimpleSkin(String uri) { return new SimpleSkin(skinService, "module", null, uri); } }