/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.workbench.common.screens.datamodeller.client.widgets.advanceddomain.annotationlisteditor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwtmockito.GwtMockitoTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.kie.workbench.common.screens.datamodeller.client.resources.i18n.Constants; import org.kie.workbench.common.screens.datamodeller.client.widgets.advanceddomain.annotationlisteditor.item.AnnotationListItem; import org.kie.workbench.common.screens.datamodeller.client.widgets.advanceddomain.valuepaireditor.ValuePairEditorPopup; import org.kie.workbench.common.screens.datamodeller.service.DataModelerService; import org.kie.workbench.common.services.datamodeller.core.Annotation; import org.kie.workbench.common.services.datamodeller.core.AnnotationDefinition; import org.kie.workbench.common.services.datamodeller.core.AnnotationValuePairDefinition; import org.kie.workbench.common.services.datamodeller.core.ElementType; import org.kie.workbench.common.services.datamodeller.driver.model.AnnotationParseRequest; import org.kie.workbench.common.services.datamodeller.driver.model.AnnotationParseResponse; import org.kie.workbench.common.services.datamodeller.driver.model.AnnotationSource; import org.kie.workbench.common.services.datamodeller.driver.model.AnnotationSourceRequest; import org.kie.workbench.common.services.datamodeller.driver.model.AnnotationSourceResponse; import org.kie.workbench.common.services.shared.project.KieProject; import org.mockito.Mock; import org.uberfire.mocks.CallerMock; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @RunWith( GwtMockitoTestRunner.class ) public class AdvancedAnnotationListEditorTest { private static final int MAX_ITEMS = 4; @Mock private AdvancedAnnotationListEditorView view; @Mock private DataModelerService dataModelerService; @Mock private InstanceMock< ValuePairEditorPopup > valuePairEditorInstance; @Mock private ValuePairEditorPopup valuePairEditor; @Mock private InstanceMock< AnnotationListItem > itemInstance; private int listItemsCount = 0; private List< AnnotationListItem > itemInstances = new ArrayList< AnnotationListItem >( ); private AdvancedAnnotationListEditor annotationListEditor; private List< Annotation > annotations = new ArrayList< Annotation >( ); @Mock private Annotation annotation; @Mock private AnnotationDefinition annotationDefinition; @Mock private AnnotationValuePairDefinition valuePairDefinition; @Mock private AnnotationSourceResponse sourceResponse; private Map< String, AnnotationSource > annotationSourcesMap = new HashMap< String, AnnotationSource >( ); @Mock private KieProject kieProject; @Mock private ElementType elementType; @Mock private AdvancedAnnotationListEditorView.AddAnnotationHandler addAnnotationHandler; @Mock private AdvancedAnnotationListEditorView.DeleteAnnotationHandler deleteAnnotationHandler; @Mock private AdvancedAnnotationListEditorView.ValuePairChangeHandler valuePairChangeHandler; @Mock private AdvancedAnnotationListEditorView.ClearValuePairHandler clearValuePairHandler; private Annotation currentAnnotation; private String currentAnnotationClassName; @Mock private AnnotationDefinition currentAnnotationDefinition; private AnnotationSource currentAnnotationSources; @Mock private AnnotationValuePairDefinition currentValuePairDefinition; private String currentValuePairName; private String currentValuePairSource; private Object currentValuePairValue; private Object newValuePairValue; @Before public void setup( ) { Annotation annotation; AnnotationSource annotationSource; for ( int i = 0; i < MAX_ITEMS; i++ ) { annotation = mock( Annotation.class ); when( annotation.getClassName( ) ).thenReturn( "AnnotationClass" + i ); annotations.add( annotation ); annotationSource = mock( AnnotationSource.class ); annotationSourcesMap.put( annotation.getClassName( ), annotationSource ); } setupItemInstances( ); annotationListEditor = new AdvancedAnnotationListEditor( view, new CallerMock( dataModelerService ), valuePairEditorInstance, itemInstance ) { @Override protected AnnotationListItem createListItem( ) { if ( listItemsCount >= itemInstances.size( ) ) { throw new RuntimeException( "too many invocations" ); } else { super.createListItem( ); return itemInstances.get( listItemsCount++ ); } } @Override protected ValuePairEditorPopup createValuePairEditor( ) { super.createValuePairEditor( ); return valuePairEditor; } }; annotationListEditor.addAddAnnotationHandler( addAnnotationHandler ); annotationListEditor.addDeleteAnnotationHandler( deleteAnnotationHandler ); annotationListEditor.addValuePairChangeHandler( valuePairChangeHandler ); annotationListEditor.addClearValuePairHandler( clearValuePairHandler ); } private void setupItemInstances( ) { listItemsCount = 0; itemInstances.clear( ); for ( int i = 0; i < MAX_ITEMS; i++ ) { itemInstances.add( mock( AnnotationListItem.class ) ); } } @Test public void testLoadAnnotationsWithoutSources( ) { when( dataModelerService.resolveSourceRequest( any( AnnotationSourceRequest.class ) ) ).thenReturn( sourceResponse ); when( sourceResponse.getAnnotationSources( ) ).thenReturn( annotationSourcesMap ); annotationListEditor.loadAnnotations( annotations ); verifyAnnotationsLoaded( ); } @Test public void testLoadAnnotationsWithSources( ) { annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); verifyAnnotationsLoaded( ); } private void verifyAnnotationsLoaded( ) { verify( view, times( 1 ) ).clear( ); // the items should have been properly created and initialized. verify( itemInstance, times( itemInstances.size( ) ) ).get( ); // each item should have been properly loaded with the corresponding annotation. Annotation annotation; AnnotationListItem listItem; for ( int i = 0; i < annotations.size( ); i++ ) { annotation = annotations.get( i ); listItem = itemInstances.get( i ); verify( listItem, times( 1 ) ).loadAnnotation( annotation, annotationSourcesMap.get( annotation.getClassName( ) ) ); } } @Test public void testSetReadonlyTrue( ) { annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); annotationListEditor.setReadonly( true ); verify( view, times( 1 ) ).setReadonly( true ); assertEquals( true, annotationListEditor.isReadonly( ) ); //the items are set to the by default state = false when the annotations are initially loaded. verifyItemsReadonlyStatus( 1, false ); //and finally to the desired state when setReadonly( true ) is invoked. verifyItemsReadonlyStatus( 1, true ); } @Test public void testSetReadonlyFalse( ) { annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); annotationListEditor.setReadonly( false ); verify( view, times( 1 ) ).setReadonly( false ); assertEquals( false, annotationListEditor.isReadonly( ) ); //the items are set to the by default state = false when initially loaded and finally to the desired state //when setReadonly( false ) is invoked. verifyItemsReadonlyStatus( 2, false ); } private void verifyItemsReadonlyStatus( int times, boolean expectedReadonlyStatus ) { for ( AnnotationListItem listItem : itemInstances ) { verify( listItem, times( times ) ).setReadonly( expectedReadonlyStatus ); } } @Test public void testOnAddAnnotation( ) { annotationListEditor.init( kieProject, elementType ); annotationListEditor.onAddAnnotation( ); // the create annotation wizard should have been invoked. verify( view, times( 1 ) ).invokeCreateAnnotationWizard( annotationListEditor.getAddAnnotationCallback( ), kieProject, elementType ); // emulate the wizard completion. annotationListEditor.getAddAnnotationCallback( ).callback( annotation ); verify( addAnnotationHandler, times( 1 ) ).onAddAnnotation( annotation ); } @Test public void testOnDeleteAnnotation( ) { when( annotation.getClassName( ) ).thenReturn( "AnnotationClassName" ); annotationListEditor.init( kieProject, elementType ); annotationListEditor.onDeleteAnnotation( annotation ); String expectedMessage = Constants.INSTANCE.advanced_domain_annotation_list_editor_message_confirm_annotation_deletion( "AnnotationClassName", elementType.name( ) ); verify( view, times( 1 ) ).showYesNoDialog( expectedMessage, annotationListEditor.getDeleteAnnotationCommand( annotation ), annotationListEditor.getNoActionCommand( ), annotationListEditor.getNoActionCommand( ) ); // emulate the delete completion annotationListEditor.getDeleteAnnotationCommand( annotation ).execute( ); verify( deleteAnnotationHandler, times( 1 ) ).onDeleteAnnotation( annotation ); } @Test public void testOnEditSimpleValuePair( ) { prepareValuePairEdition( ); // it's the simple value case when( valuePairEditor.isGenericEditor( ) ).thenReturn( false ); // emulates that currentAnnotation an currentValuePair where selected for edition in the UI annotationListEditor.onEditValuePair( currentAnnotation, currentValuePairName ); verifyValuePairEditorCreatedAndShown( ); // the value for the valuePairEditor should have been properly set with currentValuePairValue. verify( valuePairEditor, times( 1 ) ).setValue( currentValuePairValue ); // setup the returned value. newValuePairValue = mock( Object.class ); // emulate the user pressing the ok button on the valuePairEditor prepareValuePairEditorSubmit( ); when( valuePairEditor.isValid( ) ).thenReturn( true ); annotationListEditor.doValuePairChange( valuePairEditor, valuePairEditor.getValue( ) ); verify( valuePairChangeHandler, times( 1 ) ).onValuePairChange( currentAnnotationClassName, currentValuePairName, newValuePairValue ); verifyValuePairEditorDestroyedAndHidden( ); } @Test public void testOnEditGenericValuePair( ) { prepareValuePairEdition( ); // it's the generic value case when( valuePairEditor.isGenericEditor( ) ).thenReturn( true ); when( valuePairEditor.getValuePairDefinition( ) ).thenReturn( currentValuePairDefinition ); // emulates that currentAnnotation an currentValuePair where selected for edition in the UI annotationListEditor.onEditValuePair( currentAnnotation, currentValuePairName ); verifyValuePairEditorCreatedAndShown( ); // the value for the valuePairEditor should have been properly set with the current value pair source for // the generic case. verify( valuePairEditor, times( 1 ) ).setValue( currentValuePairSource ); // setup the returned value. // generic case requires a parse response, and the newValuePairValue comes from server. newValuePairValue = mock( Object.class ); AnnotationParseResponse parseResponse = mock( AnnotationParseResponse.class ); Annotation parsedAnnotation = mock( Annotation.class ); when( parseResponse.getAnnotation( ) ).thenReturn( parsedAnnotation ); when( parsedAnnotation.getValue( currentValuePairName ) ).thenReturn( newValuePairValue ); when( parseResponse.hasErrors( ) ).thenReturn( false ); when( dataModelerService.resolveParseRequest( any( AnnotationParseRequest.class ), eq( kieProject ) ) ).thenReturn( parseResponse ); // emulate the user pressing the ok button on the valuePairEditor prepareValuePairEditorSubmit( ); when( valuePairEditor.isValid( ) ).thenReturn( true ); annotationListEditor.doValuePairChange( valuePairEditor, valuePairEditor.getValue( ) ); verify( valuePairChangeHandler, times( 1 ) ).onValuePairChange( currentAnnotationClassName, currentValuePairName, newValuePairValue ); verifyValuePairEditorDestroyedAndHidden( ); } private void prepareValuePairEdition( ) { // the editor was previously loaded with a set of annotations. annotationListEditor.init( kieProject, elementType ); annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // emulates that an arbitrary annotation from the list of loaded annotations was selected for the value pair edition. currentAnnotation = annotations.get( 0 ); currentAnnotationClassName = currentAnnotation.getClassName( ); currentAnnotationSources = annotationSourcesMap.get( currentAnnotationClassName ); currentValuePairName = "valuePairName"; currentValuePairSource = "valuePairSource"; currentValuePairValue = mock( Object.class ); when( currentAnnotation.getAnnotationDefinition( ) ).thenReturn( currentAnnotationDefinition ); when( currentAnnotationDefinition.getValuePair( currentValuePairName ) ).thenReturn( currentValuePairDefinition ); when( currentValuePairDefinition.getName( ) ).thenReturn( currentValuePairName ); when( currentAnnotationSources.getValuePairSource( currentValuePairName ) ).thenReturn( currentValuePairSource ); when( currentAnnotation.getValue( currentValuePairName ) ).thenReturn( currentValuePairValue ); when( valuePairEditor.getValuePairDefinition( ) ).thenReturn( currentValuePairDefinition ); } private void verifyValuePairEditorCreatedAndShown( ) { // the value pair editor should have been created, initialized and shown. verify( valuePairEditorInstance, times( 1 ) ).get( ); verify( valuePairEditor, times( 1 ) ).init( currentAnnotation.getClassName( ), currentValuePairDefinition ); verify( valuePairEditor, times( 1 ) ).show( ); } private void verifyValuePairEditorDestroyedAndHidden( ) { verify( valuePairEditor, times( 1 ) ).hide( ); verify( valuePairEditorInstance, times( 1 ) ).destroy( valuePairEditor ); } private void prepareValuePairEditorSubmit( ) { // emulate the valuePairEditor status when the user presses the ok action. when( valuePairEditor.getAnnotationClassName( ) ).thenReturn( currentAnnotationClassName ); when( valuePairEditor.getValuePairDefinition( ) ).thenReturn( currentValuePairDefinition ); when( valuePairEditor.getValue( ) ).thenReturn( newValuePairValue ); } @Test public void testOnClearValuePairWithoutDefaultValue( ) { prepareValuePairClear( ); //the value pair hasn't a default value when( valuePairDefinition.getDefaultValue( ) ).thenReturn( null ); // emulate the clear invocation from the ui for the given valuePair annotationListEditor.onClearValuePair( annotation, "valuePairName" ); //an alert should have been raised since the valuePair has no default value. String expectedMessage = Constants.INSTANCE.advanced_domain_annotation_list_editor_message_value_pair_has_no_default_value( "valuePairName", "AnnotationClassName" ); verify( view, times( 1 ) ).showYesNoDialog( expectedMessage, annotationListEditor.getNoActionCommand( ) ); } @Test public void testOnClearValuePairWithDefaultValue( ) { prepareValuePairClear( ); //the value pair has a default value Object someValue = mock( Object.class ); when( valuePairDefinition.getDefaultValue( ) ).thenReturn( someValue ); // emulate the clear invocation from the ui for the given valuePair annotationListEditor.onClearValuePair( annotation, "valuePairName" ); // the configured handler should have been invoked. verify( clearValuePairHandler, times( 1 ) ).onClearValuePair( annotation, "valuePairName" ); } private void prepareValuePairClear( ) { when( annotation.getClassName( ) ).thenReturn( "AnnotationClassName" ); when( annotation.getAnnotationDefinition( ) ).thenReturn( annotationDefinition ); when( annotationDefinition.getValuePair( "valuePairName" ) ).thenReturn( valuePairDefinition ); } @Test public void testExpandCollapseChanges( ) { // first load. annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // when loaded by the first time, all annotations are collapsed. for ( AnnotationListItem listItem : itemInstances ) { verify( listItem, times( 1 ) ).setCollapsed( true ); verify( listItem, never( ) ).setCollapsed( false ); } // emulate some expand/collapse actions coming from the UI // annotation #0 is expanded annotationListEditor.onCollapseChange( annotations.get( 0 ), false ); // annotation #1 is expanded, and then collapsed again, so finally collapsed. annotationListEditor.onCollapseChange( annotations.get( 1 ), false ); annotationListEditor.onCollapseChange( annotations.get( 1 ), true ); // annotation #2 is expanded annotationListEditor.onCollapseChange( annotations.get( 2 ), false ); // annotation #3 is un-touched. (the by default state is collapsed) // reset the items prior the second load setupItemInstances( ); // second load. annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // after the reload we should have the following. // annotation #0 should be expanded verify( itemInstances.get( 0 ), times( 1 ) ).setCollapsed( false ); verify( itemInstances.get( 0 ), never( ) ).setCollapsed( true ); // annotation #1 should be collapsed. verify( itemInstances.get( 1 ), times( 1 ) ).setCollapsed( true ); verify( itemInstances.get( 1 ), never( ) ).setCollapsed( false ); // annotation #2 should be expanded. verify( itemInstances.get( 2 ), times( 1 ) ).setCollapsed( false ); verify( itemInstances.get( 2 ), never( ) ).setCollapsed( true ); // annotation #3 should be collapsed. verify( itemInstances.get( 3 ), times( 1 ) ).setCollapsed( true ); verify( itemInstances.get( 3 ), never( ) ).setCollapsed( false ); } @Test public void testClear( ) { // load the editor. annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // clear the editor. annotationListEditor.clear( ); // the view is cleared 1 prior annotations loading + 1 when clear is invoked. verify( view, times( 2 ) ).clear( ); verifyItemsCleared( ); } @Test public void testDestroy( ) { // load the editor. annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // the managed bean life-cycle destroy method was invoked. annotationListEditor.destroy( ); verifyItemsCleared( ); } @Test public void testRemoveAnnotation() { // load the editor annotationListEditor.loadAnnotations( annotations, annotationSourcesMap ); // remove an arbitrary annotation. annotationListEditor.removeAnnotation( annotations.get( 2 ) ); // the corresponding item should have been removed from the view and destroyed. verify( view, times( 1 ) ).removeItem( itemInstances.get( 2 ) ); verify( itemInstance, times( 1 ) ).destroy( itemInstances.get( 2 ) ); } private void verifyItemsCleared( ) { // all the listItems should have been properly destroyed. for ( AnnotationListItem listItem : itemInstances ) { verify( itemInstance, times( 1 ) ).destroy( listItem ); } } }