// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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 com.google.collide.client.ui.tree;
import com.google.collide.client.code.FileTreeNodeDataAdapter;
import com.google.collide.client.code.FileTreeNodeRenderer;
import com.google.collide.client.workspace.FileTreeNode;
import com.google.collide.json.client.JsoArray;
import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
import org.waveprotocol.wave.client.common.util.SignalEvent;
/**
* Tests the {@link SelectionModel}.
*/
public class SelectionModelTest extends GWTTestCase {
interface TestResources extends Tree.Resources, FileTreeNodeRenderer.Resources {
}
private static void checkNodeArray(
JsoArray<FileTreeNode> expected, JsoArray<FileTreeNode> actual) {
assertEquals("Array sizes don't line up!", expected.size(), actual.size());
for (int i = 0; i < expected.size(); i++) {
assertEquals("expected: " + expected.get(i).getName() + " != " + actual.get(i).getName(),
expected.get(i), actual.get(i));
}
}
private Tree<FileTreeNode> mockTree;
private SelectionModel<FileTreeNode> mockSelectionModel;
private TestResources resources;
@Override
public String getModuleName() {
return TreeTestUtils.BUILD_MODULE_NAME;
}
@Override
public void gwtTearDown() throws Exception {
super.gwtTearDown();
}
/**
* Tests select responses to ctrl clicks across tiers in the tree.
*/
public void testCtrlSelectAcrossTiers() {
FileTreeNode root = mockTree.getModel().getRoot();
// Render the tree.
mockTree.renderTree(-1);
SignalEvent ctrlSignalEvent = new MockSignalEvent(true, false);
// Select a bunch of nodes at the same tier.
FileTreeNode AD1 = getNodeByPath(0);
assertNotNull("Node did not get rendered!", AD1.getRenderedTreeNode());
assertFalse(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
JsoArray<FileTreeNode> expectedSelects = JsoArray.create();
// Select the first top level dir.
mockSelectionModel.selectNode(AD1, ctrlSignalEvent);
expectedSelects.add(AD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the second top level dir
FileTreeNode BD1 = getNodeByPath(1);
mockSelectionModel.selectNode(BD1, ctrlSignalEvent);
expectedSelects.add(BD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(BD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select deeper. We should not allow cross depth selecting and should
// replace it with just the new select.
FileTreeNode AF2 = getNodeByPath(0, 0);
assertNotNull("Node did not get rendered!", AF2.getRenderedTreeNode());
assertFalse(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(AF2, ctrlSignalEvent);
expectedSelects.clear();
expectedSelects.add(AF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select another peer node.
FileTreeNode CF2 = getNodeByPath(0, 2);
assertNotNull("Node did not get rendered!", CF2.getRenderedTreeNode());
assertFalse(CF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(CF2, ctrlSignalEvent);
expectedSelects.add(CF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(CF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select another peer node.
FileTreeNode BF2 = getNodeByPath(0, 1);
assertNotNull("Node did not get rendered!", BF2.getRenderedTreeNode());
assertFalse(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(BF2, ctrlSignalEvent);
// We need to enforce sort order. AF2, BF2, CF2.
expectedSelects.splice(1, 0, BF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(CF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Ensure that if we ctrl click back higher in the stack that we clear the
// selected list.
FileTreeNode AF1 = getNodeByPath(3);
assertNotNull("Node did not get rendered!", AF1.getRenderedTreeNode());
assertFalse(AF1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(AF1, ctrlSignalEvent);
expectedSelects.clear();
expectedSelects.add(AF1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF1.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(CF2.getRenderedTreeNode().isSelected(resources.treeCss()));
}
/**
* Tests select responses to ctrl clicks that should result in select toggling
*/
public void testCtrlSelectToggle() {
FileTreeNode root = mockTree.getModel().getRoot();
// Render the tree.
mockTree.renderTree(-1);
SignalEvent ctrlSignalEvent = new MockSignalEvent(true, false);
// Select a bunch of nodes at the same tier.
FileTreeNode AD1 = getNodeByPath(0);
assertNotNull("Node did not get rendered!", AD1.getRenderedTreeNode());
assertFalse(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
JsoArray<FileTreeNode> expectedSelects = JsoArray.create();
// Select the first top level dir.
mockSelectionModel.selectNode(AD1, ctrlSignalEvent);
expectedSelects.add(AD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the second top level dir
FileTreeNode BD1 = getNodeByPath(1);
mockSelectionModel.selectNode(BD1, ctrlSignalEvent);
expectedSelects.add(BD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(BD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the first file.
FileTreeNode AF1 = getNodeByPath(3);
assertNotNull("Node did not get rendered!", AF1.getRenderedTreeNode());
assertFalse(AF1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(AF1, ctrlSignalEvent);
expectedSelects.add(AF1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Now toggle the second dir.
mockSelectionModel.selectNode(BD1, ctrlSignalEvent);
expectedSelects.remove(BD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertFalse(BD1.getRenderedTreeNode().isSelected(resources.treeCss()));
}
/**
* Tests select responses to shift clicks that should do range selects.
*/
public void testShiftSelect() {
FileTreeNode root = mockTree.getModel().getRoot();
// Render the tree.
mockTree.renderTree(-1);
SignalEvent shiftSignalEvent = new MockSignalEvent(false, true);
FileTreeNode AD1 = getNodeByPath(0);
assertNotNull("Node did not get rendered!", AD1.getRenderedTreeNode());
assertFalse(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
JsoArray<FileTreeNode> expectedSelects = JsoArray.create();
// Select the first top level dir.
mockSelectionModel.selectNode(AD1, shiftSignalEvent);
expectedSelects.add(AD1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AD1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Shift select the last top level file.
FileTreeNode BF1 = getNodeByPath(4);
mockSelectionModel.selectNode(BF1, shiftSignalEvent);
expectedSelects.add(getNodeByPath(1));
expectedSelects.add(getNodeByPath(2));
expectedSelects.add(getNodeByPath(3));
expectedSelects.add(getNodeByPath(4));
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(BF1.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(getNodeByPath(2).getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(getNodeByPath(3).getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the last file. It should zero the shift selection.
mockSelectionModel.selectNode(BF1, shiftSignalEvent);
expectedSelects.clear();
expectedSelects.add(BF1);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(BF1.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(getNodeByPath(0).getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(getNodeByPath(1).getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(getNodeByPath(2).getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(getNodeByPath(3).getRenderedTreeNode().isSelected(resources.treeCss()));
// Select deeper. We should not allow cross depth selecting and
// should replace it with just the new select.
FileTreeNode AF2 = getNodeByPath(0, 0);
assertNotNull("Node did not get rendered!", AF2.getRenderedTreeNode());
assertFalse(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(AF2, shiftSignalEvent);
expectedSelects.clear();
expectedSelects.add(AF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(BF1.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the adjacent peer node.
FileTreeNode BF2 = getNodeByPath(0, 1);
assertNotNull("Node did not get rendered!", BF2.getRenderedTreeNode());
assertFalse(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(BF2, shiftSignalEvent);
expectedSelects.add(BF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Select the last peer node.
FileTreeNode DF2 = getNodeByPath(0, 3);
assertNotNull("Node did not get rendered!", DF2.getRenderedTreeNode());
assertFalse(DF2.getRenderedTreeNode().isSelected(resources.treeCss()));
// Change the select.
mockSelectionModel.selectNode(DF2, shiftSignalEvent);
expectedSelects.add(getNodeByPath(0, 2));
expectedSelects.add(getNodeByPath(0, 3));
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(DF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertTrue(getNodeByPath(0, 2).getRenderedTreeNode().isSelected(resources.treeCss()));
// Ensure that if we shift click the last one we clear the selected list.
mockSelectionModel.selectNode(DF2, shiftSignalEvent);
expectedSelects.clear();
expectedSelects.add(DF2);
checkNodeArray(expectedSelects, mockSelectionModel.getSelectedNodes());
assertTrue(DF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(AF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(BF2.getRenderedTreeNode().isSelected(resources.treeCss()));
assertFalse(getNodeByPath(0, 2).getRenderedTreeNode().isSelected(resources.treeCss()));
}
@Override
protected void gwtSetUp() throws Exception {
super.gwtSetUp();
resources = GWT.create(TestResources.class);
NodeDataAdapter<FileTreeNode> dataAdapter = new FileTreeNodeDataAdapter();
NodeRenderer<FileTreeNode> dataRenderer = FileTreeNodeRenderer.create(resources);
Tree.Model<FileTreeNode> model =
new Tree.Model<FileTreeNode>(dataAdapter, dataRenderer, resources);
mockTree = new Tree<FileTreeNode>(new Tree.View<FileTreeNode>(resources), model);
mockTree.getModel()
.setRoot(
FileTreeNode.transform(TreeTestUtils
.createMockTree(TreeTestUtils.CLIENT_NODE_INFO_FACTORY)));
mockSelectionModel = mockTree.getSelectionModel();
}
private FileTreeNode getNodeByPath(int... indices) {
FileTreeNode cursor = mockTree.getModel().getRoot();
for (int i = 0; i < indices.length; i++) {
cursor = cursor.getUnifiedChildren().get(indices[i]);
}
return cursor;
}
}