1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package androidx.emoji.text;
17
18import static androidx.emoji.util.Emoji.EMOJI_FLAG;
19import static androidx.emoji.util.Emoji.EMOJI_GENDER;
20import static androidx.emoji.util.Emoji.EMOJI_WITH_ZWJ;
21import static androidx.emoji.util.EmojiMatcher.hasEmoji;
22import static androidx.emoji.util.EmojiMatcher.hasEmojiCount;
23import static androidx.emoji.util.KeyboardUtil.altDel;
24import static androidx.emoji.util.KeyboardUtil.ctrlDel;
25import static androidx.emoji.util.KeyboardUtil.del;
26import static androidx.emoji.util.KeyboardUtil.fnDel;
27import static androidx.emoji.util.KeyboardUtil.forwardDel;
28import static androidx.emoji.util.KeyboardUtil.shiftDel;
29import static androidx.emoji.util.KeyboardUtil.zero;
30
31import static org.hamcrest.MatcherAssert.assertThat;
32import static org.hamcrest.core.IsNot.not;
33import static org.junit.Assert.assertEquals;
34import static org.junit.Assert.assertFalse;
35import static org.junit.Assert.assertTrue;
36
37import android.support.test.filters.SdkSuppress;
38import android.support.test.filters.SmallTest;
39import android.support.test.runner.AndroidJUnit4;
40import android.text.Editable;
41import android.text.Selection;
42import android.text.SpannableStringBuilder;
43import android.view.KeyEvent;
44
45import androidx.emoji.util.TestString;
46
47import org.junit.Before;
48import org.junit.BeforeClass;
49import org.junit.Test;
50import org.junit.runner.RunWith;
51
52@SmallTest
53@RunWith(AndroidJUnit4.class)
54@SdkSuppress(minSdkVersion = 19)
55public class HardDeleteTest {
56
57    private TestString mTestString;
58    private Editable mEditable;
59
60    @BeforeClass
61    public static void setupEmojiCompat() {
62        EmojiCompat.reset(TestConfigBuilder.config());
63    }
64
65    @Before
66    public void setup() {
67        mTestString = new TestString(EMOJI_WITH_ZWJ).withPrefix().withSuffix();
68        mEditable = new SpannableStringBuilder(mTestString.toString());
69        EmojiCompat.get().process(mEditable);
70        assertThat(mEditable, hasEmojiCount(1));
71        assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
72    }
73
74    @Test
75    public void testOnKeyDown_doesNotDelete_whenKeyCodeIsNotDelOrForwardDel() {
76        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
77        final KeyEvent event = zero();
78        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
79        assertThat(mEditable, hasEmoji());
80        assertEquals(mTestString.toString(), mEditable.toString());
81    }
82
83    @Test
84    public void testOnKeyDown_doesNotDelete_withOtherModifiers() {
85        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
86        final KeyEvent event = fnDel();
87        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
88        assertThat(mEditable, hasEmoji());
89        assertEquals(mTestString.toString(), mEditable.toString());
90    }
91
92    @Test
93    public void testOnKeyDown_doesNotDelete_withAltModifier() {
94        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
95        final KeyEvent event = altDel();
96        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
97        assertThat(mEditable, hasEmoji());
98        assertEquals(mTestString.toString(), mEditable.toString());
99    }
100
101    @Test
102    public void testOnKeyDown_doesNotDelete_withCtrlModifier() {
103        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
104        final KeyEvent event = ctrlDel();
105        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
106        assertThat(mEditable, hasEmoji());
107        assertEquals(mTestString.toString(), mEditable.toString());
108    }
109
110    @Test
111    public void testOnKeyDown_doesNotDelete_withShiftModifier() {
112        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
113        final KeyEvent event = shiftDel();
114        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
115        assertThat(mEditable, hasEmoji());
116        assertEquals(mTestString.toString(), mEditable.toString());
117    }
118
119    @Test
120    public void testOnKeyDown_doesNotDelete_withSelectionLongerThanZeroLength() {
121        // when there is a selection which is longer than 0, it should not delete.
122        Selection.setSelection(mEditable, 0, mEditable.length());
123        final KeyEvent event = del();
124        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
125        assertThat(mEditable, hasEmoji());
126        assertEquals(mTestString.toString(), mEditable.toString());
127    }
128
129    @Test
130    public void testOnKeyDown_doesNotDelete_withoutEmojiSpans() {
131        final Editable editable = new SpannableStringBuilder("abc");
132        Selection.setSelection(editable, 1);
133        final KeyEvent event = del();
134        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
135        assertThat(mEditable, hasEmoji());
136        assertEquals(mTestString.toString(), mEditable.toString());
137    }
138
139    @Test
140    public void testOnKeyDown_doesNotDelete_whenNoSpansBefore() {
141        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
142        final KeyEvent event = del();
143        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
144        assertThat(mEditable, hasEmoji());
145        assertEquals(mTestString.toString(), mEditable.toString());
146    }
147
148    @Test
149    public void testOnKeyDown_deletesEmoji() {
150        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
151        final KeyEvent event = del();
152        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
153        assertThat(mEditable, not(hasEmoji()));
154        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
155    }
156
157    @Test
158    public void testOnKeyDown_doesNotForwardDeleteEmoji_withNoSpansAfter() {
159        Selection.setSelection(mEditable, mTestString.emojiEndIndex());
160        final KeyEvent event = forwardDel();
161        assertFalse(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
162        assertThat(mEditable, hasEmoji());
163        assertEquals(mTestString.toString(), mEditable.toString());
164    }
165
166    @Test
167    public void testOnKeyDown_forwardDeletesEmoji() {
168        Selection.setSelection(mEditable, mTestString.emojiStartIndex());
169        final KeyEvent event = forwardDel();
170        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, event.getKeyCode(), event));
171        assertThat(mEditable, not(hasEmoji()));
172        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
173    }
174
175    @Test
176    public void testOnKeyDown_deletesEmoji_ifSelectionIsInSpanBoundaries() {
177        Selection.setSelection(mEditable, mTestString.emojiStartIndex() + 1);
178        final KeyEvent delEvent = del();
179        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, delEvent.getKeyCode(), delEvent));
180        assertThat(mEditable, not(hasEmoji()));
181        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
182    }
183
184    @Test
185    public void testOnKeyDown_deletesEmoji_ifSelectionIsInSpanBoundaries_withForwardDel() {
186        Selection.setSelection(mEditable, mTestString.emojiStartIndex() + 1);
187        final KeyEvent forwardDelEvent = forwardDel();
188        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, forwardDelEvent.getKeyCode(),
189                forwardDelEvent));
190        assertThat(mEditable, not(hasEmoji()));
191        assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
192    }
193
194    @Test
195    public void testOnKeyDown_deletesOnlyEmojiBeforeTheCursor() {
196        // contains three emojis
197        mTestString = new TestString(EMOJI_FLAG)
198                .append(EMOJI_WITH_ZWJ)
199                .append(EMOJI_GENDER)
200                .withPrefix().withSuffix();
201        mEditable = new SpannableStringBuilder(mTestString.toString());
202        EmojiCompat.get().process(mEditable);
203
204        // put the cursor after the second emoji
205        Selection.setSelection(mEditable, mTestString.emojiStartIndex()
206                + EMOJI_FLAG.charCount()
207                + EMOJI_WITH_ZWJ.charCount());
208
209        // delete
210        final KeyEvent forwardDelEvent = del();
211        assertTrue(EmojiCompat.handleOnKeyDown(mEditable, forwardDelEvent.getKeyCode(),
212                forwardDelEvent));
213
214        assertThat(mEditable, hasEmojiCount(2));
215        assertThat(mEditable, hasEmoji(EMOJI_FLAG));
216        assertThat(mEditable, hasEmoji(EMOJI_GENDER));
217
218        assertEquals(new TestString(EMOJI_FLAG).append(EMOJI_GENDER)
219                .withPrefix().withSuffix().toString(), mEditable.toString());
220    }
221
222}
223