1 package io.guixer.lang;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static com.google.common.collect.Lists.newArrayList;
5 import static com.google.common.collect.Maps.newHashMap;
6 import static io.guixer.lang.GuixerSyntaxErrorType.GROUP_NAME_SHOULD_NOT_BE_EMPTY;
7 import static io.guixer.lang.GuixerSyntaxErrorType.GROUP_NAME_SHOULD_START_BY_AN_UPPERCASE_CHARACTER;
8 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_ASSERT_ABSENT_SHOULD_NOT_BE_MIXED;
9 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_ASSERT_PRESENT_SHOULD_NOT_BE_MIXED;
10 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_CALL_SHOULD_NOT_BE_MIXED;
11 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_CLEAR_SHOULD_NOT_BE_MIXED;
12 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_CLICK_SHOULD_NOT_BE_MIXED;
13 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_GET_SHOULD_NOT_BE_MIXED;
14 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_SEND_KEYS_SHOULD_NOT_BE_MIXED;
15 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_SLEEP_SHOULD_NOT_BE_MIXED;
16 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_SWITCH_TO_FRAME_SHOULD_NOT_BE_MIXED;
17 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_WAIT_FOR_NOT_SHOULD_NOT_BE_MIXED;
18 import static io.guixer.lang.GuixerSyntaxErrorType.SEQ_AND_WAIT_FOR_SHOULD_NOT_BE_MIXED;
19 import static net.avcompris.commons3.databeans.DataBeans.instantiate;
20 import static org.apache.commons.lang3.StringUtils.isBlank;
21 import static org.apache.commons.lang3.StringUtils.join;
22 import static org.apache.commons.lang3.StringUtils.substringAfter;
23 import static org.apache.commons.lang3.StringUtils.substringBefore;
24 import static org.apache.commons.lang3.StringUtils.substringBetween;
25
26 import java.io.File;
27 import java.io.IOException;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.annotation.Nullable;
32
33 import org.apache.commons.lang3.NotImplementedException;
34
35 import com.google.common.collect.ImmutableList;
36
37 import io.guixer.lang.command.AssertAbsentCommand;
38 import io.guixer.lang.command.AssertFalseCommand;
39 import io.guixer.lang.command.AssertPresentCommand;
40 import io.guixer.lang.command.AssertTrueCommand;
41 import io.guixer.lang.command.AttributeCommand;
42 import io.guixer.lang.command.CallCommand;
43 import io.guixer.lang.command.ClearCommand;
44 import io.guixer.lang.command.ClickCommand;
45 import io.guixer.lang.command.Command;
46 import io.guixer.lang.command.ExecuteScriptCommand;
47 import io.guixer.lang.command.ExtCommand;
48 import io.guixer.lang.command.FailureCommand;
49 import io.guixer.lang.command.GetCommand;
50 import io.guixer.lang.command.MessageCommand;
51 import io.guixer.lang.command.SendKeysCommand;
52 import io.guixer.lang.command.SetMaskedVariableCommand;
53 import io.guixer.lang.command.SetVariableCommand;
54 import io.guixer.lang.command.SleepCommand;
55 import io.guixer.lang.command.SuccessCommand;
56 import io.guixer.lang.command.SwitchToFrameCommand;
57 import io.guixer.lang.command.TagCommand;
58 import io.guixer.lang.command.WaitForCommand;
59 import io.guixer.lang.command.WaitForNotCommand;
60 import io.guixer.types.AttributeScope;
61
62 public abstract class AbstractGuixerScenarioLoader {
63
64 public static GuixerScenario load(
65 final File yamlFile,
66 final MyYamlStrategy yamlStrategy
67 ) throws IOException, GuixerSyntaxException {
68
69 checkNotNull(yamlFile, "yamlFile");
70
71 new GuixerSyntaxChecker().check(yamlFile);
72
73 checkNotNull(yamlStrategy, "yamlStrategy");
74
75 final MyYaml yaml = yamlStrategy.loadYaml(yamlFile);
76
77 final String scenarioName = yaml.get("name").asString();
78
79 final Map<String, String> environmentVariables = newHashMap();
80
81 if (yaml.has("environment")) {
82
83 final MyYaml environmentYaml = yaml.get("environment");
84
85 for (final String key : environmentYaml.keysAsStrings()) {
86
87 final String value = environmentYaml.get(key).asString();
88
89 environmentVariables.put(key, value);
90 }
91 }
92
93 final Map<String, String> attributes = newHashMap();
94
95 if (yaml.has("attributes")) {
96
97 final MyYaml attributesYaml = yaml.get("attributes");
98
99 for (final String key : attributesYaml.keysAsStrings()) {
100
101 @Nullable
102 final String value = attributesYaml.get(key).isArray()
103 ? null
104 : attributesYaml.get(key).asString();
105
106 attributes.put(key, value);
107 }
108 }
109
110 final MutableGuixerScenario scenario = instantiate(MutableGuixerScenario.class)
111 .setName(scenarioName)
112 .setEnvironmentVariables(environmentVariables)
113 .setAttributes(attributes);
114
115 for (final Step step : loadSteps(yaml.get("steps"))) {
116
117 scenario.addToSteps(step);
118 }
119
120 return scenario;
121 }
122
123 private interface MutableGuixerScenario extends GuixerScenario {
124
125 MutableGuixerScenario setName(
126 String name
127 );
128
129 MutableGuixerScenario addToSteps(
130 Step step
131 );
132
133 MutableGuixerScenario setEnvironmentVariables(
134 Map<String, String> environmentVariables
135 );
136
137 MutableGuixerScenario setAttributes(
138 Map<String, String> attributes
139 );
140 }
141
142 private interface MutableGroupStep extends GroupStep {
143
144 MutableGroupStep setName(
145 String name
146 );
147
148 MutableGroupStep addToSteps(
149 Step step
150 );
151 }
152
153 private interface MutableStatusStep extends StatusStep {
154
155 MutableStatusStep setLabel(
156 String label
157 );
158 }
159
160 private interface MutableSetLaneStep extends SetLaneStep {
161
162 MutableSetLaneStep setLaneId(
163 String laneId
164 );
165 }
166
167 private interface MutableAtomicStep extends AtomicStep {
168
169 MutableAtomicStep setIntent(
170 String intent
171 );
172
173 MutableAtomicStep setGetCommand(
174 @Nullable GetCommand get
175 );
176
177 MutableAtomicStep setClearCommand(
178 @Nullable ClearCommand clear
179 );
180
181 MutableAtomicStep setSendKeysCommand(
182 @Nullable SendKeysCommand sendKeys
183 );
184
185 MutableAtomicStep setClickCommand(
186 @Nullable ClickCommand click
187 );
188
189 MutableAtomicStep setCallCommand(
190 @Nullable CallCommand call
191 );
192
193 MutableAtomicStep setExecuteScriptCommand(
194 @Nullable ExecuteScriptCommand executeScript
195 );
196
197 MutableAtomicStep setSwitchToFrameCommand(
198 @Nullable SwitchToFrameCommand switchToFrame
199 );
200
201 MutableAtomicStep setSleepCommand(
202 @Nullable SleepCommand sleep
203 );
204
205 MutableAtomicStep setWaitForCommand(
206 @Nullable WaitForCommand waitFor
207 );
208
209 MutableAtomicStep setWaitForNotCommand(
210 @Nullable WaitForNotCommand waitForNot
211 );
212
213 MutableAtomicStep setFailureCommand(
214 @Nullable FailureCommand failure
215 );
216
217 MutableAtomicStep setSuccessCommand(
218 @Nullable SuccessCommand success
219 );
220
221 MutableAtomicStep setMessageCommand(
222 @Nullable MessageCommand message
223 );
224
225 MutableAtomicStep setAssertPresentCommand(
226 @Nullable AssertPresentCommand assertPresent
227 );
228
229 MutableAtomicStep setAssertAbsentCommand(
230 @Nullable AssertAbsentCommand assertAbsent
231 );
232
233 MutableAtomicStep setAssertTrueCommand(
234 @Nullable AssertTrueCommand assertTrue
235 );
236
237 MutableAtomicStep setAssertFalseCommand(
238 @Nullable AssertFalseCommand assertFalse
239 );
240
241 MutableAtomicStep setSetVariableCommand(
242 @Nullable SetVariableCommand setVariable
243 );
244
245 MutableAtomicStep setSetMaskedVariableCommand(
246 @Nullable SetMaskedVariableCommand setMaskedVariable
247 );
248
249 MutableAtomicStep setTagCommand(
250 @Nullable TagCommand tag
251 );
252
253 MutableAtomicStep setAttributeCommand(
254 @Nullable AttributeCommand attribute
255 );
256
257 MutableAtomicStep setExtCommand(
258 @Nullable ExtCommand ext
259 );
260
261 MutableAtomicStep setTakeScreenshot(
262 boolean takeScreenshot
263 );
264
265 MutableAtomicStep addToSeq(
266 Command command
267 );
268 }
269
270 private static ImmutableList<Step> loadSteps(
271 final MyYaml yaml
272 ) throws GuixerSyntaxException {
273
274 checkNotNull(yaml, "yaml");
275
276 if (!yaml.isArray()) {
277
278 throw new IllegalArgumentException("\"steps\" should be an array in YAML, but was: " + yaml);
279 }
280
281 final List<Step> steps = newArrayList();
282
283 for (final MyYaml item : yaml.items()) {
284
285 final Step step = loadStep(item);
286
287 steps.add(step);
288 }
289
290 return ImmutableList.copyOf(steps);
291 }
292
293 @Nullable
294 private static String getExtOrNull(
295 final MyYaml yaml
296 ) {
297
298 for (final String key : yaml.keysAsStrings()) {
299
300 if (key.startsWith("ext[") && key.endsWith("]")) {
301
302 return yaml.get(key).asString();
303 }
304 }
305
306 return null;
307 }
308
309 @Nullable
310 private static String getExtDirectiveOrNull(
311 final MyYaml yaml
312 ) {
313
314 for (final String key : yaml.keysAsStrings()) {
315
316 if (key.startsWith("ext[") && key.endsWith("]")) {
317
318 return key;
319 }
320 }
321
322 return null;
323 }
324
325 private static Step loadStep(
326 final MyYaml yaml
327 ) throws GuixerSyntaxException {
328
329 if (!yaml.isMap()) {
330
331 throw new IllegalStateException("step should be a map in YAML, but was: " + yaml);
332 }
333
334 @Nullable
335 final String intent = getStringOrNull(yaml, "intent");
336
337 @Nullable
338 final String setLane = getStringOrNull(yaml, "setLane");
339
340 @Nullable
341 final String get = getStringOrNull(yaml, "get");
342
343 @Nullable
344 final String clear = getStringOrNull(yaml, "clear");
345
346 @Nullable
347 final String sendKeys = getStringOrNull(yaml, "sendKeys");
348
349 @Nullable
350 final String click = getStringOrNull(yaml, "click");
351
352 @Nullable
353 final String call = getStringOrNull(yaml, "call");
354
355 @Nullable
356 final String executeScript = getStringOrNull(yaml, "executeScript");
357
358 @Nullable
359 final String switchToFrame = getStringOrNull(yaml, "switchToFrame");
360
361 @Nullable
362 final String sleep = getStringOrNull(yaml, "sleep");
363
364 @Nullable
365 final String waitFor = getStringOrNull(yaml, "waitFor");
366
367 @Nullable
368 final String waitForNot = getStringOrNull(yaml, "waitForNot");
369
370 @Nullable
371 final String failure = getStringOrNull(yaml, "failure");
372
373 @Nullable
374 final String success = getStringOrNull(yaml, "success");
375
376 @Nullable
377 final String message = getStringOrNull(yaml, "message");
378
379 @Nullable
380 final String assertPresent = getStringOrNull(yaml, "assertPresent");
381
382 @Nullable
383 final String assertAbsent = getStringOrNull(yaml, "assertAbsent");
384
385 @Nullable
386 final String setVariable = getStringOrNull(yaml, "setVariable");
387
388 @Nullable
389 final String setMaskedVariable = getStringOrNull(yaml, "setMaskedVariable");
390
391 @Nullable
392 final String tag = getStringOrNull(yaml, "tag",
393 "tag[STEP]", "tag[RUN]", "tag[UPLOAD]");
394
395 @Nullable
396 final String attribute = getStringOrNull(yaml, "attribute",
397 "attribute[STEP]", "attribute[RUN]", "attribute[UPLOAD]");
398
399 @Nullable
400 final String ext = getExtOrNull(yaml);
401
402 @Nullable
403 final Boolean takeScreenshot = getBooleanOrNull(yaml, "takeScreenshot");
404
405 @Nullable
406 final String status = getStringOrNull(yaml, "status");
407
408 if (status != null) {
409
410 return instantiate(MutableStatusStep.class)
411 .setLabel(status);
412
413 } else if (setLane != null) {
414
415 return instantiate(MutableSetLaneStep.class)
416 .setLaneId(setLane);
417
418 } else if (intent == null
419 && get == null
420 && clear == null
421 && sendKeys == null
422 && click == null
423 && call == null
424 && executeScript == null
425 && switchToFrame == null
426 && sleep == null
427 && waitFor == null
428 && waitForNot == null
429 && success == null
430 && failure == null
431 && message == null
432 && assertPresent == null
433 && assertAbsent == null
434 && setVariable == null
435 && setMaskedVariable == null
436 && tag == null
437 && attribute == null
438 && ext == null
439 && takeScreenshot == null) {
440
441 final String groupName = yaml.uniqueKey();
442
443 if (isBlank(groupName)) {
444
445 throw new GuixerSyntaxException("groupName should not be empty: " + groupName,
446 GROUP_NAME_SHOULD_NOT_BE_EMPTY);
447 }
448
449 if (!Character.isUpperCase(groupName.charAt(0))) {
450
451 throw new GuixerSyntaxException(
452 "groupName should start by an uppercase character, but was: " + groupName,
453 GROUP_NAME_SHOULD_START_BY_AN_UPPERCASE_CHARACTER);
454 }
455
456 final MutableGroupStep group = instantiate(MutableGroupStep.class)
457 .setName(groupName);
458
459 for (final Step step : loadSteps(yaml.get(groupName))) {
460
461 group.addToSteps(step);
462 }
463
464 return group;
465 }
466
467 final MutableAtomicStep step = instantiate(MutableAtomicStep.class)
468 .setIntent(intent)
469 .setGetCommand(loadGetCommand(get))
470 .setClearCommand(loadClearCommand(clear))
471 .setSendKeysCommand(loadSendKeysCommand(sendKeys))
472 .setClickCommand(loadClickCommand(click))
473 .setCallCommand(loadCallCommand(call))
474 .setExecuteScriptCommand(loadExecuteScriptCommand(executeScript))
475 .setSwitchToFrameCommand(loadSwitchToFrameCommand(switchToFrame))
476 .setSleepCommand(loadSleepCommand(sleep))
477 .setWaitForCommand(loadWaitForCommand(waitFor))
478 .setWaitForNotCommand(loadWaitForNotCommand(waitForNot))
479 .setFailureCommand(loadFailureCommand(failure))
480 .setSuccessCommand(loadSuccessCommand(success))
481 .setMessageCommand(loadMessageCommand(message))
482 .setAssertPresentCommand(loadAssertPresentCommand(assertPresent))
483 .setAssertAbsentCommand(loadAssertAbsentCommand(assertAbsent))
484 .setSetVariableCommand(loadSetVariableCommand(setVariable))
485 .setSetMaskedVariableCommand(loadSetMaskedVariableCommand(setMaskedVariable))
486 .setTagCommand(loadTagCommand(getTagDirectiveOrNull(yaml), tag))
487 .setAttributeCommand(loadAttributeCommand(getAttributeDirectiveOrNull(yaml), attribute))
488 .setExtCommand(loadExtCommand(getExtDirectiveOrNull(yaml), ext))
489 .setTakeScreenshot(takeScreenshot == null ? false : takeScreenshot);
490
491 if (yaml.has("seq")) {
492
493 if (get != null) {
494 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"get\" should not be mixed.",
495 SEQ_AND_GET_SHOULD_NOT_BE_MIXED);
496 } else if (clear != null) {
497 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"clear\" should not be mixed.",
498 SEQ_AND_CLEAR_SHOULD_NOT_BE_MIXED);
499 } else if (sendKeys != null) {
500 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"sendKeys\" should not be mixed.",
501 SEQ_AND_SEND_KEYS_SHOULD_NOT_BE_MIXED);
502 } else if (click != null) {
503 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"click\" should not be mixed.",
504 SEQ_AND_CLICK_SHOULD_NOT_BE_MIXED);
505 } else if (call != null) {
506 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"call\" should not be mixed.",
507 SEQ_AND_CALL_SHOULD_NOT_BE_MIXED);
508 } else if (switchToFrame != null) {
509 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"switchToFrame\" should not be mixed.",
510 SEQ_AND_SWITCH_TO_FRAME_SHOULD_NOT_BE_MIXED);
511 } else if (sleep != null) {
512 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"sleep\" should not be mixed.",
513 SEQ_AND_SLEEP_SHOULD_NOT_BE_MIXED);
514 } else if (waitFor != null) {
515 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"waitFor\" should not be mixed.",
516 SEQ_AND_WAIT_FOR_SHOULD_NOT_BE_MIXED);
517 } else if (waitForNot != null) {
518 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"waitForNot\" should not be mixed.",
519 SEQ_AND_WAIT_FOR_NOT_SHOULD_NOT_BE_MIXED);
520 } else if (assertPresent != null) {
521 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"assertPresent\" should not be mixed.",
522 SEQ_AND_ASSERT_PRESENT_SHOULD_NOT_BE_MIXED);
523 } else if (assertAbsent != null) {
524 throw new GuixerSyntaxException("Mixed declaration: \"seq\" and \"assertAbsent\" should not be mixed.",
525 SEQ_AND_ASSERT_ABSENT_SHOULD_NOT_BE_MIXED);
526 }
527
528 for (final MyYaml commandYaml : yaml.get("seq").items()) {
529
530 final String commandLabel = commandYaml.uniqueKey();
531
532 final MyYaml commandValueYaml = commandYaml.get(commandLabel);
533
534 final String commandValue;
535
536 if (commandValueYaml.isArray()) {
537
538 final List<String> itemsAsStrings = newArrayList();
539
540 commandValueYaml.items().forEach(item ->
541
542 itemsAsStrings.add(item.asString()));
543
544 commandValue = join(itemsAsStrings, " ");
545
546 } else {
547
548 commandValue = commandValueYaml.asString();
549 }
550
551 final Command command;
552
553 if ("get".equals(commandLabel)) {
554 command = loadGetCommand(commandValue);
555 } else if ("clear".equals(commandLabel)) {
556 command = loadClearCommand(commandValue);
557 } else if ("sendKeys".equals(commandLabel)) {
558 command = loadSendKeysCommand(commandValue);
559 } else if ("click".equals(commandLabel)) {
560 command = loadClickCommand(commandValue);
561 } else if ("call".equals(commandLabel)) {
562 command = loadCallCommand(commandValue);
563 } else if ("executeScript".equals(commandLabel)) {
564 command = loadExecuteScriptCommand(commandValue);
565 } else if ("switchToFrame".equals(commandLabel)) {
566 command = loadSwitchToFrameCommand(commandValue);
567 } else if ("sleep".equals(commandLabel)) {
568 command = loadSleepCommand(commandValue);
569 } else if ("waitFor".equals(commandLabel)) {
570 command = loadWaitForCommand(commandValue);
571 } else if ("waitForNot".equals(commandLabel)) {
572 command = loadWaitForNotCommand(commandValue);
573 } else if ("failure".equals(commandLabel)) {
574 command = loadFailureCommand(commandValue);
575 } else if ("success".equals(commandLabel)) {
576 command = loadSuccessCommand(commandValue);
577 } else if ("message".equals(commandLabel)) {
578 command = loadMessageCommand(commandValue);
579 } else if ("assertPresent".equals(commandLabel)) {
580 command = loadAssertPresentCommand(commandValue);
581 } else if ("assertAbsent".equals(commandLabel)) {
582 command = loadAssertAbsentCommand(commandValue);
583 } else if ("assertTrue".equals(commandLabel)) {
584 command = loadAssertTrueCommand(commandValue);
585 } else if ("assertFalse".equals(commandLabel)) {
586 command = loadAssertFalseCommand(commandValue);
587 } else if ("setVariable".equals(commandLabel)) {
588 command = loadSetVariableCommand(commandValue);
589 } else if ("setMaskedVariable".equals(commandLabel)) {
590 command = loadSetMaskedVariableCommand(commandValue);
591 } else if ("tag".equals(commandLabel)
592 || "tag[STEP]".equals(commandLabel)
593 || "tag[RUN]".equals(commandLabel)
594 || "tag[UPLOAD]".equals(commandLabel)) {
595 command = loadTagCommand(commandLabel, commandValue);
596 } else if ("attribute".equals(commandLabel)
597 || "attribute[STEP]".equals(commandLabel)
598 || "attribute[RUN]".equals(commandLabel)
599 || "attribute[UPLOAD]".equals(commandLabel)) {
600 command = loadAttributeCommand(commandLabel, commandValue);
601 } else if (commandLabel.startsWith("ext[")) {
602 command = loadExtCommand(commandLabel, commandValue);
603 } else {
604 throw new NotImplementedException("commandLabel: " + commandLabel);
605 }
606
607 step.addToSeq(command);
608 }
609 }
610
611 return step;
612 }
613
614 @Nullable
615 private static String getTagDirectiveOrNull(
616 final MyYaml yaml
617 ) {
618
619 for (final String directive : new String[] {
620 "tag",
621 "tag[STEP]",
622 "tag[RUN]",
623 "tag[UPLOAD]" }) {
624
625 if (yaml.has(directive)) {
626
627 return directive;
628 }
629 }
630
631 return null;
632 }
633
634 @Nullable
635 private static String getAttributeDirectiveOrNull(
636 final MyYaml yaml
637 ) {
638
639 for (final String directive : new String[] {
640 "attribute",
641 "attribute[STEP]",
642 "attribute[RUN]",
643 "attribute[UPLOAD]" }) {
644
645 if (yaml.has(directive)) {
646
647 return directive;
648 }
649 }
650
651 return null;
652 }
653
654 @Nullable
655 private static String getStringOrNull(
656 final MyYaml yaml,
657 final String name,
658 final String... alternateNames
659 ) {
660
661 if (yaml.has(name)) {
662
663 return yaml.get(name).asString();
664 }
665
666 for (final String alternateName : alternateNames) {
667
668 if (yaml.has(alternateName)) {
669
670 return yaml.get(alternateName).asString();
671 }
672 }
673
674 return null;
675 }
676
677 @Nullable
678 private static Boolean getBooleanOrNull(
679 final MyYaml yaml,
680 final String name
681 ) {
682
683 return yaml.has(name)
684 ? yaml.get(name).asBoolean()
685 : null;
686 }
687
688 @Nullable
689 private static GetCommand loadGetCommand(
690 @Nullable final String url
691 ) {
692
693 if (url == null) {
694
695 return null;
696
697 } else {
698
699 return new GetCommand(url);
700 }
701 }
702
703 @Nullable
704 private static ClickCommand loadClickCommand(
705 @Nullable final String locator
706 ) {
707
708 if (locator == null) {
709
710 return null;
711
712 } else {
713
714 return new ClickCommand(locator);
715 }
716 }
717
718 @Nullable
719 private static SleepCommand loadSleepCommand(
720 @Nullable final String secondsAsString
721 ) {
722
723 if (secondsAsString == null) {
724
725 return null;
726
727 } else {
728
729 final int seconds;
730
731 try {
732
733 seconds = Integer.parseInt(secondsAsString);
734
735 } catch (final NumberFormatException e) {
736
737 throw new RuntimeException("Cannot parse seconds: " + secondsAsString, e);
738 }
739
740 return new SleepCommand(seconds);
741 }
742 }
743
744 @Nullable
745 private static ClearCommand loadClearCommand(
746 @Nullable final String locator
747 ) {
748
749 if (locator == null) {
750
751 return null;
752
753 } else {
754
755 return new ClearCommand(locator);
756 }
757 }
758
759 @Nullable
760 private static CallCommand loadCallCommand(
761 @Nullable final String call
762 ) {
763
764 if (call == null) {
765
766 return null;
767
768 } else {
769
770 return new CallCommand(call);
771 }
772 }
773
774 @Nullable
775 private static ExecuteScriptCommand loadExecuteScriptCommand(
776 @Nullable final String script
777 ) {
778
779 if (script == null) {
780
781 return null;
782
783 } else {
784
785 return new ExecuteScriptCommand(script);
786 }
787 }
788
789 @Nullable
790 private static SwitchToFrameCommand loadSwitchToFrameCommand(
791 @Nullable final String frameName
792 ) {
793
794 if (frameName == null) {
795
796 return null;
797
798 } else {
799
800 return new SwitchToFrameCommand(frameName);
801 }
802 }
803
804 @Nullable
805 private static WaitForCommand loadWaitForCommand(
806 @Nullable final String locator
807 ) {
808
809 if (locator == null) {
810
811 return null;
812
813 } else {
814
815 return new WaitForCommand(locator);
816 }
817 }
818
819 @Nullable
820 private static WaitForNotCommand loadWaitForNotCommand(
821 @Nullable final String locator
822 ) {
823
824 if (locator == null) {
825
826 return null;
827
828 } else {
829
830 return new WaitForNotCommand(locator);
831 }
832 }
833
834 @Nullable
835 private static FailureCommand loadFailureCommand(
836 @Nullable final String message
837 ) {
838
839 if (message == null) {
840
841 return null;
842
843 } else {
844
845 return new FailureCommand(message);
846 }
847 }
848
849 @Nullable
850 private static SuccessCommand loadSuccessCommand(
851 @Nullable final String message
852 ) {
853
854 if (message == null) {
855
856 return null;
857
858 } else {
859
860 return new SuccessCommand(message);
861 }
862 }
863
864 @Nullable
865 private static MessageCommand loadMessageCommand(
866 @Nullable final String text
867 ) {
868
869 if (text == null) {
870
871 return null;
872
873 } else {
874
875 return new MessageCommand(text);
876 }
877 }
878
879 @Nullable
880 private static AssertPresentCommand loadAssertPresentCommand(
881 @Nullable final String locator
882 ) {
883
884 if (locator == null) {
885
886 return null;
887
888 } else {
889
890 return new AssertPresentCommand(locator);
891 }
892 }
893
894 @Nullable
895 private static AssertAbsentCommand loadAssertAbsentCommand(
896 @Nullable final String locator
897 ) {
898
899 if (locator == null) {
900
901 return null;
902
903 } else {
904
905 return new AssertAbsentCommand(locator);
906 }
907 }
908
909 @Nullable
910 private static AssertTrueCommand loadAssertTrueCommand(
911 @Nullable final String expression
912 ) {
913
914 if (expression == null) {
915
916 return null;
917
918 } else {
919
920 return new AssertTrueCommand(expression);
921 }
922 }
923
924 @Nullable
925 private static AssertFalseCommand loadAssertFalseCommand(
926 @Nullable final String expression
927 ) {
928
929 if (expression == null) {
930
931 return null;
932
933 } else {
934
935 return new AssertFalseCommand(expression);
936 }
937 }
938
939 @Nullable
940 private static SendKeysCommand loadSendKeysCommand(
941 @Nullable final String s
942 ) {
943
944 if (s == null) {
945
946 return null;
947
948 } else {
949
950 final String locator = extractLocator(s);
951
952 final String value = extractValue(s, locator);
953
954 return new SendKeysCommand(locator, removeQuotes(value));
955 }
956 }
957
958 @Nullable
959 private static SetMaskedVariableCommand loadSetMaskedVariableCommand(
960 @Nullable final String s
961 ) {
962
963 if (s == null) {
964
965 return null;
966 }
967
968 final String name = substringBefore(s, " ");
969
970 final String value = substringAfter(s, " ").trim();
971
972 return new SetMaskedVariableCommand(name, removeQuotes(value));
973 }
974
975 @Nullable
976 private static TagCommand loadTagCommand(
977 @Nullable final String directive,
978 @Nullable final String s
979 ) {
980
981 if (s == null) {
982
983 return null;
984 }
985
986 final AttributeScope scope = extractAttributeScope(directive);
987
988 final String name = s;
989
990 return new TagCommand(scope, removeQuotes(name));
991 }
992
993 @Nullable
994 private static AttributeCommand loadAttributeCommand(
995 @Nullable final String directive,
996 @Nullable final String s
997 ) {
998
999 if (s == null) {
1000
1001 return null;
1002 }
1003
1004 final AttributeScope scope = extractAttributeScope(directive);
1005
1006 final String name = s.startsWith("\"")
1007 ? substringBetween(s, "\"")
1008 : substringBefore(s, " ");
1009
1010 final String value = substringAfter(substringAfter(s, name), " ").trim();
1011
1012 return new AttributeCommand(scope, name, removeQuotes(value));
1013 }
1014
1015 private static AttributeScope extractAttributeScope(
1016 final String s
1017 ) {
1018
1019 return s.contains("[")
1020 ? AttributeScope.valueOf(substringBetween(s, "[", "]"))
1021 : AttributeScope.STEP;
1022 }
1023
1024 @Nullable
1025 private static ExtCommand loadExtCommand(
1026 @Nullable final String directive,
1027 @Nullable final String s
1028 ) {
1029
1030 if (s == null) {
1031
1032 return null;
1033 }
1034
1035 final String namespace = substringBetween(directive, "[", "]");
1036
1037 return new ExtCommand(namespace, removeQuotes(s));
1038 }
1039
1040 @Nullable
1041 private static SetVariableCommand loadSetVariableCommand(
1042 @Nullable final String s
1043 ) {
1044
1045 if (s == null) {
1046
1047 return null;
1048 }
1049
1050 final String name = substringBefore(s, " ");
1051
1052 final String value = substringAfter(s, " ").trim();
1053
1054 return new SetVariableCommand(name, removeQuotes(value));
1055 }
1056
1057 private static String removeQuotes(
1058 final String s
1059 ) {
1060
1061 if (s.startsWith("'")) {
1062
1063 return s.substring(1, s.length() - 1);
1064
1065 } else if (s.startsWith("\"")) {
1066
1067 return s.substring(1, s.length() - 1);
1068
1069 } else {
1070
1071 return s;
1072 }
1073 }
1074
1075 private static String extractLocator(
1076 final String s
1077 ) {
1078
1079 final StringBuilder sb = new StringBuilder();
1080
1081 for (final char c : s.toCharArray()) {
1082
1083 if (c == ' ') {
1084
1085 break;
1086 }
1087
1088 sb.append(c);
1089
1090 if (c == '[') {
1091
1092 sb.append(parseToEndingSquareBracket(s.substring(sb.length())));
1093
1094 break;
1095 }
1096 }
1097
1098 return removeQuotes(sb.toString());
1099 }
1100
1101 private static String extractValue(
1102 final String s,
1103 final String locator
1104 ) {
1105
1106 return s.substring(
1107
1108 s.startsWith("'") ? locator.length() + 2 : locator.length()
1109
1110 ).trim();
1111 }
1112
1113 private static String parseToEndingSquareBracket(
1114 final String s
1115 ) {
1116
1117 final StringBuilder sb = new StringBuilder();
1118
1119 for (final char c : s.toCharArray()) {
1120
1121 sb.append(c);
1122
1123 if (c == ']') {
1124
1125 return sb.toString();
1126 }
1127 }
1128
1129 throw new IllegalStateException("Cannot parse end of locator: " + s);
1130 }
1131 }