| /******************************************************************************* |
| * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * eugen - initial API and implementation |
| * Christian W. Damus - bug 527686 |
| ******************************************************************************/ |
| package org.eclipse.emf.ecp.view.spi.treemasterdetail.ui.swt; |
| |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.CoreMatchers.sameInstance; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.mock; |
| |
| import org.eclipse.emf.ecp.test.common.DefaultRealm; |
| import org.eclipse.emf.ecp.view.internal.context.ViewModelContextImpl; |
| import org.eclipse.emf.ecp.view.spi.context.ViewModelContext; |
| import org.eclipse.emf.ecp.view.spi.model.VControl; |
| import org.eclipse.emf.ecp.view.spi.model.VFeaturePathDomainModelReference; |
| import org.eclipse.emf.ecp.view.spi.model.VView; |
| import org.eclipse.emf.ecp.view.spi.model.VViewFactory; |
| import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption; |
| import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException; |
| import org.eclipse.emf.ecp.view.spi.swt.masterdetail.DetailViewCache; |
| import org.eclipse.emf.ecp.view.test.common.swt.spi.SWTTestUtil; |
| import org.eclipse.emf.ecp.view.treemasterdetail.model.VTreeMasterDetail; |
| import org.eclipse.emf.ecp.view.treemasterdetail.model.VTreeMasterDetailFactory; |
| import org.eclipse.emf.emfstore.bowling.BowlingFactory; |
| import org.eclipse.emf.emfstore.bowling.BowlingPackage; |
| import org.eclipse.emf.emfstore.bowling.League; |
| import org.eclipse.emf.emfstore.bowling.Player; |
| import org.eclipse.emfforms.spi.common.report.ReportService; |
| import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException; |
| import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell; |
| import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.custom.ScrolledComposite; |
| import org.eclipse.swt.custom.StackLayout; |
| import org.eclipse.swt.events.MenuListener; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.swt.widgets.TypedListener; |
| import org.eclipse.swtbot.swt.finder.SWTBot; |
| import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| @SuppressWarnings("restriction") |
| public class TreeMasterDetailRenderer_PTest { |
| |
| private DefaultRealm realm; |
| private TreeMasterDetailSWTRenderer renderer; |
| private Shell shell; |
| private ViewModelContext context; |
| |
| @Before |
| public void before() throws DatabindingFailedException { |
| realm = new DefaultRealm(); |
| final ReportService reportService = mock(ReportService.class); |
| final VTreeMasterDetail tmd = VTreeMasterDetailFactory.eINSTANCE.createTreeMasterDetail(); |
| // define explicit inner View |
| final VView detailView = VViewFactory.eINSTANCE.createView(); |
| detailView.setRootEClass(BowlingPackage.eINSTANCE.getLeague()); |
| final VControl vControl = VViewFactory.eINSTANCE.createControl(); |
| final VFeaturePathDomainModelReference featurePathDomainModelReference = VViewFactory.eINSTANCE |
| .createFeaturePathDomainModelReference(); |
| featurePathDomainModelReference.setDomainModelEFeature(BowlingPackage.eINSTANCE.getLeague_Name()); |
| vControl.setDomainModelReference(featurePathDomainModelReference); |
| detailView.getChildren().add(vControl); |
| tmd.setDetailView(detailView); |
| |
| final League domainModel = BowlingFactory.eINSTANCE.createLeague(); |
| Player player = BowlingFactory.eINSTANCE.createPlayer(); |
| player.setName("Player 1"); //$NON-NLS-1$ |
| domainModel.getPlayers().add(player); |
| player = BowlingFactory.eINSTANCE.createPlayer(); |
| player.setName("Player 2"); //$NON-NLS-1$ |
| domainModel.getPlayers().add(player); |
| |
| context = new ViewModelContextImpl(tmd, domainModel); |
| context.putContextValue(DetailViewCache.DETAIL_VIEW_CACHE_SIZE, 5); |
| renderer = new TreeMasterDetailSWTRenderer(tmd, context, reportService); |
| renderer.init(); |
| |
| shell = new Shell(Display.getDefault(), SWT.NONE); |
| } |
| |
| @After |
| public void testTearDown() { |
| realm.dispose(); |
| shell.dispose(); |
| } |
| |
| @Test |
| public void getGridDescription() { |
| final SWTGridDescription gridDescription = renderer.getGridDescription(null); |
| assertEquals(1, gridDescription.getColumns()); |
| assertEquals(1, gridDescription.getRows()); |
| } |
| |
| @Test |
| public void contextMenu_delete_resetSelectionToRoot() |
| throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| final Control render = render(); |
| final SWTBot bot = new SWTBot(render); |
| final SWTBotTree tree = bot.tree(); |
| SWTTestUtil.selectTreeItem(tree.widget.getItem(0).getItem(0)); |
| SWTTestUtil.waitForUIThread(); |
| |
| tree.contextMenu("Delete").click(); //$NON-NLS-1$ |
| |
| SWTTestUtil.waitForUIThread(); |
| |
| final TreeItem[] selection = tree.widget.getSelection(); |
| assertTrue("Root node was not selected automatically!", //$NON-NLS-1$ |
| selection.length == 1 && selection[0].getData() instanceof League); |
| } |
| |
| @Test |
| public void hasContextMenu() { |
| assertTrue(renderer.hasContextMenu()); |
| } |
| |
| @Test |
| public void hasDnDSupport() { |
| assertTrue(renderer.hasDnDSupport()); |
| } |
| |
| @Test |
| public void initialRendering_NoContainer() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| final Control renderResult = render(); |
| assertTrue(Composite.class.isInstance(renderResult)); |
| final Composite resultComposite = Composite.class.cast(renderResult); |
| assertEquals(2, resultComposite.getChildren().length); |
| |
| // FIXME why do we have an intermediate composite? |
| assertTrue(Composite.class.isInstance(resultComposite.getChildren()[0])); |
| final Composite topComposite = Composite.class.cast(resultComposite.getChildren()[0]); |
| assertEquals(1, topComposite.getChildren().length); |
| |
| assertTrue(Composite.class.isInstance(topComposite.getChildren()[0])); |
| final Composite titleComposite = Composite.class.cast(topComposite.getChildren()[0]); |
| assertEquals(3, titleComposite.getChildren().length); |
| // FIXME check backgroundcolor? |
| |
| assertTrue(Label.class.isInstance(titleComposite.getChildren()[0])); |
| final Label titleIcon = Label.class.cast(titleComposite.getChildren()[0]); |
| assertNotNull(titleIcon.getImage()); |
| |
| assertTrue(Label.class.isInstance(titleComposite.getChildren()[1])); |
| final Label titleLabel = Label.class.cast(titleComposite.getChildren()[1]); |
| assertEquals("View Editor", titleLabel.getText()); //$NON-NLS-1$ |
| |
| assertTrue(ToolBar.class.isInstance(titleComposite.getChildren()[2])); |
| final ToolBar titleToolbar = ToolBar.class.cast(titleComposite.getChildren()[2]); |
| assertEquals(0, titleToolbar.getChildren().length); |
| |
| // Bottom composite |
| // FIXME why do we have an intermediate composite? |
| assertTrue(Composite.class.isInstance(resultComposite.getChildren()[1])); |
| final Composite bottomComposite = Composite.class.cast(resultComposite.getChildren()[1]); |
| assertEquals(1, bottomComposite.getChildren().length); |
| |
| assertTrue(SashForm.class.isInstance(bottomComposite.getChildren()[0])); |
| final SashForm sash = SashForm.class.cast(bottomComposite.getChildren()[0]); |
| assertEquals(2, sash.getChildren().length); |
| |
| assertTrue(Composite.class.isInstance(sash.getChildren()[0])); |
| final Composite treeComposite = Composite.class.cast(sash.getChildren()[0]); |
| assertEquals(1, treeComposite.getChildren().length); |
| |
| assertTrue(Tree.class.isInstance(treeComposite.getChildren()[0])); |
| final Tree tree = Tree.class.cast(treeComposite.getChildren()[0]); |
| assertEquals(1, tree.getItemCount()); |
| final TreeItem item = TreeItem.class.cast(tree.getItems()[0]); |
| assertEquals("League", item.getText()); //$NON-NLS-1$ |
| assertNotNull(item.getImage()); |
| |
| assertTrue(ScrolledComposite.class.isInstance(sash.getChildren()[1])); |
| final ScrolledComposite detailScrolledComposite = ScrolledComposite.class.cast(sash.getChildren()[1]); |
| assertEquals(1, detailScrolledComposite.getChildren().length); |
| // FIXME why do we have an intermediate composite? |
| assertTrue(Composite.class.isInstance(detailScrolledComposite.getChildren()[0])); |
| final Composite detailComposite = Composite.class.cast(detailScrolledComposite.getChildren()[0]); |
| assertEquals(2, detailComposite.getChildren().length); |
| |
| // Detail Title |
| assertTrue(Composite.class.isInstance(detailComposite.getChildren()[0])); |
| final Composite detailTitleComposite = Composite.class.cast(detailComposite.getChildren()[0]); |
| assertEquals(1, detailTitleComposite.getChildren().length); |
| |
| assertTrue(Label.class.isInstance(detailTitleComposite.getChildren()[0])); |
| final Label detailTitle = Label.class.cast(detailTitleComposite.getChildren()[0]); |
| assertEquals("Details", detailTitle.getText()); //$NON-NLS-1$ |
| |
| // Detail Content |
| assertTrue(Composite.class.isInstance(detailComposite.getChildren()[1])); |
| final Composite detailContentComposite = Composite.class.cast(detailComposite.getChildren()[1]); |
| assertEquals(1, detailContentComposite.getChildren().length); |
| |
| // too many intermediate composites |
| final Composite content = Composite.class.cast( |
| Composite.class.cast( |
| detailContentComposite.getChildren()[0])); |
| final StackLayout stack = (StackLayout) content.getLayout(); |
| final Composite details = (Composite) stack.topControl; |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(String.format("Instead of 3 Elements of Control found %1$s controls.", details.getChildren().length)); //$NON-NLS-1$ |
| sb.append("The controls are:"); //$NON-NLS-1$ |
| for (final Control c : details.getChildren()) { |
| sb.append(String.format("Control: %1$s.", c)); //$NON-NLS-1$ |
| } |
| assertEquals(sb.toString(), 3, |
| details.getChildren().length); |
| |
| assertTrue(Label.class.isInstance(details.getChildren()[0])); |
| final Label label = Label.class.cast(details.getChildren()[0]); |
| assertEquals("Name", label.getText()); //$NON-NLS-1$ |
| |
| assertTrue(Label.class.isInstance(details.getChildren()[1])); |
| final Label validation = Label.class.cast(details.getChildren()[1]); |
| assertNull(validation.getImage()); |
| |
| assertTrue(Composite.class.isInstance(details.getChildren()[2])); |
| final Composite control = Composite.class.cast(details.getChildren()[2]); |
| assertEquals(1, control.getChildren().length); |
| |
| assertTrue(Text.class.isInstance(control.getChildren()[0])); |
| final Text textControl = Text.class.cast(control.getChildren()[0]); |
| assertEquals("", textControl.getText()); //$NON-NLS-1$ |
| |
| assertContextMenu(tree, 1); |
| } |
| |
| @Test |
| public void tmd_readOnly() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| context.getViewModel().setReadonly(true); |
| final Control renderResult = render(); |
| final Tree tree = getTree(renderResult); |
| assertTrue(tree.isEnabled()); |
| final Composite detail = getDetail(renderResult); |
| assertTrue(detail.isEnabled()); |
| final Control[] content = getDetailContent(detail); |
| assertFalse(content[2].isEnabled()); |
| |
| // Read-only tree should not have any context menu |
| assertNull(tree.getMenu()); |
| } |
| |
| @Test |
| public void detailView_readOnly() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| ((VTreeMasterDetail) context.getViewModel()).getDetailView().setReadonly(true); |
| final Control renderResult = render(); |
| final Tree tree = getTree(renderResult); |
| assertTrue(tree.isEnabled()); |
| final Composite detail = getDetail(renderResult); |
| assertTrue(detail.isEnabled()); |
| final Control[] content = getDetailContent(detail); |
| assertFalse(content[2].isEnabled()); |
| |
| assertContextMenu(tree, 1); |
| } |
| |
| /** |
| * Verify that a cached detail view rendering is reused. |
| */ |
| @SuppressWarnings("nls") |
| @Test |
| public void detailViewRenderingReused() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| final Control renderResult = render(); |
| final Tree tree = getTree(renderResult); |
| final TreeItem leagueItem = tree.getItem(0); |
| leagueItem.setExpanded(true); |
| final TreeItem player1Item = leagueItem.getItem(0); |
| final TreeItem player2Item = leagueItem.getItem(1); |
| |
| select(tree, player1Item); |
| |
| // Get the player's detail |
| final Composite detail = getDetail(renderResult); |
| final Text nameText = (Text) getDetailContent(detail)[2]; |
| assertThat(nameText.getText(), is("Player 1")); |
| |
| // Change the selection to another player |
| select(tree, player2Item); |
| |
| // Verify the updates |
| assertThat(getDetail(renderResult), sameInstance(detail)); |
| assertThat(getDetailContent(detail)[2], sameInstance(nameText)); |
| assertThat(nameText.getText(), is("Player 2")); |
| } |
| |
| @Test |
| public void tmd_initially_disabled() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| context.getViewModel().setEnabled(false); |
| final Control renderResult = render(); |
| final Tree tree = getTree(renderResult); |
| assertTrue(tree.isEnabled()); |
| final Composite detail = getDetail(renderResult); |
| assertTrue(detail.isEnabled()); |
| final Control[] content = getDetailContent(detail); |
| assertFalse(content[2].isEnabled()); |
| |
| assertContextMenu(tree, 0); |
| } |
| |
| @Test |
| public void tmd_dynamic_disabled() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| final Control renderResult = render(); |
| context.getViewModel().setEnabled(false); |
| final Tree tree = getTree(renderResult); |
| assertTrue(tree.isEnabled()); |
| final Composite detail = getDetail(renderResult); |
| assertTrue(detail.isEnabled()); |
| final Control[] content = getDetailContent(detail); |
| assertFalse(content[2].isEnabled()); |
| |
| assertContextMenu(tree, 0); |
| } |
| |
| @Test |
| public void detailView_initially_disabled() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| ((VTreeMasterDetail) context.getViewModel()).getDetailView().setEnabled(false); |
| final Control renderResult = render(); |
| final Tree tree = getTree(renderResult); |
| assertTrue(tree.isEnabled()); |
| final Composite detail = getDetail(renderResult); |
| assertTrue(detail.isEnabled()); |
| final Control[] content = getDetailContent(detail); |
| assertFalse(content[2].isEnabled()); |
| |
| assertContextMenu(tree, 1); |
| } |
| |
| private Control render() throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { |
| final Control renderResult = renderer.render(new SWTGridCell(0, 0, renderer), shell); |
| renderer.finalizeRendering(shell); |
| return renderResult; |
| } |
| |
| private void select(Tree tree, TreeItem item) { |
| tree.setSelection(item); |
| final Event selection = new Event(); |
| selection.display = tree.getDisplay(); |
| selection.widget = tree; |
| selection.item = item; |
| selection.type = SWT.Selection; |
| tree.notifyListeners(SWT.Selection, selection); |
| } |
| |
| private Tree getTree(Control renderResult) { |
| final Composite resultComposite = Composite.class.cast(renderResult); |
| final Composite bottomComposite = Composite.class.cast(resultComposite.getChildren()[1]); |
| final SashForm sash = SashForm.class.cast(bottomComposite.getChildren()[0]); |
| final Composite treeComposite = Composite.class.cast(sash.getChildren()[0]); |
| final Tree tree = Tree.class.cast(treeComposite.getChildren()[0]); |
| return tree; |
| } |
| |
| private Composite getDetail(Control renderResult) { |
| final Composite resultComposite = Composite.class.cast(renderResult); |
| final Composite bottomComposite = Composite.class.cast(resultComposite.getChildren()[1]); |
| final SashForm sash = SashForm.class.cast(bottomComposite.getChildren()[0]); |
| final ScrolledComposite detailScrolledComposite = ScrolledComposite.class.cast(sash.getChildren()[1]); |
| final Composite detailComposite = Composite.class.cast(detailScrolledComposite.getChildren()[0]); |
| final Composite detailContentComposite = Composite.class.cast(detailComposite.getChildren()[1]); |
| return detailContentComposite; |
| } |
| |
| private Control[] getDetailContent(Composite detailContentComposite) { |
| final Composite detailStackComposite = (Composite) detailContentComposite.getChildren()[0]; |
| final StackLayout stackLayout = (StackLayout) detailStackComposite.getLayout(); |
| final Composite content = (Composite) stackLayout.topControl; |
| final Label label = Label.class.cast(content.getChildren()[0]); |
| final Label validation = Label.class.cast(content.getChildren()[1]); |
| final Composite control = Composite.class.cast(content.getChildren()[2]); |
| final Text textControl = Text.class.cast(control.getChildren()[0]); |
| |
| return new Control[] { label, validation, textControl }; |
| } |
| |
| private void assertContextMenu(Tree tree, int numberItems) { |
| final Menu menu = tree.getMenu(); |
| assertEquals(0, menu.getItemCount()); |
| final MenuListener menuListener = (MenuListener) ((TypedListener) menu.getListeners(SWT.Show)[0]) |
| .getEventListener(); |
| menuListener.menuShown(null); |
| assertEquals(numberItems, menu.getItemCount()); |
| } |
| } |