/** * *************************************************************************** * Copyright (c) 2010 Qcadoo Limited * Project: Qcadoo Framework * Version: 1.4 * * This file is part of Qcadoo. * * Qcadoo is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************** */ package com.qcadoo.view.internal.xml; import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.test.util.ReflectionTestUtils.getField; import static org.springframework.test.util.ReflectionTestUtils.setField; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import junit.framework.Assert; import org.json.JSONObject; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import org.springframework.core.io.ClassPathResource; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.qcadoo.localization.api.TranslationService; import com.qcadoo.model.api.DataDefinition; import com.qcadoo.model.api.DataDefinitionService; import com.qcadoo.model.api.FieldDefinition; import com.qcadoo.model.api.types.BelongsToType; import com.qcadoo.model.api.types.HasManyType; import com.qcadoo.model.internal.types.StringType; import com.qcadoo.security.api.SecurityRole; import com.qcadoo.security.api.SecurityRolesService; import com.qcadoo.view.api.ribbon.RibbonActionItem; import com.qcadoo.view.beans.sample.CustomViewService; import com.qcadoo.view.internal.ViewDefinitionServiceImpl; import com.qcadoo.view.internal.ViewHookDefinition; import com.qcadoo.view.internal.api.ComponentPattern; import com.qcadoo.view.internal.api.ContextualHelpService; import com.qcadoo.view.internal.api.InternalViewDefinition; import com.qcadoo.view.internal.api.ViewDefinition; import com.qcadoo.view.internal.api.ViewDefinitionService; import com.qcadoo.view.internal.components.ButtonComponentPattern; import com.qcadoo.view.internal.components.CheckBoxComponentPattern; import com.qcadoo.view.internal.components.TextAreaComponentPattern; import com.qcadoo.view.internal.components.TextInputComponentPattern; import com.qcadoo.view.internal.components.form.FormComponentPattern; import com.qcadoo.view.internal.components.grid.GridComponentPattern; import com.qcadoo.view.internal.components.window.WindowComponentPattern; import com.qcadoo.view.internal.hooks.HookFactory; import com.qcadoo.view.internal.hooks.HookType; import com.qcadoo.view.internal.hooks.ViewConstructionHook; import com.qcadoo.view.internal.hooks.ViewEventListenerHook; import com.qcadoo.view.internal.hooks.ViewLifecycleHook; import com.qcadoo.view.internal.internal.ViewComponentsResolverImpl; import com.qcadoo.view.internal.internal.ViewHooksHolder; import com.qcadoo.view.internal.ribbon.RibbonParserService; public class ViewDefinitionParserImplTest { private ViewDefinitionParserImpl viewDefinitionParser; private DataDefinitionService dataDefinitionService; private ViewDefinitionService viewDefinitionService; private HookFactory hookFactory; private ApplicationContext applicationContext; private String xml1; private String xml2; private DataDefinition dataDefinitionA; private DataDefinition dataDefinitionB; private TranslationService translationService; private ContextualHelpService contextualHelpService; private SecurityRolesService securityRolesService; private SecurityRole userRoleMock, adminRoleMock; private static ViewComponentsResolverImpl viewComponentsResolver; @BeforeClass public static void initClass() throws Exception { viewComponentsResolver = new ViewComponentsResolverImpl(); viewComponentsResolver.register("window", WindowComponentPattern.class); viewComponentsResolver.register("form", FormComponentPattern.class); viewComponentsResolver.register("checkbox", CheckBoxComponentPattern.class); viewComponentsResolver.register("input", TextInputComponentPattern.class); viewComponentsResolver.register("textarea", TextAreaComponentPattern.class); viewComponentsResolver.register("grid", GridComponentPattern.class); viewComponentsResolver.register("button", ButtonComponentPattern.class); } @Before public void init() throws Exception { applicationContext = mock(ApplicationContext.class); dataDefinitionService = mock(DataDefinitionService.class); translationService = mock(TranslationService.class); RibbonParserService ribbonService = new RibbonParserService(); contextualHelpService = mock(ContextualHelpService.class); securityRolesService = mock(SecurityRolesService.class); viewDefinitionService = new ViewDefinitionServiceImpl(); hookFactory = new HookFactory(); setField(hookFactory, "applicationContext", applicationContext); viewDefinitionParser = new ViewDefinitionParserImpl(); setField(viewDefinitionParser, "dataDefinitionService", dataDefinitionService); setField(viewDefinitionParser, "viewDefinitionService", viewDefinitionService); setField(viewDefinitionParser, "hookFactory", hookFactory); setField(viewDefinitionParser, "translationService", translationService); setField(viewDefinitionParser, "applicationContext", applicationContext); setField(viewDefinitionParser, "contextualHelpService", contextualHelpService); setField(viewDefinitionParser, "viewComponentsResolver", viewComponentsResolver); setField(viewDefinitionParser, "ribbonService", ribbonService); setField(viewDefinitionParser, "securityRolesService", securityRolesService); userRoleMock = mock(SecurityRole.class); given(securityRolesService.getRoleByIdentifier("ROLE_USER")).willReturn(userRoleMock); adminRoleMock = mock(SecurityRole.class); given(securityRolesService.getRoleByIdentifier("ROLE_ADMIN")).willReturn(adminRoleMock); xml1 = "view/test1.xml"; xml2 = "view/test2.xml"; given(applicationContext.getBean(CustomViewService.class)).willReturn(new CustomViewService()); dataDefinitionA = mock(DataDefinition.class); dataDefinitionB = mock(DataDefinition.class); FieldDefinition nameA = mock(FieldDefinition.class, "nameA"); FieldDefinition nameB = mock(FieldDefinition.class, "nameB"); FieldDefinition hasManyB = mock(FieldDefinition.class, "hasManyB"); FieldDefinition belongToA = mock(FieldDefinition.class, "belongsToA"); HasManyType hasManyBType = mock(HasManyType.class); BelongsToType belongToAType = mock(BelongsToType.class); given(nameA.getDataDefinition()).willReturn(dataDefinitionA); given(nameA.getType()).willReturn(new StringType()); given(nameB.getType()).willReturn(new StringType()); given(nameB.getDataDefinition()).willReturn(dataDefinitionA); given(hasManyB.getType()).willReturn(hasManyBType); given(hasManyB.getDataDefinition()).willReturn(dataDefinitionB); given(belongToA.getType()).willReturn(belongToAType); given(belongToA.getDataDefinition()).willReturn(dataDefinitionB); given(hasManyBType.getDataDefinition()).willReturn(dataDefinitionB); given(belongToAType.getDataDefinition()).willReturn(dataDefinitionA); given(dataDefinitionA.getField("beansB")).willReturn(hasManyB); given(dataDefinitionA.getField("name")).willReturn(nameA); given(dataDefinitionB.getField("activeA")).willReturn(nameA); given(dataDefinitionB.getField("activeB")).willReturn(nameA); given(dataDefinitionB.getField("activeC")).willReturn(nameA); given(dataDefinitionB.getField("beanA")).willReturn(belongToA); given(dataDefinitionB.getField("beanM")).willReturn(belongToA); given(dataDefinitionB.getField("beanB")).willReturn(belongToA); given(dataDefinitionB.getField("name")).willReturn(nameB); given(dataDefinitionA.getName()).willReturn("beanA"); given(dataDefinitionB.getName()).willReturn("beanB"); given(dataDefinitionA.getFields()).willReturn(ImmutableMap.of("name", nameA, "beansB", hasManyB)); given(dataDefinitionB.getFields()).willReturn(ImmutableMap.of("name", nameB, "beanA", belongToA)); given(dataDefinitionService.get("sample", "beanA")).willReturn(dataDefinitionA); given(dataDefinitionService.get("sample", "beanB")).willReturn(dataDefinitionB); } @Test public void shouldParseXml() { // when List<InternalViewDefinition> viewDefinitions = parseAndGetViewDefinitions(); // then assertEquals(2, viewDefinitions.size()); assertNotNull(viewDefinitions.get(0)); assertNotNull(viewDefinitions.get(1)); } @Test public void shouldSetViewDefinitionAttributes() { // when InternalViewDefinition viewDefinition = parseAndGetViewDefinition(); // then assertEquals("simpleView", viewDefinition.getName()); assertEquals("sample", viewDefinition.getPluginIdentifier()); assertEquals(userRoleMock, viewDefinition.getAuthorizationRole()); assertThat(viewDefinition.getComponentByReference("mainWindow"), instanceOf(WindowComponentPattern.class)); } @Test public void shouldSetCustomAuthorizationRole() { // when InternalViewDefinition viewDefinition = parseAndGetViewDefinition(xml2); // then assertEquals(adminRoleMock, viewDefinition.getAuthorizationRole()); } @Test public void shouldShouldThrowExceptionIfCustomAuthorizationRoleCannotBeFound() { // given given(securityRolesService.getRoleByIdentifier("ROLE_ADMIN")).willReturn(null); // when & then try { parseAndGetViewDefinition(xml2); Assert.fail(); } catch (ViewDefinitionParserException ignored) { // Success } catch (Exception ignored) { Assert.fail(); } } @SuppressWarnings("unchecked") @Test public void shouldHasRibbon() throws Exception { // given InternalViewDefinition viewDefinition = parseAndGetViewDefinition(); TranslationService translationService = mock(TranslationService.class); setField(viewDefinition, "translationService", translationService); given(applicationContext.getBean(SecurityRolesService.class)).willReturn(securityRolesService); given(securityRolesService.canAccess(Mockito.any(SecurityRole.class))).willReturn(true); JSONObject jsOptions = (JSONObject) ((Map<String, Map<String, Object>>) viewDefinition.prepareView(new JSONObject(), Locale.ENGLISH).get("components")).get("mainWindow").get("jsOptions"); JSONObject ribbon = jsOptions.getJSONObject("ribbon"); // then assertNotNull(ribbon); assertEquals(2, ribbon.getJSONArray("groups").length()); assertEquals("first", ribbon.getJSONArray("groups").getJSONObject(0).getString("name")); assertEquals(2, ribbon.getJSONArray("groups").getJSONObject(0).getJSONArray("items").length()); assertEquals("second", ribbon.getJSONArray("groups").getJSONObject(1).getString("name")); assertEquals(2, ribbon.getJSONArray("groups").getJSONObject(1).getJSONArray("items").length()); JSONObject item11 = ribbon.getJSONArray("groups").getJSONObject(0).getJSONArray("items").getJSONObject(0); assertEquals("test", item11.getString("name")); assertFalse(item11.has("icon")); assertEquals(RibbonActionItem.Type.BIG_BUTTON.toString(), item11.getString("type")); // assertEquals("#{mainWindow.beanBForm}.save,#{mainWindow}.back", item11.getString("clickAction")); JSONObject item12 = ribbon.getJSONArray("groups").getJSONObject(0).getJSONArray("items").getJSONObject(1); assertEquals("test2", item12.getString("name")); assertEquals("icon2", item12.getString("icon")); assertEquals(RibbonActionItem.Type.SMALL_BUTTON.toString(), item12.getString("type")); assertEquals("xxx", item12.getString("clickAction")); JSONObject item21 = ribbon.getJSONArray("groups").getJSONObject(1).getJSONArray("items").getJSONObject(0); assertEquals("test2", item21.getString("name")); assertFalse(item21.has("icon")); assertEquals(RibbonActionItem.Type.BIG_BUTTON.toString(), item21.getString("type")); assertFalse(item21.has("clickAction")); JSONObject item22 = ribbon.getJSONArray("groups").getJSONObject(1).getJSONArray("items").getJSONObject(1); assertEquals("combo1", item22.getString("name")); assertFalse(item22.has("icon")); assertEquals(RibbonActionItem.Type.BIG_BUTTON.toString(), item22.getString("type")); assertEquals("yyy3", item22.getString("clickAction")); assertEquals(2, item22.getJSONArray("items").length()); JSONObject item221 = item22.getJSONArray("items").getJSONObject(0); assertEquals("test1", item221.getString("name")); assertFalse(item221.has("icon")); assertEquals(RibbonActionItem.Type.BIG_BUTTON.toString(), item221.getString("type")); assertEquals("yyy1", item221.getString("clickAction")); JSONObject item222 = item22.getJSONArray("items").getJSONObject(1); assertEquals("test2", item222.getString("name")); assertEquals("icon2", item222.getString("icon")); assertEquals(RibbonActionItem.Type.BIG_BUTTON.toString(), item222.getString("type")); assertEquals("yyy2", item222.getString("clickAction")); } @Test public void shouldSetFields() { // when InternalViewDefinition vd = parseAndGetViewDefinition(); // then checkComponent(vd.getComponentByReference("mainWindow"), WindowComponentPattern.class, "mainWindow", "beanB"); checkComponent(vd.getComponentByReference("beanBForm"), FormComponentPattern.class, "beanBForm", "beanB"); checkComponent(vd.getComponentByReference("referenceToTextarea"), TextAreaComponentPattern.class, "name", "beanB"); checkComponent(vd.getComponentByReference("active"), CheckBoxComponentPattern.class, "active", "beanB"); checkComponent(vd.getComponentByReference("neverEnabledCheckbox"), CheckBoxComponentPattern.class, "neverEnabledCheckbox", "beanB"); checkComponent(vd.getComponentByReference("enabledCheckbox"), CheckBoxComponentPattern.class, "enabledCheckbox", "beanB"); checkComponent(vd.getComponentByReference("selectBeanA"), TextInputComponentPattern.class, "selectBeanA", "beanA"); checkComponent(vd.getComponentByReference("beanM"), TextAreaComponentPattern.class, "beanM", "beanB"); checkComponent(vd.getComponentByReference("beansBInnerGrig"), GridComponentPattern.class, "beansBGrig", "beanB"); checkComponent(vd.getComponentByReference("beanAForm"), FormComponentPattern.class, "beanAForm", "beanA"); checkComponent(vd.getComponentByReference("beanAFormName"), TextInputComponentPattern.class, "name", "beanA"); checkComponent(vd.getComponentByReference("beansBGrig"), GridComponentPattern.class, "beansBGrig", "beanB"); checkComponent(vd.getComponentByReference("link"), ButtonComponentPattern.class, "link", "beanB"); checkFieldListener(vd.getComponentByReference("beanBForm"), vd.getComponentByReference("referenceToTextarea"), "name"); checkFieldListener(vd.getComponentByReference("beanBForm"), vd.getComponentByReference("active"), "activeA"); checkFieldListener(vd.getComponentByReference("beanBForm"), vd.getComponentByReference("beanAForm"), "beanA"); checkFieldListener(vd.getComponentByReference("beanBForm"), vd.getComponentByReference("beanM"), "beanM"); } @Test public final void shouldMarkComponentAsPermanentlyDisabled() { // when final InternalViewDefinition vd = parseAndGetViewDefinition(); final ComponentPattern component = vd.getComponentByReference("neverEnabledCheckbox"); // then assertTrue(component.isPermanentlyDisabled()); assertFalse(component.isDefaultEnabled()); } @Test public final void shouldMarkComponentAsNotEnabled() { // when final InternalViewDefinition vd = parseAndGetViewDefinition(); final ComponentPattern component = vd.getComponentByReference("active"); // then assertFalse(component.isPermanentlyDisabled()); assertFalse(component.isDefaultEnabled()); } @Test public final void shouldMarkComponentAsEnabled() { // when final InternalViewDefinition vd = parseAndGetViewDefinition(); final ComponentPattern component = vd.getComponentByReference("enabledCheckbox"); // then assertFalse(component.isPermanentlyDisabled()); assertTrue(component.isDefaultEnabled()); } @SuppressWarnings("unchecked") private void checkFieldListener(final ComponentPattern component, final ComponentPattern listener, final String field) { Map<String, ComponentPattern> listeners = (Map<String, ComponentPattern>) getField(component, "fieldEntityIdChangeListeners"); ComponentPattern actualListener = listeners.get(field); assertEquals(listener, actualListener); } private void checkComponent(final ComponentPattern component, final Class<?> clazz, final String name, final String model) { assertNotNull(component); assertThat(component, instanceOf(clazz)); assertEquals(name, component.getName()); } @Test public void shouldSetHooks() { // when ViewDefinition viewDefinition = parseAndGetViewDefinition(); // then assertHookDefinition(viewDefinition, HookType.BEFORE_INITIALIZE, 0, CustomViewService.class, "onBeforeInitialize"); assertHookDefinition(viewDefinition, HookType.AFTER_INITIALIZE, 0, CustomViewService.class, "onAfterInitialize"); assertHookDefinition(viewDefinition, HookType.BEFORE_RENDER, 0, CustomViewService.class, "onBeforeRender"); } private void assertHookDefinition(final Object object, final HookType hookType, final int hookPosition, final Class<?> hookBeanClass, final String hookMethodName) { ViewHooksHolder hooksHolder = (ViewHooksHolder) getField(object, "viewHooksHolder"); ViewHookDefinition hook; if (hookType.getCategory() == HookType.Category.LIFECYCLE_HOOK) { Multimap<HookType, ViewLifecycleHook> lifecycleHooks = (Multimap<HookType, ViewLifecycleHook>) getField(hooksHolder, "lifecycleHooks"); hook = Lists.newArrayList(lifecycleHooks.get(hookType)).get(hookPosition); } else if (hookType.getCategory() == HookType.Category.CONSTRUCTION_HOOK) { List<ViewConstructionHook> constructionHooks = (List<ViewConstructionHook>) getField(hooksHolder, "constructionHooks"); hook = constructionHooks.get(hookPosition); } else { Assert.fail("Unsupported hook type: " + hookType); return; } assertNotNull(hook); assertThat(hook.getBean(), instanceOf(hookBeanClass)); assertEquals(hookMethodName, hook.getMethod().getName()); } private List<InternalViewDefinition> parseAndGetViewDefinitions() { List<InternalViewDefinition> views = new LinkedList<InternalViewDefinition>(); views.add(viewDefinitionParser.parseViewXml(new ClassPathResource(xml1), "sample")); views.add(viewDefinitionParser.parseViewXml(new ClassPathResource(xml2), "sample")); return views; } private InternalViewDefinition parseAndGetViewDefinition() { return parseAndGetViewDefinition(xml1); } private InternalViewDefinition parseAndGetViewDefinition(final String xmlPath) { return viewDefinitionParser.parseViewXml(new ClassPathResource(xmlPath), "sample"); } @SuppressWarnings("unchecked") @Test public void shouldSetListeners() throws Exception { // when ComponentPattern component = parseAndGetViewDefinition().getComponentByReference("beanBForm"); // then List<ViewEventListenerHook> customEvents = (List<ViewEventListenerHook>) getField(component, "customEventListeners"); assertEquals("save", customEvents.get(0).getEventName()); assertThat(customEvents.get(0).getBean(), instanceOf(CustomViewService.class)); assertEquals("saveForm", customEvents.get(0).getMethod().getName()); assertEquals("generate", customEvents.get(1).getEventName()); assertThat(customEvents.get(1).getBean(), instanceOf(CustomViewService.class)); assertEquals("generate", customEvents.get(1).getMethod().getName()); } }