// 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.editor.input; import com.google.collide.client.util.input.KeyCodeMap; import com.google.collide.client.util.input.ModifierKeys; import com.google.gwt.junit.client.GWTTestCase; import elemental.events.KeyboardEvent.KeyCode; import org.waveprotocol.wave.client.common.util.SignalEvent; /** * Test cases for input handling, switching between input modes+schemes, and * stream+event shortcut callbacks to keyboard events. * * */ public class InputModeTest extends GWTTestCase { private TestScheme inputScheme; class TestScheme extends InputScheme { private int flag = 0; private int flagVal = 1; public int getFlag() { return flag; } /** * The value to set flag to when setFlag() is called */ public void initFlag(int val, int i) { flagVal = i; flag = val; } public void initFlag(int i) { flagVal = i; } // should only be called by test subclasses protected void setFlag() { flag = flagVal; } protected void setFlag(int i) { flag = i; } public void setup() {} public void teardown() {} } /** * Used to record internal function calls */ public abstract class TestMode extends InputMode { private int flag = 0; private int flagVal = 1; public int getFlag() { return flag; } /** * The value to set flag to when setFlag() is called */ public void initFlag(int val, int i) { flagVal = i; flag = val; } public void initFlag(int i) { flagVal = i; } // should only be called by test subclasses protected void setFlag() { flag = flagVal; } protected void setFlag(int i) { flag = i; } } /** * Test mode 1 to install into TestScheme * * Sets internal flag to the signal's keycode and returns true for defaultInput */ public class TestMode1 extends TestMode { public void setup() { setFlag(); } public void teardown() { setFlag(); } public boolean onDefaultInput(SignalEvent e, char text) { setFlag(e.getKeyCode()); return true; } public boolean onDefaultPaste(SignalEvent e, String text) { return false; } } /** * Test mode 2 to install into TestScheme * * Returns false on defaultInput */ public class TestMode2 extends TestMode { public void setup() { setFlag(); } public void teardown() { setFlag(); } public boolean onDefaultInput(SignalEvent e, char text) { return false; } public boolean onDefaultPaste(SignalEvent e, String text) { return false; } } /** * Test mode 3 to install into TestScheme * * defaultInput sets flag equal to -1 and returns true * defaultInputMulti sets flag equal to length of event string and returns false */ public class TestMode3 extends TestMode { public void setup() { setFlag(); } public void teardown() { setFlag(); } public boolean onDefaultInput(SignalEvent e, char text) { setFlag(-1); return true; } public boolean onDefaultPaste(SignalEvent e, String text) { setFlag(text.length()); return false; } } /* (non-Javadoc) * @see com.google.gwt.junit.client.GWTTestCase#getModuleName() */ @Override public String getModuleName() { return "com.google.collide.client.TestCode"; } /** * Setup client environment */ @Override protected void gwtSetUp() throws Exception { super.gwtSetUp(); inputScheme = new TestScheme(); } /** * Tests a scheme with no modes installed */ public void testNoModes() { SignalEvent sig = new TestSignalEvent('a'); // user typed an "a" character assertFalse(sendSignal(sig)); // returns false when no modes are installed } /** * Test mode switching */ public void testModeSwitch() { TestMode mode1 = new TestMode1(); mode1.initFlag(2); inputScheme.addMode(1, mode1); assertEquals(inputScheme.getMode(), null); inputScheme.switchMode(1); // mode1.setup() should have been called assertEquals(mode1.getFlag(), 2); TestMode mode2 = new TestMode2(); mode1.initFlag(3); mode2.initFlag(4); inputScheme.addMode(2, mode2); // should still be in mode1 assertEquals(inputScheme.getMode(), mode1); inputScheme.switchMode(2); // see if mode1.teardown() and mode2.setup() were called assertEquals(inputScheme.getMode(), mode2); assertEquals(mode1.getFlag(), 3); assertEquals(mode2.getFlag(), 4); } /** * Test signal handling */ public void testSignalHandle() { TestMode mode1 = new TestMode1(); inputScheme.addMode(1, mode1); inputScheme.switchMode(1); TestSignalEvent sig = new TestSignalEvent('a'); assertTrue(sendSignal(sig)); // mode1.defaultInput() should return true assertEquals(mode1.getFlag(), 'a'); // flag should be set to the keyCode } /** * Test shortcuts * * All TestSignalEvents should be intialized with uppercase letters or constants * from KeyCode.* to simulate keyboard input */ public void testShortcuts() { TestMode mode2 = new TestMode2(); inputScheme.addMode(2, mode2); inputScheme.switchMode(2); // CMD+ALT+s mode2.addShortcut(new EventShortcut(ModifierKeys.ACTION | ModifierKeys.ALT, 's') { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // SHIFT+TAB mode2.addShortcut(new EventShortcut(ModifierKeys.SHIFT, KeyCodeMap.TAB) { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // CMD+\ mode2.addShortcut(new EventShortcut(ModifierKeys.ACTION, '\\') { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // ; mode2.addShortcut(new EventShortcut(ModifierKeys.NONE, ';') { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // . mode2.addShortcut(new EventShortcut(ModifierKeys.ALT, '.') { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // PAGE_UP mode2.addShortcut(new EventShortcut(ModifierKeys.NONE, KeyCodeMap.PAGE_UP) { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } } ); // feed in javascript keycode representations TestSignalEvent evt1 = new TestSignalEvent('A'); TestSignalEvent evt2 = new TestSignalEvent('A', ModifierKeys.ACTION, ModifierKeys.ALT); TestSignalEvent evt3 = new TestSignalEvent('S'); TestSignalEvent evt4 = new TestSignalEvent('S', ModifierKeys.ACTION, ModifierKeys.ALT); TestSignalEvent evt5 = new TestSignalEvent(KeyCode.TAB, ModifierKeys.SHIFT); TestSignalEvent evt6 = new TestSignalEvent(KeyCode.BACKSLASH, ModifierKeys.ACTION); TestSignalEvent evt7 = new TestSignalEvent(KeyCode.SEMICOLON); TestSignalEvent evt8 = new TestSignalEvent(KeyCode.PERIOD, ModifierKeys.ALT); TestSignalEvent evt9 = new TestSignalEvent(KeyCode.PAGE_UP); // evt1..3 shouldn't fire the callback, only evt4..6 should inputScheme.initFlag(0, 1); assertFalse(sendSignal(evt1)); assertEquals(inputScheme.getFlag(), 0); assertFalse(sendSignal(evt2)); assertEquals(inputScheme.getFlag(), 0); assertFalse(sendSignal(evt3)); assertEquals(inputScheme.getFlag(), 0); assertTrue(sendSignal(evt4)); assertEquals(inputScheme.getFlag(), 1); inputScheme.initFlag(0, 2); assertTrue(sendSignal(evt5)); assertEquals(inputScheme.getFlag(), 2); inputScheme.initFlag(0, 3); assertTrue(sendSignal(evt6)); assertEquals(inputScheme.getFlag(), 3); inputScheme.initFlag(0, 4); assertTrue(sendSignal(evt7)); assertEquals(inputScheme.getFlag(), 4); inputScheme.initFlag(0, 5); assertTrue(sendSignal(evt8)); assertEquals(inputScheme.getFlag(), 5); inputScheme.initFlag(0, 6); assertTrue(sendSignal(evt9)); assertEquals(inputScheme.getFlag(), 6); // now try out StreamShortcuts, such as vi quit: ":q!" mode2.addShortcut(new StreamShortcut(":q!") { @Override public boolean event(InputScheme scheme, SignalEvent event) { // callback in here ((TestScheme) scheme).setFlag(); return true; } }); TestSignalEvent x = new TestSignalEvent('X'); TestSignalEvent excl = new TestSignalEvent('1', ModifierKeys.SHIFT); // ! TestSignalEvent q = new TestSignalEvent('Q'); TestSignalEvent upperQ = new TestSignalEvent('Q', ModifierKeys.SHIFT); TestSignalEvent colon = new TestSignalEvent(KeyCode.SEMICOLON, null, ModifierKeys.SHIFT); inputScheme.initFlag(0, 2); assertFalse(sendSignal(x)); // random entry assertEquals(inputScheme.getFlag(), 0); assertTrue(sendSignal(colon)); // beginning of command assertEquals(inputScheme.getFlag(), 0); assertFalse(sendSignal(upperQ)); // test uppercase vs lowercase assertEquals(inputScheme.getFlag(), 0); assertFalse(sendSignal(excl)); assertEquals(inputScheme.getFlag(), 0); // do the combo :q! assertTrue(sendSignal(colon)); assertTrue(sendSignal(q)); assertTrue(sendSignal(excl)); assertEquals(inputScheme.getFlag(), 2); // triggers callback assertFalse(sendSignal(x)); // should reset } /** * Test defaultMultiInput for longer strings * * All strings > 1 char should skip shortcut system entirely */ /*public void testMultiInput() { TestMode mode3 = new TestMode3(); inputScheme.addMode(3, mode3); inputScheme.switchMode(3); TestSignalEvent paste = new TestSignalEvent('p', ModifierKeys.CMD); String text = "Test multi char paste"; assertFalse(sendStringEvent(paste, text)); assertEquals(inputScheme.getFlag(), text.length()); String oneletter = "A"; assertTrue(sendStringEvent(paste, oneletter)); assertEquals(inputScheme.getFlag(), -1); }*/ /** * Sends the signal to the current installed InputScheme */ private boolean sendSignal(SignalEvent sig) { return inputScheme.handleEvent(sig, ""); } private boolean sendStringEvent(SignalEvent sig, String text) { return inputScheme.handleEvent(sig, text); } }