-
Notifications
You must be signed in to change notification settings - Fork 2
/
Selenium.dyalog
797 lines (713 loc) · 31.3 KB
/
Selenium.dyalog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
:Namespace Selenium ⍝ V 2.11.0
⍝ This namespace allows scripted browser actions. Use it to QA websites, inluding RIDE.
⍝
⍝ 2017 05 09 Adam: Version info added
⍝ 2017 05 23 Adam: now gives helpful messages for DLL problems, harmonised ADOC utils
⍝ 2020 02 12 MBaas 2.10: updated to use a config (.json)-file to facilitate testing with various browsers (incl. HTMLRenderer)
⍝ 2020 05 08 MBaas: praparing for cross-platformness;new folder-structure for drivers
⍝ 2020 07 11 MBaas: lots of changes to make it working on ALL platforms;revised structure of settings (AND names of parameter DRIVERS → DRIVER)
⍝ 2021 11 15 MBaas: 2.11.0 settings.json may now use "SELENIUM_DRIVERPATH" to point to the folder with the downloaded drivers -
⍝ this way it becomes more generally usable and less platform-dependent. Changed to semantic versioning. Settings file now JSON5.
:Section ADOC
∇ t←Describe
t←1↓∊(⎕UCS 10),¨{⍵/⍨∧\(⊂'')≢¨⍵}Comments ⎕SRC ⎕THIS ⍝ first block of non-empty comment lines
∇
∇ (n v d)←Version;f;s
s←⎕SRC ⎕THIS
f←Words⊃s ⍝ split first line
n←2⊃f ⍝ ns name
v←'V'~⍨⊃⌽f ⍝ version number
d←1↓∊'-',¨3↑Words⊃⌽Comments s ⍝ date
∇
Words←{⎕ML←3 ⋄ ⍵⊂⍨' '≠⍵}
Comments←{1↓¨⍵/⍨∧\'⍝'=⊃∘{(∨\' '≠⍵)/⍵}¨⍵}1∘↓
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section INITIALISATION
⎕WX←3
DEFAULTBROWSER←'Chrome'
DLLPATH←'' ⍝ might be overridden through ⍺[4] when calling Test ⍝TODO: update comment
RETRYLIMIT←DEFAULTRETRYLIMIT←5 ⍝ seconds
EXT←'.dyalog'
RIDEEXT←'.ridetest'
PORT←8080
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section MAIN FRAMEWORK PROGRAMS
∇ failed←{stop_site_match_config}Test path_filter;⎕USING;stop;match;site
⍝ stop: 0 (default) ignore but report errors; 1 stop on error; 2 stop before every test
⍝ site: port number (default is PORT) or URL
⍝ match: 0 (default) run all tests on the baseURL; 1 run tests on baseURL matching dir struct
⍝ config: points to an entry of your settinghs.json-Foöe
'stop_site_match_config'DefaultTo 0
:If 82=⎕DR stop_site_match_config ⍝ handle mode where just the name of a config is given
stop_site_match_config←0 0 0,⊂stop_site_match_config
:EndIf
:If 0<≢4⊃4↑stop_site_match_config
:AndIf 0<≢4⊃stop_site_match_config
ApplySettings 4⊃stop_site_match_config
:EndIf
InitBrowser''
(⍎,∘'←∊Keys.(',,∘')')⍕Keys.⎕NL ¯2 ⍝ Localize non-alphanumeric key names for easy access
failed←({3<≢⍵:3↑⍵ ⋄ ⍵}stop_site_match_config)RunAllTests path_filter
BROWSER.Quit
∇
∇ path←base NormalizePath path
path base←{('/'@{'\'=⍵})⍵}¨path base
base←base,('/'≠⊃⌽base)/'/'
:If './'≡2↑path ⋄ path←base,2↓path ⍝ relative path
⍝ :ElseIf... ⍝ are there any more cases to consider???
:EndIf
∇
∇ InitBrowser settings;browser;files;msg;path;len;options;opt;pth;subF;suffix;drv;opts;p;cap;BSVC;z
options←''
:If ×⎕NC'BROWSER' ⍝ close any open browser
BROWSER.Quit
:Else
:If 0=⎕NC'SETTINGS' ⋄ ApplySettings settings ⋄ :EndIf
:EndIf
browser←SETTINGS.BROWSER
files←browser SetUsing(path←DLLPATH)SETTINGS.Newtonpath
'CURRENTBROWSER'DefaultTo'' ⍝ Avoid VALUE ERRORs
⎕EX'BROWSER'/⍨browser≢CURRENTBROWSER ⍝ We want to switch or need a new one
:Trap 0 ⍝ Try to find out if Browser is alive - not always reliable
{}BROWSER.Url
:Else
subF←(('WLM'⍳1 1⊃'.'⎕WG'APLVersion')⊃'Win' 'Linux' 'Mac'),'/' ⍝ subfolder for platform-specific driver files
suffix←('W'=1 1⊃'.'⎕WG'APLVersion')/'.exe'
:If 2=SETTINGS.⎕NC'Executable'
drv←SETTINGS.Executable,suffix
:Else
⍝ drv←(⎕C browser),'driver',suffix
drv←(lc browser),'driver',suffix
:EndIf
:If 2=SETTINGS.⎕NC'DRIVER' ⋄ path←SETTINGS.DRIVER ⋄ :EndIf
pth←path
:If '.'=⊃pth
:AndIf (2⊃pth)∊'\/'
pth←∊1 ⎕NPARTS(SourcePath ⎕THIS),2↓pth
:EndIf
:If ~(⎕NEXISTS⍠1)pth,drv
:AndIf (⎕NEXISTS⍠1)pth,subF,drv
pth←pth,subF ⍝ pick subfolder depending on platform
:ElseIf (⎕NEXISTS⍠1)pth,(subF←subF,((1+∨/'64'⍷1⊃'.'⎕WG'APLVersion')⊃'32' '64'),'/'),drv ⍝ test adding bits to folder (see folder-structure for Edge/Win!)
pth←pth,subF ⍝ pick subfolder depending on platform
:EndIf
:If 9=⎕NC'BROWSEROPTIONS' ⍝ if var exists
:AndIf 0<≢BROWSEROPTIONS
options←InitOptions browser
:For opt :In BROWSEROPTIONS.⎕NL-2
⍎'options.',(∊opt),'←BROWSEROPTIONS.',,∊opt
:EndFor
:EndIf
:If 2=SETTINGS.⎕NC'AdditionalCapabilities'
:AndIf 0 ⍝ not yet ripe for production!
:If options≡'' ⋄ options←InitOptions browser ⋄ :EndIf
:For cap :In SETTINGS.AdditionalCapabilities
options.AddAdditionalCapability(cap.name)(cap.value)
:EndFor
:EndIf
:If 2=SETTINGS.⎕NC'AddArguments'
:If options≡'' ⋄ options←InitOptions browser ⋄ :EndIf
:For opt :In SETTINGS.AddArguments
options.AddArgument⊂opt
:EndFor
:EndIf
:If 2=SETTINGS.⎕NC'LoggingPreferences'
:If options≡'' ⋄ options←InitOptions browser ⋄ :EndIf
cap←options.ToCapabilities
:For p :In SETTINGS.LoggingPreferences
options.SetLoggingPreference p.type(⍎p.level)
:EndFor
:EndIf
:If ~0{6::⍺ ⋄ ⍎⍵}'QUIETMODE' ⋄ ⎕←'Starting ',browser ⋄ :EndIf
:Trap 0
BSVC←(⍎browser,'DriverService').CreateDefaultService(pth)(drv)
:Trap 90
z←1⊣BSVC.IsRunning
:Else
z←0
:EndTrap
:If ~z
⎕←'Could not create instance of ',browser,'DriverService.'
⎕←'You may have to adjust file-permissions to make this file executable:'
⎕←⎕SH'ls -l ',pth,drv
'Could not create DriverService - check msg in session'⎕SIGNAL 11
:EndIf
:If options≡''
BROWSER←⎕NEW(⍎browser,'Driver')BSVC
:Else
BROWSER←⎕NEW(⍎browser,'Driver')(BSVC options)
:EndIf
:Else
msg←'Could not load '
msg,←∊1↓⎕NPARTS⊃files
msg,←' and ',∊1↓⎕NPARTS⊃⌽files
msg,←' from ',pth,' ─ they may be '
:If 1 1≡⎕NEXISTS¨files
msg,←'blocked (Properties>General>Unblock)',⎕UCS 13
msg,←'Or maybe something else is wrong. Here are the details of the exception:',⎕UCS 13
msg,←⎕EXCEPTION.Message
:If options≢'' ⋄ msg,←(⎕UCS 13 13),'options:',⍕options ⋄ :EndIf
msg ⎕SIGNAL 19
:Else
(msg,'missing')⎕SIGNAL 22
:EndIf
:EndTrap
CURRENTBROWSER←browser
ACTIONS←⎕NEW OpenQA.Selenium.Interactions.Actions BROWSER
:End
:If ~×#.⎕NC'MAX'
:Trap 90 ⍝ can't do that with CEF
BROWSER.Manage.Window.Maximize
:EndTrap
:EndIf
∇
∇ options←InitOptions browser
options←⎕NEW⍎browser,'Options'
∇
∇ SaveScreenshot ToFile
BROWSER.GetScreenshot.SaveAsFile⊂ToFile
∇
∇ failed←stop_site_match RunAllTests path_filter;files;maxlen;n;start;i;file;msg;time;path;filter;allfiles;hasfilter;shutUp;showMsg;prefix
path filter←2↑(eis path_filter),⊂''
shutUp←0{6::⍺ ⋄ ⍎⍵}'QUIETMODE' ⍝ use QUIETMODE to suppress everything BUT error-messages
showMsg←⍎(1+shutUp⊃)'{⍵}' '{}'
allfiles←(≢path)↓¨FindAllFiles path
hasfilter←×≢filter
files←filter ⎕S'%'⍣hasfilter⊢allfiles
n←≢files
showMsg'Selected: ',(⍕n),(hasfilter/' of ',⍕≢allfiles),' tests.'
maxlen←⌈/≢¨files
failed←''
start←⎕AI[3]
:For i file :InEach (⍳n)files
prefix←(⎕UCS 13),maxlen↑file
msg←stop_site_match Run1Test path file
:If 0=⍴msg
showMsg prefix,' *** PASSED ***'
:Else
failed,←⊂file
⎕←prefix,' *** FAILED *** #',(⍕i),' of ',(⍕n),': ',msg
:EndIf
:EndFor
time←∊'ms',¨⍨⍕¨24 60⊤⌊0.5+0.001×⎕AI[3]-start
showMsg←'Total of ',(⍕n),' samples tested in ',time,': ',(⍕≢failed),' failed.'
∇
∇ r←stop_site_match Run1Test(path file);name;Test;stop;site;match
stop site match←3↑stop_site_match
site←Urlify site
{~UrlIs ⍵:GoTo ⍵}site,'/',match/file↓⍨¯7×EXT≡¯7↑file
:Select ⊃⌽'.'Split file
:Case RIDEEXT
name←⎕FX'msg←Test dummy' 'RideScript','⍝',¨⊃⎕NGET(path,file)1 ⍝ Create Test function from raw RIDE script
:Case EXT
name←⎕SE.SALT.Load path,file
:EndSelect
:If ×⎕NC'name'
:AndIf 'Test'≡name
:Trap 0/⍨0=stop
'Test'⎕STOP⍨1/⍨2=stop ⍝ stop on line 1 if stop=2
r←Test ⍬
:If stop⌊×≢r
⎕←'test for ',file,' failed:'
⎕←r
⎕←' Test ⍬ ⍝ to Rerun'
⎕←' →⎕LC ⍝ to continue'
(⎕LC[1]+1)⎕STOP 1⊃⎕SI
:EndIf
:Else
r←'Trapped error: ',,⍕⎕DMX.EN
:EndTrap
:EndIf
∇
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section MISERVER UTILITIES
∇ {ok}←id ListMgrSelect items;elements
⍝ Move items from left to right in a MiServer ejListManager control
ok←1
elements←(id,'_left')FindListItems items
elements DragAndDrop¨⊂id,'_right_container'
∇
∇ {ok}←{open}ejAccordionTab(tabText ctlId)
⍝ Make sure that a control, within an accordiontab, is visible or not
ok←1
'open'DefaultTo 1
:If open≠(BROWSER.FindElementById⊂ctlId).Displayed ⍝ If it doesn't have the desired state
'LinkText'Click tabText
{(open ctlId)←⍵
open=(BROWSER.FindElementById⊂ctlId).Displayed}Retry open ctlId
:EndIf
∇
∇ r←id FindListItems text;li;ok
⍝ Find list items with a given text within element with id (e.g. [Syncfusion ej]ListBox items)
(ok li)←{li←⌷'CssSelectors'Find'#',⍵,' li' ⋄ (0≠⍴li)li}Retry id
r←(li.Text∊eis text)/li
∇
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section COVER FUNCTIONS
∇ ClearInput obj;q
:If 0≢q←Find obj
q.Clear
:EndIf
∇
∇ {ok}←obj SendKeys text;q;i;k
⍝ Send keystrokes - see Keys.⎕NL -2 for special keys like Keys.Enter
⍝ Note that even 'A' Control 'X' will be interpreted as Ctrl+A,X
⍝ To get A,Ctrl+X use 'A'(Control 'X')
ok←1
q←Find obj
:If q≢0 ⍝ make sure that we found it!
text←eis text
i←4~⍨Keys.(Shift Control Alt)⍳¯1↓text
ACTIONS.Reset
:For k :In i
(ACTIONS.(KeyDown ##.k⌷Keys.(Shift Control Alt))).Build.Perform
:EndFor
q.SendKeys,¨text~Keys.(Shift Control Alt)
:EndIf
∇
∇ {ok}←id SetInputValue text;s;i;r
⍝ Set the value of an input control to text.
:If ∨/i←text='"'
text←(1+i)/text
text[(⍸i)+(0,¯1↓+\i)[⍸i]]←'\'
:EndIf
s←'document.getElementById("',id,'").value= "',text,'";'
ok←1
:Trap 0
r←ExecuteScript s
:Else
ok←0
:EndTrap
∇
∇ {ok}←{type}Click id;b;ok;time
⍝ Click on an element, by default identified by id. See "Find" for options
ok←1
'type'DefaultTo'Id'
b←type Find id
('Control "',id,'" not found')⎕SIGNAL(0≡b)/11
b.Click
∇
∇ {ok}←fromid DragAndDrop toid;from;to
⍝ Drag and Drop
ok←1
(from to)←Find¨fromid toid
ACTIONS.Reset
(ACTIONS.DragAndDrop from to).Perform
∇
∇ {ok}←fromid DragAndDropToOffset xy;from
⍝ Drag
ok←1
from←Find fromid
ACTIONS.Reset
(ACTIONS.DragAndDropToOffset from,xy).Build.Perform
∇
∇ {ok}←{action}MoveToElement args;id;target
⍝ Move to element with optional x & y offsets
⍝ And perform optional action (Click|ClickAndHold|ContextClick|DoubleClick)
ok←1
(⊃args)←Find⊃args ⍝ Elements [2 3] optional x & y offsets (integers)
ACTIONS.Reset
(ACTIONS.MoveToElement args).Build.Perform
⎕DL 0.1
:If 2=⎕NC'action'
:If (⊂action)∊'Click' 'ClickAndHold' 'ContextClick' 'DoubleClick'
((ACTIONS⍎action)⍬).Build.Perform
:Else
('Unsupported action: ',action)⎕SIGNAL 11
:EndIf
:EndIf
∇
∇ {r}←ExecuteScript script ⍝ cover for awkward syntax and meaningless result
r←BROWSER.ExecuteScript script ⍬
∇
∇ r←{level}GetLogs types;lb;e;entry;tit;r∆;type
⍝ chould/should take ⍵ to select desired log(s) - once we have some data in them...;)
⍝ level ∊ 'Info' 'Severe'
⍝ types ∊ 'Browser'
⍝ These values depend on the browser that's used!
⍝ TODO: Document this!
r←''
types←types{0=≢⍺:⍵ ⋄ (⊆⍺)∩⍵}⊃⌷¨BROWSER.Manage.Logs.AvailableLogTypes
:For type :In types
lb←BROWSER.Manage.Logs.GetLog⊂type
tit←'Log: ',type,' ('
r∆←⍬
:If 0<lb.Count
:For e :In ⍳lb.Count
entry←e⌷lb
:If 2=⎕NC'level' ⋄ :AndIf 0<≢level ⋄ :AndIf ~(⊂⍕entry.Level)∊⊆level ⋄ :Continue
:EndIf
r∆,←⊂' ',' ',⍕entry
:EndFor
:EndIf
:If 0<≢r∆
r←r,(⊂tit,(⍕≢r∆),' entries)'),r∆
:Else
r←r,⊂tit,,'(no entries)'
:EndIf
:EndFor
∇
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section USER UTILITIES FOR QA SCRIPTS
∇ r←Text element;f
⍝ Retrieves (visible) text of element
:If 9≠⎕NC'element' ⋄ element←Find element ⋄ :EndIf
:If element.TagName≡'input'
r←element.GetAttribute⊂'value'
:Else
r←element.Text
:EndIf
∇
∇ {ok}←selectId Select itemText;sp;se;type
⍝ Select an item in a select element
ok←1
⍝ ↓↓↓ Id can be tuple of (type identifier - see Find)
:If 2=≡selectId ⋄ (type selectId)←selectId
:Else ⋄ type←'Id'
:EndIf
'Select not found'⎕SIGNAL(0≡sp←type Find selectId)/11
se←⎕NEW OpenQA.Selenium.Support.UI.SelectElement sp
se.SelectByText⊂,itemText
∇
∇ {ok}←selectId SelectItemText itemsText;item;se
⍝ Select items in a select control
⍝ Each item can be deselected by preceding it with '-'.
⍝ A single '-' deselects all
ok←1
se←⎕NEW OpenQA.Selenium.Support.UI.SelectElement,⊂Find selectId
:For item :In eis itemsText
:If item≡'~'
se.DeselectAll
:ElseIf '~'=1↑item
se.DeselectByText⊂1↓item
:Else
se.SelectByText item 1 ⍝ signature changed in WebDriver4 - do we need to distinguish?
:EndIf
:EndFor
∇
∇ r←{type}Find id;f;ok;time;value;attr;search;elms;mask
:If 9=⎕NC'id'
r←id ⍝ Already an object
:Else
'type'DefaultTo'Id'
⍝ See auto-complete on BROWSER.F for a list of possible ways to find things
(id attr value)←{3↑⍵,(⍴⍵)↓'' '' ''}eis id
:If search←~0∊⍴attr
type,←('s'=¯1↑type)↓'s'
:EndIf
:If 's'=¯1↑type ⍝ The call FindElements*
f←⍎'BROWSER.FindElementsBy',¯1↓type
:Else
f←⍎'BROWSER.FindElementBy',type
:EndIf
time←⎕AI[3]
:Repeat ⍝ Other functions use Retry operator, but we need to collect the result
:Trap 0
r←f⊂id
ok←1
:Else
r←0
:If RETRYLIMIT>0 ⋄ ⎕DL 0.1 ⋄ ok←0 ⋄ :EndIf
:EndTrap
:Until ok∨(⎕AI[3]-time)>1000×RETRYLIMIT ⍝ Try for a second
:If ok
:AndIf search
:If 0<r.Count
elms←⌷r
:If r←∨/mask←<\(⊂value)≡¨attr∘{⍵.GetAttribute⊂⍺}¨elms
r←⊃mask/elms
:EndIf
:Else
r←0 ⍝ nothing found!
:EndIf
:EndIf
:EndIf
∇
∇ {ok}←(fn Retry)arg;time;z
⍝ Retry fn for a while
ok←0 ⋄ time←⎕AI[3]
:Repeat
:Trap 0
ok←fn arg
:Else
⎕DL 0.1
:EndTrap
:Until (⊃ok)∨(⎕AI[3]-time)>1000×RETRYLIMIT ⍝ Try for a second
∇
∇ {ok}←Wait msec
ok←×⎕DL msec÷1000
∇
∇ r←larg WaitFor args;f;text;msg;element
⍝ Retry until text/value of element begins with text
⍝ Return msg on failure, '' on success
:If 9≠⎕NC'larg' ⋄ larg←Find larg ⋄ :EndIf
:If larg≡0 ⋄ r←'Did not find element "',(⍕larg),'"' ⋄ →0 ⋄ :EndIf
element←larg
args←eis args
(text msg)←2↑args,(⍴args)↓'Thank You!' 'Expected output did not appear'
f←'{∨/''',((1+text='''')/text),'''','≡⍷'[1+×⍴,text]
:If element.TagName≡'input'
f,←'element.GetAttribute⊂''value''}'
:Else
f,←'element.Text}'
:EndIf
r←(~(⍎f)Retry ⍬)/msg
∇
IsVisible←{(Find ⍵).Displayed} ⍝ test if given element is Displayed (useful if combined with Retry to wait till control is accessible)
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:Section RIDE-IN-BROWSER QA SCRIPT UTILITIES
:Namespace Cache ⍝ to be populated by SeRef, EdRef, Lb
:EndNamespace
∇ se←SeRef ⍝ Get ref to Session
:Trap 2 6 90 ⍝ SYNTAX (not found) VALUE (first use) EXCEPTION (stale - probably won't happen)
{}Cache.SE.Displayed
:Else
Cache.SE←'CssSelector'Find'.ride_win textarea'
:EndTrap
se←Cache.SE
∇
Se←{Do:SeRef SendKeys¨⊂⍣(1=≡,⍵)⊢⍵ ⋄ 1}Retry ⍝ SendKeys in Session
∇ ed←EdRef ⍝ Get ref to Editor
:Trap 6 90 ⍝ VALUE (first use) EXCEPTION (stale = new editor)
{}Cache.ED.Displayed
:Else
Cache.ED←'CssSelector'Find'.ride_win_cm textarea'
:EndTrap
ed←Cache.ED
∇
Ed←{Do:EdRef SendKeys¨⊂⍣(1=≡,⍵)⊢⍵ ⋄ 1}Retry ⍝ SendKeys in Editor
∇ {oks}←Lb glyphs ⍝ Click Language Bar buttons
:If 0∊Cache.⎕NC'LB' 'LB_Text'
Cache.LB_Text←(Cache.LB←⌷'CssSelectors'Find'#lb b').Text
:EndIf
oks←{Click Cache.LB⊃⍨Cache.LB_Text⍳⊂,⍵}¨glyphs
∇
Tb←{'ClassName'Click'tb_',⍵}Retry ⍝ click toolbar button
LastIs←{Do:''≡msg,←EndsWith Nested ⍵ ⋄ 1}Retry ⍝ Check last non-empty line in session
EndsWith←{ ⍝ last session line(s) contain patterns
h←-≢⍵
se←h↑¯1↓Session~⊂''
∧/2</¯1,∊⍵ Has¨se:'' ⍝ last line is 6-space prompt
'Session had "',(NlFmt se),'"; expected "',(NlFmt ⍵),'". '
}
∇ r←Session ⍝ Session text
r←(⌷'CssSelectors'Find'.CodeMirror-line span').Text
∇
Has←{ ⍝ Location of pattern ⍵ in source ⍺
rot←,¯1⌽⍵
'$$'≡2↑rot:(2↓rot)⎕S 0⊢⍺
'$;$'≡3↑rot:('^',(rot[4]~'^'),4↓rot)⎕S 0⊢⍺
';'=⊃⍵:0/⍨(1↓rot)≡⍺↑⍨¯1+≢⍵
loc←⍺⍷⍵
∨/loc:loc⍳1
¯1
}
Fix←{⍺←2 ⋄ Se((⍕⍺),'⎕FIX''file://',⍵,'''')Enter} ⍝ Execute ⎕FIX in Session
Gives←{Do:r←LastIs¨eis ⍵⊣Se ⍺ Enter ⋄ r←1}
∇ r←Do ⍝ Enables skipping to end if a problem has already occured
:If 0=⎕NC'msg'
msg←''
:EndIf
r←''≡msg
∇
∇ {r}←RideScript;lines;l;major;minor ⍝ Script processor
⍝ To be called before one or more comment lines
⍝ each script line may have one, two, or three major sections:
⍝ command: results: timeout
⍝ : is the major separator and ; is the minor separator for results
⍝ command gets typed into the session, and then Enter is pressed
⍝ if there is a "results", then (before the timemout) each of these must occur in the given order
⍝ the first target in results specifies the beginning of the line (so skip this with :;)
⍝ targets are RegEx when enclosed in Dollar signs ($)
⍝ the timeout may be temporarily adjusted from the default by specifying a number of seconds
⍝ var←6
⍝ just enters the assignment in the session
⍝ var×7: 42
⍝ checks that the expected result is given.
⍝ ⊢⎕DL 6:
lines←{⍵/⍨'⍝'≠⊃¨⍵}{1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}¨(1+2⊃⎕LC)↓⎕NR'Test'
msg←''
:For l :In lines/⍨'⍝'≠⊃¨lines
major←1↓¨':'Split l
:If 1=≢major
Se(⊃major)Enter
:Else
:If 3≤≢major
RETRYLIMIT←⊃∊(//⎕VFI⊃2↓major),DEFAULTRETRYLIMIT
:EndIf
minor←';'Split 2⊃major
(⊃minor)↓⍨←1
r←(⊃major)Gives minor
:EndIf
→0/⍨''≢msg
:EndFor
∇
:EndSection
:Section UTILS
Nested←{(+/∨\' '≠⌽⍵)↑¨↓⍵}⊢⍴⍨¯2↑1,⍴ ⍝ Vector of vectors from simple vector or matrix
NlFmt←{1↓∊'¶',¨⍵} ⍝ Convert VTV to ¶-separated simple vector
Split←,⊂⍨⊣=, ⍝ Split ⍵ at separator ⍺, but keep the separators as prefixes to each section
∇ r←lc R ⍝ lowercase
:If 18≤2⊃⎕VFI 2↑2⊃'.'⎕WG'aplversion'
r←⎕C R
:Else
r←0(819⌶)R
:EndIf
∇
∇ {ok}←GoTo url;z;base ⍝ Ask the browser to navigate to a URL and check that it did it
ok←1
:If 'http'≢lc 4↑url ⍝ it's probably a relative URL (does this text need be more detailed?)
base←BROWSER.Url
:While (≢url)>z←url⍳'/'
:If z=1
base←((2≥+\base='/')/base),'/'
:ElseIf '../'≡z↑url
base←()↑base
:ElseIf './'≡z↑url ⍝ do nothing
:Else
base←base,z↑url
:EndIf
url←z↓url
:EndWhile
url←base,url
:EndIf
BROWSER.Navigate.GoToUrl⊂url
:Trap 90
('Could not navigate from ',BROWSER.Url,' to ',url)⎕SIGNAL 11/⍨~UrlIs url
:Else
('Alert running "',url,'": ',⎕EXCEPTION.Message)⎕SIGNAL 11
:EndTrap
∇
UrlIs←{(⊂BROWSER.Url)∊⍵(⍵,'/')} ⍝ Is the browser currently at ⍵?
List←0 1⎕NINFO⍠1⊢ ⍝ List names and types in directory ⍵
Files←⊃(/⍨∘(2∘=))/ ⍝ Filter for files only
Folders←⊃(/⍨∘(1∘=))/ ⍝ Filter for folders only
DefaultTo←{0=⎕NC ⍺:⍎⍺,'←⍵' ⋄ _←⍵} ⍝ set ⍺ to ⍵ if undefined
PathOf←{⍵↓⍨1-⌊/'/\'⍳⍨⌽⍵} ⍝ Extract path from path/filename.ext
eis←{(≡⍵)∊0 1:,⊂,⍵ ⋄ ⍵} ⍝ Enlose (even scalars) If Simple
Urlify←{0''⍬∊⍨⊂⍵:∇ PORT ⋄ ⍬≡0⍴⍵:'http://127.0.0.1:',⍕⍵ ⋄ ⍵} ⍝ Ensure URL even if given just port number
∇ r←FindAllFiles root ⍝ Recursive
:If ∨/' '≠root
root,←'/'/⍨~'/\'∊⍨¯1↑root ⍝ append trailing / if missing
r←Files List root,'*',EXT
r,←Files List root,'*',RIDEEXT
r,←⊃,/FindAllFiles¨Folders List root,'*'
:Else
r←0⍴⊂''
:EndIf
∇
∇ {files}←browser SetUsing path;net ⍝ S)SETTINGS:Newtonpathet the path to the Selenium DLLs
(path Newtonpath)←path
:If path≡'' ⋄ path←SourcePath ⎕THIS
:Else ⋄ path←path,(~'/\'∊⍨⊢/path)/'/' ⋄ :EndIf
⎕USING←0⍴⎕USING
⎕USING,←⊂'' ⍝ VC 200513 via mail to MB
:If 4≠System.Environment.Version.Major ⍝ if not .NET 4, it is likely Core!
net←'netstandard2.0'
:Else
net←'net47'
:EndIf
net,←⎕se.SALTUtils.FS
files←'dll' 'Support.dll',¨⍨⊂path,net,'WebDriver.'
⎕USING,←⊂'OpenQA,',⊃files ⍝ if we need to dig into deeper into Selenium...
⎕USING,←⊂'OpenQA.Selenium,',⊃files
⎕USING,←⊂'OpenQA.Selenium.',browser,',',⊃files
⎕USING,←⊂'OpenQA.Selenium.Support,',⊃⌽files
⎕USING,←⊂'Newtonsoft.Json,',(1⊃1 ⎕NPARTS ¯1↓path),Newtonpath,net,'Newtonsoft.Json.dll'
⍝ make sure we use the correct path-separator (⎕USING)
⎕USING←{⎕se.SALTUtils.FS@('/'∘=)⍵}¨⎕USING
∇
SourceFile←{ ⍝ Get full pathname of sourcefile for ref ⍵
file←4⊃5179⌶⍵ ⍝ ⎕FIX
''≡file~' ':⍵.SALT_Data.SourceFile ⍝ SALT
file
}
SourcePath←{⊃1⎕nparts SourceFile ⍵} ⍝ just the path of the SourceFile in questions
:EndSection ───────────────────────────────────────────────────────────────────────────────────
:section SETTINGS
∇ R←GetSettings;v;varnam
R←1⊃⎕NGET(SourcePath ⎕THIS),'settings.json'
:For varnam :In ⊂'SELENIUM_DRIVERPATH'
:If ∨/('$',varnam)⍷R ⍝ do json-settings refer to SELENIUM_DRIVERPATH?
:If 0=≢v←2 ⎕NQ'.' 'GetEnvironment'varnam
('Environment variable "',varnam,'" referred in settings.json, but not found in environment!')⎕SIGNAL 11
:EndIf
v←'/'@('\'∘=)v ⍝ we don't require "/" in paths, so we need to replace'emn here to avoid invalid json
R←(('$',varnam)⎕R v⍠('Regex' 0))R
:EndIf
:EndFor
R←(⎕JSON⍠'Dialect' 'JSON5')R
R←Flatten R
∇
∇ R←{flavours_vars_mem}Flatten ns;vars;flavours;n;nl;z;AddVar;mem;v;f;f∆;ref
⍝ process tree-structures settings into a "flattened" structure
mem←0
:If 0=⎕NC'flavours_vars_mem'
vars←flavours←0 2⍴''
:ElseIf 2=≢flavours_vars_mem
(flavours vars)←flavours_vars_mem
:Else
(flavours vars mem)←flavours_vars_mem
:EndIf
vars←vars AddVars ns
:For n :In (ns.⎕NL-9)~⊂'Flavours'
:If 2=(ns⍎n).⎕NC'isDriverParam'
ns.⎕EX n,'.isDriverParam'
vars←vars⍪(n)(⎕JSON ns⍎n)
:Else
flavours v←(flavours vars)Flatten ns⍎n
:If mem
flavours[flavours[;1]⍳⊂n;2]←⊂v
:EndIf
:EndIf
:EndFor
:If 9=ns.⎕NC'Flavours'
z←~(nl←ns.Flavours.⎕NL ¯9)∊flavours[;1]
z←z∧nl≢¨⊂'Flavours'
flavours⍪←(,[1.5]z/nl),⊂vars
flavours←1⊃(flavours vars 1)Flatten ns.Flavours
:EndIf
:If 0=⎕NC'flavours_vars_mem' ⍝ top-level
R←#.⎕NS''
:For v :In ↓vars
R.{⍎(1⊃⍵),'←0⎕json 2⊃⍵'}v
:EndFor
:For f :In ⍳1↑⍴flavours
ref←R.⎕NS''
:For v :In ↓2⊃flavours[f;]
ref.{⍎(1⊃⍵),'←0⎕JSON 2⊃⍵'}v
:EndFor
ref R.{(1⊃⍵)⎕NS ⍺}flavours[f;]
:EndFor
:Else
R←flavours vars
:EndIf
∇
∇ vars←vars AddVars ns;n;i
:For n :In ns.⎕NL-2
:If (≢vars)<i←vars[;1]⍳⊂n
vars←vars⍪2↑⊂n
:EndIf
vars[i;2]←⊂1 ⎕JSON ns⍎n
:EndFor
∇
∇ R←ApplySettings name;settings;ref;go
settings←GetSettings
ref←settings{6::'' ⋄ d←2⎕nq'.' 'GetEnvironment' 'SELENIUM_DRIVER'⋄ (0=≢⍵)∧0=≢d:⍺⍎⍺.default ⋄ (0=≢⍵): d ⋄ ⍺⍎⍵}name
:If ref≡''
('Settings "',name,'" not found!')⎕SIGNAL 11
ref←settings.{6::'' ⋄ ⍺⍎⍺⍎⍵}'default'
:EndIf
SETTINGS←ref ⍝ memorize them in the NS (in case we need them again...)
DLLPATH←(1⊃⎕NPARTS SourceFile ⎕THIS)NormalizePath ref{6::2⊃⍵ ⋄ ⍺⍎1⊃⍵}'DLLPATH'DLLPATH
DEFAULTBROWSER←ref{6::2⊃⍵ ⋄ ⍺⍎1⊃⍵}'BROWSER'DEFAULTBROWSER
PORT←ref{6::2⊃⍵ ⋄ ⍺⍎1⊃⍵}'PORT'PORT
BROWSEROPTIONS←⍬ ⍝ no options found...
NEWTONPATH←settings.Newtonpath
:Select ⍬⍴ref.⎕NC'Options'
:CaseList 2 9 ⋄ BROWSEROPTIONS←ref.Options
:EndSelect
⎕USING←'System'
∇
:endsection SETTINGS
:EndNamespace