1 package com.atlassian.selenium.keyboard;
2
3 import com.atlassian.selenium.AbstractSeleniumDriver;
4 import com.atlassian.selenium.SeleniumClient;
5 import com.atlassian.selenium.SeleniumKeyHandler;
6 import com.atlassian.webtest.ui.keys.CharacterKey;
7 import com.atlassian.webtest.ui.keys.CharacterKeySequence;
8 import com.atlassian.webtest.ui.keys.Key;
9 import com.atlassian.webtest.ui.keys.KeyEventAware;
10 import com.atlassian.webtest.ui.keys.KeyEventType;
11 import com.atlassian.webtest.ui.keys.KeySequence;
12 import com.atlassian.webtest.ui.keys.KeySequenceBuilder;
13 import com.atlassian.webtest.ui.keys.Sequences;
14 import com.atlassian.webtest.ui.keys.SpecialKey;
15 import com.atlassian.webtest.ui.keys.SpecialKeys;
16 import com.atlassian.webtest.ui.keys.SpecialKeysSequence;
17 import com.atlassian.webtest.ui.keys.TypeMode;
18 import com.atlassian.webtest.ui.keys.TypeModeAware;
19 import com.google.common.collect.Maps;
20
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import static com.google.common.base.Preconditions.checkNotNull;
29
30
31
32
33
34
35 public final class SeleniumTypeWriter extends AbstractSeleniumDriver
36 {
37 private final String target;
38 private final TypeMode defaultMode;
39 private final Map<TypeMode, Typer> typers = Maps.newHashMap();
40
41 public SeleniumTypeWriter(SeleniumClient client, String target, TypeMode defaultMode)
42 {
43 super(client);
44 this.target = checkNotNull(target, "target");
45 validateMode(checkNotNull(defaultMode, "defaultMode"));
46 this.defaultMode = defaultMode;
47 initTypers();
48 }
49
50 private void validateMode(TypeMode defaultMode)
51 {
52 if (defaultMode == TypeMode.DEFAULT)
53 {
54 throw new IllegalArgumentException("DEFAULT TypeMode not allowed");
55 }
56 }
57
58 private void initTypers()
59 {
60 typers.put(TypeMode.INSERT, new InsertingTyper());
61 typers.put(TypeMode.INSERT_WITH_EVENT, new LastCharEventTyper());
62 typers.put(TypeMode.TYPE, new FullEventTyper());
63 }
64
65 public SeleniumTypeWriter type(KeySequence sequence)
66 {
67 typers.get(resolveMode(sequence)).type(sequence);
68 return this;
69 }
70
71 public SeleniumTypeWriter clear()
72 {
73 client.type(target, "");
74 return this;
75 }
76
77 private TypeMode resolveMode(KeySequence sequence)
78 {
79 if (isTypeModeAware(sequence))
80 {
81 if (asTypeModeAware(sequence).typeMode() == TypeMode.DEFAULT)
82 {
83 return defaultMode;
84 }
85 return asTypeModeAware(sequence).typeMode();
86 }
87 return defaultMode;
88 }
89
90 private boolean isTypeModeAware(KeySequence sequence)
91 {
92 return sequence instanceof TypeModeAware;
93 }
94
95 private TypeModeAware asTypeModeAware(KeySequence seq)
96 {
97 return (TypeModeAware) seq;
98 }
99
100 private void setUpModifiers(KeySequence sequence)
101 {
102 for (SeleniumModifierKey key : seleniumModifiersFor(sequence))
103 {
104 key.keyDown(client);
105 }
106 }
107
108 private void tearDownModifiers(KeySequence sequence)
109 {
110 for (SeleniumModifierKey key : seleniumReversedModifiersFor(sequence))
111 {
112 key.keyUp(client);
113 }
114 }
115
116 private Collection<SeleniumModifierKey> seleniumModifiersFor(KeySequence sequence)
117 {
118 return SeleniumModifierKey.forKeys(sequence.withPressed());
119 }
120
121 private Collection<SeleniumModifierKey> seleniumReversedModifiersFor(KeySequence sequence)
122 {
123 List<SeleniumModifierKey> answer = new ArrayList<SeleniumModifierKey>(seleniumModifiersFor(sequence));
124 Collections.reverse(answer);
125 return answer;
126 }
127
128 private void insert(CharacterKeySequence sequence)
129 {
130 setUpModifiers(sequence);
131 try
132 {
133 bareInsert(sequence);
134 }
135 finally
136 {
137 tearDownModifiers(sequence);
138 }
139 }
140
141 private void bareInsert(CharacterKeySequence sequence)
142 {
143 client.type(target, existingValue() + sequence.string());
144 }
145
146 private void fullEventType(CharacterKeySequence sequence)
147 {
148 setUpModifiers(sequence);
149 try
150 {
151 bareFullEventType(sequence);
152 }
153 finally
154 {
155 tearDownModifiers(sequence);
156 }
157 }
158
159 private void bareFullEventType(CharacterKeySequence sequence)
160 {
161 new SeleniumKeyHandler(client, target, extractKeyEvents(sequence), false).typeWithFullKeyEvents(sequence.string());
162 }
163
164 private void lastCharType(CharacterKeySequence sequence)
165 {
166 if (sequence.string().length() == 0)
167 {
168 bareInsert(sequence);
169 return;
170 }
171 setUpModifiers(sequence);
172 try
173 {
174 bareLastCharType(sequence);
175 }
176 finally
177 {
178 tearDownModifiers(sequence);
179 }
180 }
181
182 private void bareLastCharType(CharacterKeySequence sequence)
183 {
184 final String toType = sequence.string();
185 bareInsert(withoutLastChar(toType));
186 client.simulateKeyPressForCharacter(target, lastChar(toType).string().charAt(0), extractKeyEvents(sequence));
187 }
188
189 private CharacterKeySequence withoutLastChar(String toType)
190 {
191 return asCharacterSequence(Sequences.chars(toType.substring(0, toType.length()-1)));
192 }
193
194 private CharacterKeySequence lastChar(String toType)
195 {
196 return asCharacterSequence(Sequences.chars(toType.substring(toType.length()-1)));
197 }
198
199 private void typeSpecialKeys(KeySequence sequence)
200 {
201 if (sequence instanceof SpecialKeys)
202 {
203 typeSpecialKey((SpecialKey) sequence, extractKeyEvents(sequence));
204 }
205 else if (sequence instanceof SpecialKeysSequence)
206 {
207 final SpecialKeysSequence specialKeysSequence = asSpecialKeysSequence(sequence);
208 Collection<KeyEventType> events = specialKeysSequence.keyEvents();
209 for (SpecialKeys specialKey : specialKeysSequence.specialKeys())
210 {
211 typeSpecialKey(specialKey, events);
212 }
213 }
214 else
215 {
216 throw new AssertionError("Should not invoke .typeSpecialKeys for: " + sequence);
217 }
218 }
219
220 private void typeSpecialKey(SpecialKey key, Collection<KeyEventType> events)
221 {
222 client.simulateKeyPressForSpecialKey(target, SeleniumSpecialKeys.forKey(key).keyCode(), events);
223 }
224
225 private void insertMixedSequence(KeySequence sequence)
226 {
227 setUpModifiers(sequence);
228 for (KeySequence subSequence : divide(sequence))
229 {
230 if (subSequence instanceof CharacterKeySequence)
231 {
232 bareInsert(asCharacterSequence(subSequence));
233 }
234 else if (isSpecialKeys(subSequence))
235 {
236 typeSpecialKeys(subSequence);
237 }
238 else
239 {
240 throw new AssertionError("WTF is that? " + sequence);
241 }
242 }
243 tearDownModifiers(sequence);
244 }
245
246 private void lastCharTypeMixedSequence(KeySequence sequence)
247 {
248 setUpModifiers(sequence);
249 List<KeySequence> subSequences = divide(sequence);
250 for (KeySequence subSequence : subSequences.subList(0, subSequences.size()-1))
251 {
252 if (subSequence instanceof CharacterKeySequence)
253 {
254 bareInsert(asCharacterSequence(subSequence));
255 }
256 else if (isSpecialKeys(subSequence))
257 {
258 typeSpecialKeys(subSequence);
259 }
260 else
261 {
262 throw new AssertionError("WTF is that? " + sequence);
263 }
264 }
265 KeySequence last = subSequences.get(subSequences.size()-1);
266 if (last instanceof CharacterKeySequence)
267 {
268 bareLastCharType(asCharacterSequence(last));
269 }
270 else if (isSpecialKeys(last))
271 {
272 typeSpecialKeys(last);
273 }
274 else
275 {
276 throw new AssertionError("WTF is that? " + sequence);
277 }
278 tearDownModifiers(sequence);
279 }
280
281 private void fullEventTypeMixedSequence(KeySequence sequence)
282 {
283 setUpModifiers(sequence);
284 for (KeySequence subSequence : divide(sequence))
285 {
286 if (subSequence instanceof CharacterKeySequence)
287 {
288 bareFullEventType(asCharacterSequence(subSequence));
289 }
290 else if (isSpecialKeys(subSequence))
291 {
292 typeSpecialKeys(subSequence);
293 }
294 else
295 {
296 throw new AssertionError("WTF is that? " + subSequence);
297 }
298 }
299 tearDownModifiers(sequence);
300 }
301
302 private String existingValue()
303 {
304 final String existingValue = client.getValue(target);
305 return existingValue != null ? existingValue : "";
306 }
307
308 private List<KeySequence> divide(KeySequence sequence)
309 {
310 List<KeySequence> answer = new ArrayList<KeySequence>();
311 KeySequenceBuilder charBuilder = newBuilderFrom(sequence);
312 KeySequenceBuilder keysBuilder = newBuilderFrom(sequence);
313 for (Key key : sequence.keys())
314 {
315 if (key instanceof CharacterKey)
316 {
317 keysBuilder = addCollected(answer, keysBuilder);
318 charBuilder.append(((CharacterKey)key).string());
319 }
320 else if (key instanceof SpecialKey)
321 {
322 charBuilder = addCollected(answer, charBuilder);
323 keysBuilder.append(key);
324 }
325 else
326 {
327 throw new IllegalArgumentException("Not suported: " + key);
328 }
329 }
330 addCollected(answer, charBuilder);
331 addCollected(answer, keysBuilder);
332 return answer;
333 }
334
335 private CharacterKeySequence asCharacterSequence(KeySequence seq)
336 {
337 return (CharacterKeySequence) seq;
338 }
339
340 private SpecialKeys asSppecialKeys(KeySequence seq)
341 {
342 return (SpecialKeys) seq;
343 }
344
345 private KeySequenceBuilder addCollected(List<KeySequence> answer, KeySequenceBuilder charBuilder)
346 {
347 if (charBuilder.size() > 0)
348 {
349 answer.add(charBuilder.build());
350 return new KeySequenceBuilder().keyEvents(charBuilder.keyEvents());
351 }
352 return charBuilder;
353 }
354
355 private KeySequenceBuilder newBuilderFrom(KeySequence sequence)
356 {
357 return new KeySequenceBuilder().keyEvents(extractKeyEvents(sequence));
358 }
359
360 private Set<KeyEventType> extractKeyEvents(KeySequence sequence)
361 {
362 if (sequence instanceof KeyEventAware)
363 {
364 return ((KeyEventAware)sequence).keyEvents();
365 }
366 return KeyEventType.ALL;
367 }
368
369 private boolean isSpecialKeys(KeySequence sequence)
370 {
371 return sequence instanceof SpecialKeys || sequence instanceof SpecialKeysSequence;
372 }
373
374 private SpecialKeysSequence asSpecialKeysSequence(final KeySequence sequence)
375 {
376 return (SpecialKeysSequence) sequence;
377 }
378
379 private interface Typer
380 {
381 void type(KeySequence sequence);
382 }
383
384 private class InsertingTyper implements Typer
385 {
386 public void type(KeySequence sequence)
387 {
388 if (sequence instanceof CharacterKeySequence)
389 {
390 insert((CharacterKeySequence)sequence);
391 }
392 else if (isSpecialKeys(sequence))
393 {
394 typeSpecialKeys(sequence);
395 }
396 else
397 {
398 insertMixedSequence(sequence);
399 }
400 }
401 }
402
403 private class LastCharEventTyper implements Typer
404 {
405 public void type(KeySequence sequence)
406 {
407 if (sequence instanceof CharacterKeySequence)
408 {
409 lastCharType((CharacterKeySequence)sequence);
410 }
411 else if (isSpecialKeys(sequence))
412 {
413 typeSpecialKeys(sequence);
414 }
415 else
416 {
417 lastCharTypeMixedSequence(sequence);
418 }
419 }
420 }
421
422 private class FullEventTyper implements Typer
423 {
424 public void type(KeySequence sequence)
425 {
426 if (sequence instanceof CharacterKeySequence)
427 {
428 fullEventType((CharacterKeySequence)sequence);
429 }
430 else if (isSpecialKeys(sequence))
431 {
432 typeSpecialKeys(sequence);
433 }
434 else
435 {
436 fullEventTypeMixedSequence(sequence);
437 }
438 }
439 }
440
441
442 }